---
title: "Find recipes by the ingredients you already have — Tiny Plates"
description: "Build a \"what can I cook?\" feature with a recipes by ingredients API. How /pantry handles staples, matching and ranking, with code and a live demo."
url: "https://www.tinyplates.dev/blog/recipes-by-ingredients"
---

# Find recipes by the ingredients you already have

Send what's in the kitchen, get recipes ranked by what you already have.

21 October 2026

"What can I cook with what I've got?" is one of the most useful things a food app can answer. It's also one of the fiddliest to build yourself.

It sounds simple. Take the user's ingredients, find recipes that use them. But real kitchens are messy. Almost every recipe wants salt. Does that mean everyone is "missing" salt? Someone types "chicken", but the recipe says "chicken thighs". Is that a match? And a recipe that uses one of your five ingredients isn't really a dinner idea, even if it technically "uses what you have".

So in Tiny Plates we put all of that in one endpoint: `/pantry`. You send what's in the kitchen. You get recipes back, ranked by how much of each one you already have.

```bash
curl 'https://api.tinyplates.dev/pantry?ingredients=chicken,rice,onion&maxMissing=2&language=en' \
  -H 'Authorization: Bearer rd_your_api_key'

```

Every result is a normal recipe object with four extra fields. Here's a real one from that call, trimmed:

```json
{
  "title": "Buttermilk-Marinated Air Fryer Whole Roasted Chicken",
  "coverage": 0.5,
  "matchedIngredients": ["chicken"],
  "missingIngredients": ["buttermilk"],
  "assumedIngredients": ["kosher salt"]
}

```

`matchedIngredients` is what you have. `missingIngredients` is your shopping list. `coverage` is the share of the recipe your pantry covers, from 0 to 1\. And `assumedIngredients` is the stuff we figured you already have.

## How the matching works

Let's go through the messy bits one at a time.

**Staples.** Salt, pepper, water, oil, sugar and flour count as already in your kitchen. A few common versions too, like olive oil, kosher salt and black pepper. They go in `assumedIngredients`, and they don't count towards coverage or the missing list. So a recipe isn't "two things short" just because it wants salt and pepper. If your app keeps a real inventory and you want every ingredient counted, send `staples=false`.

**Names.** You can send plain names or canonical ingredient ids. We lowercase them, turn spaces into hyphens and handle simple plurals, so "Eggs" finds recipes with "egg". Some common synonyms are folded together as well, like scallion and spring onion, or feta cheese and feta.

**"Chicken" vs "chicken thighs".** This one is on purpose, and worth knowing. A match is exact on the ingredient, not a fuzzy text search. So "chicken" matches recipes that use chicken, like a whole roast chicken. It does not match a recipe that calls for chicken breast. We think that's the honest answer, since a whole bird and a pack of breasts aren't the same shopping trip. But it means your app should send what people actually have. If someone has "chicken", it's fine to send `chicken,chicken-breast,chicken-thigh`. The [/ingredients](https://www.tinyplates.dev/docs/api/list-ingredients) endpoint lists the ingredient ids, most used first, which is handy for an autocomplete. If you're curious how we get from "2 large Vidalia onions, sliced" to plain `onion`, we wrote about [parsing ingredient lines](https://www.tinyplates.dev/blog/parse-ingredient-lines).

**Ranking.** Only recipes that use at least one of your ingredients come back. The best covered come first, and on a tie, the one with fewer missing things wins. `maxMissing` cuts the list to recipes you're short at most that many ingredients for. `maxMissing=0` means "what can I cook right now".

The other parameters are the usual ones. `limit` (20 by default, up to 100) and `offset` for paging. `language` for English, Spanish or Swedish recipes. And `without` to leave out recipes with allergens, like `without=milk,peanuts`. You can send up to 20 ingredients per call. The [pantry reference](https://www.tinyplates.dev/docs/api/pantry-recipes) has every parameter and the full response.

## A small "what can I cook?" function

Here's a function you could drop into a server route. It keeps the key on the server, asks for up to two missing ingredients, and splits the answer into "ready now" and "almost there":

```ts
interface PantryRecipe {
  coverage: number
  id: string
  matchedIngredients: string[]
  missingIngredients: string[]
  title: string
  url: string
}

export async function whatCanICook(pantry: string[]) {
  const params = new URLSearchParams({
    ingredients: pantry.join(','),
    language: 'en',
    limit: '50',
    maxMissing: '2'
  })

  const response = await fetch(`https://api.tinyplates.dev/pantry?${params}`, {
    headers: { Authorization: `Bearer ${process.env.TINYPLATES_API_KEY}` }
  })

  if (!response.ok) {
    const body = await response.json().catch(() => null)

    throw new Error(body?.error?.message ?? `Tiny Plates answered ${response.status}.`)
  }

  const { recipes } = (await response.json()) as { recipes: PantryRecipe[] }

  // A recipe that only uses one thing you have isn't much of a dinner idea.
  const meals = recipes.filter((recipe) => recipe.matchedIngredients.length >= 2)

  return {
    almost: meals.filter((recipe) => recipe.missingIngredients.length > 0),
    readyNow: meals.filter((recipe) => recipe.missingIngredients.length === 0)
  }
}

```

That filter line is there for a reason. Coverage is a share, so small recipes score really well. Send eggs, garlic and a few other things, and hard-boiled eggs can come out on top with a coverage of 1\. Technically correct! Not very helpful at 7pm, though. Asking for a bigger page and keeping recipes that use at least two of your ingredients fixes most of it. You could also look at each recipe's `courses` and drop side dishes if you only want mains.

The API's error messages are written for people, so passing `error.message` straight through is fine. Send 21 ingredients and the user is told the limit, not "400".

Want to see it working? This is Fridge Roulette, a little demo built on `/pantry`. Type in what's in your fridge and spin:

## Fridge Roulette

Live, on the real API. Go on, give it a spin.

It asks for 12 recipes with `maxMissing` set to 0 or 1, spins through them, and then calls `/substitutions` for whatever the winner is missing. The [Fridge Roulette guide](https://www.tinyplates.dev/blog/build-a-fridge-roulette-app-with-react) has every line of the React code, so we won't repeat it here.

## Tips for a good pantry screen

A few things we learned while building it:

* **Lead with what's missing.** "You're missing 2 things: buttermilk, lemon" is the most useful line on the card. People decide based on that, not on a percentage.
* **Default to staples on.** Nobody wants a list of recipes that are "missing salt". Only turn staples off if your app really knows what's in the cupboard.
* **Don't show a blank screen.** With `maxMissing=0` and a strange fridge, you can get nothing back. Say so, and offer to allow one missing ingredient.
* **Suggest a swap.** If the missing thing is butter, `/substitutions` can tell you to use olive oil instead, and when that's a bad idea. That turns "almost" into "ready now".
* **Turn "almost" into a list.** Send the recipe to [/shopping-list](https://www.tinyplates.dev/blog/one-shopping-list-for-a-week-of-recipes) and the missing bits come back sorted by aisle.

Each pantry search is one request. The free plan gives you 50 a day and 300 a week, which is plenty to build and test with, and we raise it on request. If you're coming from Yummly's recipes-by-ingredients search, we also wrote up [what to use now that Yummly is gone](https://www.tinyplates.dev/blog/yummly-alternatives).

[Grab a free key](https://www.tinyplates.dev/sign-up) and see what your fridge can do.
