Kcalbase API quickstart
UK-first food and nutrition data, one canonical row, gram weights on every serving, provenance and licence on every row. Read-only, all GET, under /v1.
Base URL: https://api.kcalbase.com. Keys are issued by hand during the preview; email support@kcalbase.com with your app name.
1. Search
curl -s "https://api.kcalbase.com/v1/foods/search?q=digestive&limit=3" \
-H "Authorization: Bearer kb_live_YOUR_KEY"
const r = await fetch('https://api.kcalbase.com/v1/foods/search?q=digestive&limit=3', {
headers: { Authorization: `Bearer ${process.env.KCALBASE_KEY}` },
});
const { results, matched, next_cursor } = await r.json();
import os, requests
r = requests.get("https://api.kcalbase.com/v1/foods/search",
params={"q": "digestive", "limit": 3},
headers={"Authorization": f"Bearer {os.environ['KCALBASE_KEY']}"})
for food in r.json()["results"]:
print(food["name"], food["brand"], food["per_100"]["energy_kcal"])
matched is exact, fuzzy (we corrected a likely typo; say "showing results for") or none.
2. Barcode
curl -s https://api.kcalbase.com/v1/foods/barcode/5000159407236 -H "Authorization: Bearer kb_live_YOUR_KEY"
8, 12, 13 or 14 digits all work. 404 means we do not have it yet.
3. One food, and many at once
GET /v1/foods/{id} returns the same object search does. GET /v1/foods/lookup?barcodes=...&ids=... takes up to 50 items and returns found in your order plus missing as you sent them; it costs one request per 10 items. Send If-None-Match with the ETag you last saw and unchanged rows come back as 304.
The food object
Every route returns the same shape. per_100 is per 100 g or 100 ml (basis). Every serving carries grams and its own nutrients block scaled from per_100. null means the source did not say, never zero. attribution.required tells you whether you must show attribution.text (you must, for open-layer rows). quality and provenance tell you how much to trust a row and where it came from. Full field list in the API reference.
Headers you will see
| Header | Meaning |
|---|---|
X-Request-Id |
Quote it when you report a problem. |
X-Kcalbase-Data-Version, X-Kcalbase-Data-Updated |
Which data snapshot answered, and when it was ingested. |
X-Kcalbase-Quota-Limit, -Used, -Remaining, -Reset |
Your monthly quota, on every metered response. |
ETag |
Send it back as If-None-Match and an unchanged row answers 304: free of bandwidth, because there is no body, but it still counts one request against your monthly quota. |
Cache-Control |
private, must-revalidate on every metered response: the answer carries your quota headers, so no shared cache may replay it and clients should revalidate with the ETag rather than serve a stale copy. |
X-Kcalbase-Cache |
hit or miss. Hits still count as requests. |