A read-only JSON API, a CLI, an MCP server, and an agent skill over about 1.9 million packaged foods from Open Food Facts. There are no API keys or accounts, and every call is a plain GET.
curl -sSL https://nutrition.sh/install | bash
Linux on x86_64 or arm64 gets a static binary, verified by checksum. Anything else builds from source if cargo is installed. The binary is called food and lands in /usr/local/bin or ~/.local/bin.
A CLI that prints JSON when piped and a table when you are looking.
The same binary as an MCP server, so agents get typed tools.
One file that teaches agents the workflow and how to read the numbers.
Search first, then fetch one product by its code. Search returns summaries; the detail call returns the nutrients, ingredients, and tags.
# text search, ranked by relevance food search "dark chocolate" # filters combine with AND; tags use Open Food Facts form food search chocolate --category en:snacks --nutriscore a --limit 5 # nutrient ranges are per 100 g food search --nutrient sugars --max 5 # one product, everything the dataset knows food get 0081014090553 # which nutrient names exist food nutrients vitamin
Output is the raw API JSON whenever stdout is a pipe or a file, so food get 0081014090553 | jq .nutrients needs no flag. On a terminal you get a readable table instead. --json and --table force either. --pages 3 follows the cursor and merges three pages.
food search "greek yogurt" --nutriscore a --limit 2 | jq -c '.data[] | {code, name, brands}' {"code":"5051898781057","name":"Tesco Fat Free Greek Yogurt 200G","brands":"Tesco"} {"code":"4088600073321","name":"Brooklea Greek 0% Fat Yogurt with Raspberry","brands":"Brooklea, Aldi"}
The same binary runs a Model Context Protocol server over stdio. Register it with Claude Code in one line:
claude mcp add food -- food mcp
Or add it to any MCP client configuration:
{ "mcpServers": { "food": { "command": "food", "args": ["mcp"] } } }
| Tool | What it does |
|---|---|
search_products | Text search plus every filter as an optional typed argument. Returns summaries and a cursor. |
get_product | Full detail for one code: nutrients, ingredients, localized names, tags, scores. |
list_nutrients | Valid nutrient identifiers with unit, product count, and observed range. |
get_dataset_status | Product count, schema version, and whether the import is complete. |
Tool results are the API's JSON. API errors come back as tool errors carrying the API's code and message, which lets the model recover. The server's own instructions cover tag formats, units, and paging, so agents need no extra prompting.
A skill teaches an agent when to use the CLI and how to read the data: the search-then-get workflow, tag formats, paging, and the unit rules below. It installs into Claude Code, Cursor, Codex, and most other agents with one command:
npx skills add https://nutrition.sh/skill.tar.gz
Add -g to install it globally instead of into the current project. The skill prefers the MCP tools when they are present and falls back to the CLI, so you can install both. Read it at skills/food/SKILL.md.
Base URL https://nutrition.sh. Every endpoint is a GET that returns JSON. The full contract is in openapi.yaml.
| Path | Purpose |
|---|---|
/api/v1/products | Browse, search, and filter products |
/api/v1/products/{code} | One complete product |
/api/v1/nutrients | The nutrient catalog |
/api/v1 | Dataset and schema metadata |
/health | Readiness and product count |
curl 'https://nutrition.sh/api/v1/products?q=peanut%20butter&nutriscore_grade=a&limit=2' { "data": [ { "code": "0068100084665", "name": "PEANUT BUTTER", "brands": "ONLY PEANUTS Kraft", "nutriscore_grade": "a", "nova_group": 2, ... } ], "page": { "limit": 2, "next_cursor": "fts:2" } }
All parameters are optional and combine with AND.
| Parameter | Meaning |
|---|---|
q | Prefix search over names, brands, and ingredients. Every word must match. |
brand, category, country, label, allergen | Exact normalized tags such as en:snacks, en:organic, en:milk |
nutriscore_grade | a through e |
nova_group | 1 through 4 |
nutrient, min_per_100g, max_per_100g | Inclusive range on one nutrient, for example sugars or energy-kcal |
limit, cursor | Page size 1 to 100, and the opaque next_cursor from the previous page |
Each nutrient record carries two units. unit belongs to value, the number as printed on the package. per_100g_unit belongs to per_100g and per_serving, normalized the way Open Food Facts stores them: grams for mass nutrients, kJ for energy, kcal for energy-kcal.
{ "name": "cholesterol", "unit": "mg", "value": 70, "per_100g_unit": "g", "per_100g": 0.0625, "per_serving": 0.07 }
Errors share one envelope. 400 for a bad parameter or cursor, 404 for an unknown code, 500 if the database or search fails.
{ "error": { "code": "bad_request", "message": "min_per_100g and max_per_100g require nutrient" } }