# Coffee API

> Look up coffee-shop drinks (the description, the recipe and a photo for each) from the free, keyless Coffee API. Get one drink by name, search the menu by word, ingredient or style, pick one at random for a "what should I order?" post, or pick a drink of the day that stays the same all day so a scheduled post never names two. It also totals up what the whole menu is made of, ingredient by ingredient. The service files its drinks across two lists that do not mean what they are called, so whether a drink is iced and what it is built on are read off the recipe here instead, and the practice rows people have left in this public sandbox are kept off the menu.

App · Food & Drink · 6 actions. Web page: https://docs.lodol.com/docs/integrations/coffee-api

## Actions

### Check service status

Check whether the Coffee API is answering, how quickly, and how much of the rate limit is left. This is the one action that reports a problem as a value instead of failing the step, so a workflow can decide what to do about it. Being throttled is reported separately from being down.

**Inputs**: None.

**Outputs**

- `message` (text)
- `online` (true/false)
- `rate_limit` (number)
- `rate_limit_remaining` (number)
- `rate_limited` (true/false)
- `reachable` (true/false)
- `response_time_ms` (number)
- `status_code` (number)

**Example output**

```json
{
  "message": "The Coffee API is up and serving drinks.",
  "online": true,
  "rate_limit": 5000,
  "rate_limit_remaining": 4999,
  "rate_limited": false,
  "reachable": true,
  "response_time_ms": 118.4,
  "status_code": 200
}
```

### Find drinks

Browse the whole menu, or just the part of it you want: search the names, recipes and descriptions for a word, keep only the coffees or only the teas, only the ones served over ice, only the ones containing particular ingredients, and order what is left. The service offers no search, no filter and no ordering that reaches across both of its lists, so both are read once and merged here, which is what lets all of that be one step. The number of drinks matching is always reported, however many are returned.

**Inputs**

- Search for (`search`, text): A word to look for in the drink names, their recipes and their descriptions, 2 to 120 characters. Best match leads: a hit on the name outranks one in the recipe, which outranks one in the description. Leave empty to browse rather than search.
- Made with (`base`, text): Only keep drinks built on this. The menu is not all coffee (it also carries tea, hot chocolate, orange juice and lemonade), and the service records no such field, so this is read off each recipe: espresso or coffee makes it a coffee, tea or matcha a tea, chocolate or cocoa a chocolate. A mocha counts as a coffee. Leave empty for the whole menu. One of: `coffee`, `tea`, `chocolate`, `other` (Everything else).
- Ice (`ice`, text): Only keep drinks that are, or are not, served over ice. Taken from the recipe rather than from which of the service's two lists a drink is filed under, because that filing is wrong for a sixth of the menu: Iced Latte and Frozen Lemonade both sit on its 'hot' list. Leave empty for both. One of: `with_ice` (Served over ice), `without_ice` (Served without ice).
- Must contain (`ingredients`, list): Only keep drinks whose recipe contains every one of these. The list is the menu's own vocabulary, with the asterisk the service uses for an optional part already read off, so Sugar is one choice rather than two. Leave empty for any recipe.
- Order by (`sort`, text): How to order the results. Ignored when you search, because a search is ordered by how well each drink matches. Defaults to name, A to Z. One of: `name` (Name, A to Z), `name_desc` (Name, Z to A), `simplest` (Simplest first (fewest ingredients)), `richest` (Richest first (most ingredients)). Default: `name`.
- Number of drinks (`limit`, number): How many drinks to return, from 1 to 100. Defaults to 25, which is more than the menu currently holds, so leaving it alone returns everything that matched. From 1 to 100. Default: `25`.
- Include test entries (`include_test_entries`, true/false): Show the rows people have left behind while trying out the service's write routes, which anyone can call. There are three today: one called 'Black CoffeeTest' and two called 'test', and they are hidden unless you switch this on. Every result says how many it hid. Default: `false`.

**Outputs**

- `base` (text)
- `drink_count` (number)
- `drinks` (list)
- `hidden_count` (number)
- `ice` (text)
- `include_test_entries` (true/false)
- `ingredients` (list)
- `menu_size` (number)
- `requested_count` (number)
- `search` (text)
- `sort` (text)
- `total_matches` (number)

**Example output**

```json
{
  "base": "coffee",
  "drink_count": 2,
  "drinks": [
    {
      "base": "coffee",
      "description": "Coffee that's steeped in cold water for at least 12 hours, which makes for a smoother, less acidic cup than hot-brewed coffee poured over ice.",
      "drink_id": "iced-3",
      "iced": true,
      "image_url": "https://upload.wikimedia.org/wikipedia/commons/6/6a/Cold_brew_coffee.jpg",
      "ingredients": [
        {
          "name": "Long steeped coffee",
          "optional": false
        },
        {
          "name": "Ice",
          "optional": false
        }
      ],
      "ingredients_text": "Long steeped coffee, Ice",
      "name": "Cold Brew",
      "test_entry": false
    },
    {
      "base": "coffee",
      "description": "A coffee with ice, typically served with a dash of milk, cream or sweetener\u2014iced coffee is really as simple as that.",
      "drink_id": "iced-1",
      "iced": true,
      "image_url": "https://upload.wikimedia.org/wikipedia/commons/d/d8/Blue_Bottle%2C_Kyoto_Style_Ice_Coffee_%285909775445%29.jpg",
      "ingredients": [
        {
          "name": "Coffee",
          "optional": false
        },
        {
          "name": "Ice",
          "optional": false
        },
        {
          "name": "Sugar",
          "optional": true
        },
        {
          "name": "Cream",
          "optional": true
        }
      ],
      "ingredients_text": "Coffee, Ice, Sugar (optional), Cream (optional)",
      "name": "Iced Coffee",
      "test_entry": false
    }
  ],
  "hidden_count": 3,
  "ice": "with_ice",
  "include_test_entries": false,
  "ingredients": [
    "Ice"
  ],
  "menu_size": 26,
  "requested_count": 25,
  "search": null,
  "sort": "name",
  "total_matches": 2
}
```

### Get a drink

Look up one drink and get its description, what goes into it, and a photo. Pick it from the list, or pass a name or a drink id in from an earlier step: an id costs one small request, and a name is matched against the menu, so 'cold brew' finds Cold Brew and a name that fits two drinks comes back saying which rather than guessing between them.

**Inputs**

- Drink (`drink`, text, required): The drink to look up. Pick one from the list, or type a name (or a drink id such as hot-2) from an earlier step. A partial name works when only one drink matches it.

**Outputs**

- `drink` (object)
- `matched_exactly` (true/false)
- `requested_drink` (text)

**Example output**

```json
{
  "drink": {
    "base": "coffee",
    "description": "As the most popular coffee drink out there, the latte is made with a shot of espresso and steamed milk, with just a touch of foam. It can be ordered plain or flavored with everything from vanilla to pumpkin spice.",
    "drink_id": "hot-2",
    "iced": false,
    "image_url": "https://images.unsplash.com/photo-1561882468-9110e03e0f78?auto=format&fit=crop&q=60&w=800",
    "ingredients": [
      {
        "name": "Espresso",
        "optional": false
      },
      {
        "name": "Steamed milk",
        "optional": false
      }
    ],
    "ingredients_text": "Espresso, Steamed milk",
    "name": "Latte",
    "test_entry": false
  },
  "matched_exactly": true,
  "requested_drink": "Latte"
}
```

### Get a random drink

Pick one drink at random (the answer to 'what should I order?') from the whole menu or from a corner of it. Different every call. Set a seed when two runs have to agree instead. For a pick that stays the same all day, use Get the drink of the day.

**Inputs**

- Made with (`base`, text): Only keep drinks built on this. The menu is not all coffee (it also carries tea, hot chocolate, orange juice and lemonade), and the service records no such field, so this is read off each recipe: espresso or coffee makes it a coffee, tea or matcha a tea, chocolate or cocoa a chocolate. A mocha counts as a coffee. Leave empty for the whole menu. One of: `coffee`, `tea`, `chocolate`, `other` (Everything else).
- Ice (`ice`, text): Only keep drinks that are, or are not, served over ice. Taken from the recipe rather than from which of the service's two lists a drink is filed under, because that filing is wrong for a sixth of the menu: Iced Latte and Frozen Lemonade both sit on its 'hot' list. Leave empty for both. One of: `with_ice` (Served over ice), `without_ice` (Served without ice).
- Seed (`seed`, text): Any text you like. Two runs given the same seed pick the same drink, which is what a test, a screenshot or a step that retries needs. Leave empty for a genuinely different drink each time.

**Outputs**

- `base` (text)
- `drink` (object)
- `ice` (text)
- `pool_size` (number)
- `seed` (text)

**Example output**

```json
{
  "base": "coffee",
  "drink": {
    "base": "coffee",
    "description": "Coffee that's steeped in cold water for at least 12 hours, which makes for a smoother, less acidic cup than hot-brewed coffee poured over ice.",
    "drink_id": "iced-3",
    "iced": true,
    "image_url": "https://upload.wikimedia.org/wikipedia/commons/6/6a/Cold_brew_coffee.jpg",
    "ingredients": [
      {
        "name": "Long steeped coffee",
        "optional": false
      },
      {
        "name": "Ice",
        "optional": false
      }
    ],
    "ingredients_text": "Long steeped coffee, Ice",
    "name": "Cold Brew",
    "test_entry": false
  },
  "ice": "with_ice",
  "pool_size": 8,
  "seed": null
}
```

### Get the drink of the day

Pick one drink for a day and keep picking that same drink all day. A random pick gives a different one on every call, so a daily workflow that retries, or that two people run an hour apart, posts two different drinks for the one day. This one is fixed by the date: the same date always gives the same drink, and tomorrow gives a different one. Add a seed of your own to have two workspaces post different drinks on the same day.

**Inputs**

- Day (`date`, date): The day to pick a drink for. Leave empty for today in UTC, which is what a scheduled step wants.
- Made with (`base`, text): Only keep drinks built on this. The menu is not all coffee (it also carries tea, hot chocolate, orange juice and lemonade), and the service records no such field, so this is read off each recipe: espresso or coffee makes it a coffee, tea or matcha a tea, chocolate or cocoa a chocolate. A mocha counts as a coffee. Leave empty for the whole menu. One of: `coffee`, `tea`, `chocolate`, `other` (Everything else).
- Ice (`ice`, text): Only keep drinks that are, or are not, served over ice. Taken from the recipe rather than from which of the service's two lists a drink is filed under, because that filing is wrong for a sixth of the menu: Iced Latte and Frozen Lemonade both sit on its 'hot' list. Leave empty for both. One of: `with_ice` (Served over ice), `without_ice` (Served without ice).
- Seed (`seed`, text): Any text you like. Two runs given the same seed pick the same drink, which is what a test, a screenshot or a step that retries needs. Leave empty for a genuinely different drink each time.

**Outputs**

- `base` (text)
- `date` (text)
- `drink` (object)
- `ice` (text)
- `pool_size` (number)
- `seed` (text)

**Example output**

```json
{
  "base": null,
  "date": "2026-09-14",
  "drink": {
    "base": "coffee",
    "description": "As the most popular coffee drink out there, the latte is made with a shot of espresso and steamed milk, with just a touch of foam. It can be ordered plain or flavored with everything from vanilla to pumpkin spice.",
    "drink_id": "hot-2",
    "iced": false,
    "image_url": "https://images.unsplash.com/photo-1561882468-9110e03e0f78?auto=format&fit=crop&q=60&w=800",
    "ingredients": [
      {
        "name": "Espresso",
        "optional": false
      },
      {
        "name": "Steamed milk",
        "optional": false
      }
    ],
    "ingredients_text": "Espresso, Steamed milk",
    "name": "Latte",
    "test_entry": false
  },
  "ice": null,
  "pool_size": 26,
  "seed": "2026-09-14"
}
```

### List ingredients

List everything the menu is made of, with the drinks that use each thing and whether every one of them treats it as optional. The service publishes recipes but no ingredient list, so this is gathered from the menu: it is what a café would order, or what a 'can we make this?' step checks against. Sugar and Sugar* are counted as the one ingredient they are.

**Inputs**

- Order by (`sort`, text): How to order the ingredients. Defaults to the most used first, which is the shopping list; A to Z is easier to read down. One of: `uses` (Used in the most drinks first), `name` (Name, A to Z). Default: `uses`.
- Include test entries (`include_test_entries`, true/false): Show the rows people have left behind while trying out the service's write routes, which anyone can call. There are three today: one called 'Black CoffeeTest' and two called 'test', and they are hidden unless you switch this on. Every result says how many it hid. Default: `false`.

**Outputs**

- `hidden_count` (number)
- `include_test_entries` (true/false)
- `ingredient_count` (number)
- `ingredients` (list)
- `menu_size` (number)
- `sort` (text)

**Example output**

```json
{
  "hidden_count": 3,
  "include_test_entries": false,
  "ingredient_count": 3,
  "ingredients": [
    {
      "always_optional": false,
      "name": "Espresso",
      "used_in": [
        "Cappuccino",
        "Frappuccino",
        "Latte"
      ],
      "used_in_count": 11
    },
    {
      "always_optional": false,
      "name": "Ice",
      "used_in": [
        "Cold Brew",
        "Iced Coffee",
        "Iced Latte"
      ],
      "used_in_count": 9
    },
    {
      "always_optional": true,
      "name": "Cream",
      "used_in": [
        "Iced Coffee",
        "Iced Espresso",
        "Nitro"
      ],
      "used_in_count": 3
    }
  ],
  "menu_size": 26,
  "sort": "uses"
}
```
