Shopping
Turn recipe requirements and pantry gaps into a clean, canonical shopping list your users can check off.
What you can build
“Buy what’s missing” buttons, grocery-text cleaners, meal-plan shopping trips, and MCP agents that assemble lists without double-counting pantry stock.
1. Find what is missing for recipes
Pass recipe ids (from a meal plan or a single cook intent). FoodAPI compares requirements to the current pantry partition.
missing = client.shopping.missing_ingredients(
recipe_ids=[recipe_id],
external_user_id="user_42",
)
for row in missing.missing_ingredients:
print(
row.canonical_name,
row.required_quantity,
row.available_quantity,
row.missing_quantity,
row.missing_unit,
row.coverage_percent,
)2. Normalize a free-text grocery list
When users paste a list, normalize lines into canonical names, quantities, and units.
normalized = client.shopping.normalize_list(
items=["2 lbs ground beef", "1 dozen eggs", "olive oil"],
external_user_id="user_42",
)
for item in normalized.normalized_items:
print(item.canonical_name, item.total_quantity, item.canonical_unit, item.ingredient_id){
"normalized_items": [
{
"canonical_name": "ground beef",
"total_quantity": 2.0,
"canonical_unit": "lb",
"ingredient_id": "…",
"sources": ["2 lbs ground beef"]
}
]
}3. Aggregate multiple recipes
Collapse several recipes into one requirement set (useful before shopping-list persistence).
agg = client.shopping.aggregate_recipes(
recipe_ids=recipe_ids,
external_user_id="user_42",
)
for row in agg.ingredients:
print(row.canonical_name, row.total_quantity, row.canonical_unit, row.sources)4. Persist a shopping list
Stateless shopping endpoints compute needs. Use shopping_lists / shoppingLists when you need a durable checklist with revisioned item writes.
slist = client.shopping_lists.create(
payload={
"name": "Friday groceries",
"status": "active",
"source_meal_plan_id": str(plan_id) if plan_id else None,
},
external_user_id="user_42",
)
# Add the first missing item (example)
first = missing.missing_ingredients[0]
mutation = client.shopping_lists.items.create(
slist.id,
expected_revision=slist.revision,
payload={
"display_name": first.canonical_name,
"ingredient_id": str(first.ingredient_id),
"quantity": first.missing_quantity,
"unit": first.missing_unit,
},
external_user_id="user_42",
)
print(mutation.item.id, mutation.revision)Production considerations
- Always pass the same
external_user_idused for pantry so “missing” reflects that user’s stock. - Quantities may be null when units cannot be reconciled — show display names and let users edit.
- MCP
build_shopping_listcomposes missing + aggregate calls (shopping + pantry read scopes). Scopes
Next
Connect shopping to the rest of the kitchen