Barcode Lookup API
Scan or type a barcode, get the product. Resolve any EAN-13 or UPC code to a food record — brand, category, Nutri-Score, and a nutrition summary — in a single request. Built for scanner-based apps.
curl -H "X-API-Key: food_•••" \
"https://foodbase.dev/v1/foods/barcode/5449000000996"
{
"name_default": "Coca-Cola",
"brand": "Coca-Cola",
"barcode": "5449000000996",
"nutriscore": "e",
"nutrition_summary": {
"energy_kcal": 42,
"sugars_g": 10.6,
"proteins_g": 0,
"fat_g": 0
}
} Prefer to click around? Try the product explorer or the photo playground.
What you get back
name_default / brand
Product name and brand for the scanned code.
name / lang
Full multilingual name set where the label carries several languages; add ?lang= (en, fr, es, de, it, bg) for localized category and allergen fields.
barcode
The barcode as stored in the catalog — zero-padding variants (UPC-12, EAN-13, GTIN-14) of the same code all resolve to it.
nutriscore
Nutri-Score grade (a–e) when available.
nutrition_summary
Headline macros: energy, sugars, protein, fat.
image_url
Product image when available.
Included on the free plan — 100 requests/day, no credit card required. See plans →
Code samples
The same request in curl, JavaScript and Python. Replace YOUR_API_KEY with a key
from your dashboard.
curl "https://foodbase.dev/v1/foods/barcode/5449000000996" \
-H "X-API-Key: YOUR_API_KEY"
const res = await fetch("https://foodbase.dev/v1/foods/barcode/5449000000996", {
headers: { "X-API-Key": "YOUR_API_KEY" },
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
import requests
res = requests.get(
"https://foodbase.dev/v1/foods/barcode/5449000000996",
headers={"X-API-Key": "YOUR_API_KEY"},
timeout=30,
)
res.raise_for_status()
data = res.json()
How it works
What one lookup does
Send GET /v1/foods/barcode/{code} with your key in the X-API-Key header. The code is GTIN-8 up to GTIN-14, at most 20 characters. FoodBase tries an exact match against the stored barcode first, then the zero-padding equivalents of the same number, so a 12-digit UPC-A finds the record stored as a 13-digit EAN.
For 5449000000996 the record comes back with barcode 5449000000996 and id 62ea12b8-b260-c288-5c38-3a4906f5d6dd. That id is stable across re-imports, so store it as your foreign key instead of the barcode string.
When several catalog rows carry the same barcode, the most popular row wins and the pick is deterministic: the same code returns the same product on every call.
- Returned barcode is the stored form, not necessarily the form you sent.
Reading the record
The 200 body is the same full food record the Food Database API returns by id, so parse it once and reuse the parser. For 5449000000996 that means source openfoodfacts, name_default Original Taste with bg and fr entries in the name array, nutriscore e, nova_group 4 and ecoscore_grade not-applicable. Label text such as serving_size_raw (1 can (330 ml)) and quantity (0kg on this record, a contributor error) is returned as typed on the packaging, so treat it as a hint rather than a number.
- categories_tags is the machine-readable taxonomy (en:colas); category is the same path as a comma-joined display string.
- allergens and traces are null when the source lists none, which is not the same as allergen-free.
- image_url points at images.openfoodfacts.org and can change when contributors update the product.
Localization and nutrition
Add ?lang=bg (or en, fr, es, de, it) to fill category_localized, category_segments_localized and allergens_localized from the translation dictionaries. Where no translation exists the field is null rather than an English fallback, so keep category and allergens in your fallback chain. lang does not touch name or ingredients.
nutrition_summary has six keys: energy_kcal, proteins_g, carbs_g, fat_g, sugars_g, fiber_g. Any of them can be null, and the whole object is null when the source recorded no nutrition. For the full per-100 g table, 40+ nutrients where the source has them, call the Nutrition API with the id you just received.
Scanner flow, caching and misses
On 200, render the card from name_default, brand, image_url and nutrition_summary, and keep the id. On 404 the code is not in the catalog: fall back to the Food Image Recognition API with a front-of-pack photo, or hand the user a text search. Do not retry a 404; it will not change until the catalog is updated.
200 responses carry Cache-Control: public, max-age=300, and 404s are cacheable for 60 seconds. Honor them in any cache between your users and FoodBase. Every response also carries X-RateLimit-Second-Remaining and the daily-quota headers; throttle on them before you see a 429.
Coverage is best for European and US retail products, because the catalog is built from Open Food Facts and USDA FoodData Central, with GS1 Bulgaria and Metro Bulgaria on top. Private-label lines and very new products are the most common misses.
Build with it
Barcode scanners
Turn a phone camera scan into instant product + nutrition data.
Pantry & grocery apps
Let users add items by scanning the package barcode.
Retail & checkout
Enrich a basket with food attributes keyed off the barcode.
Limits by plan
| Free | 100 requests/day shared across all endpoints and keys, 1 request/s with a burst of 5. |
|---|---|
| Starter | 5,000 requests/day, 5/s with a burst of 25. |
| Pro | 25,000 requests/day, 20/s with a burst of 100. |
| Enterprise | 100,000 requests/day, 50/s with a burst of 250. |
Every response carries X-RateLimit-* headers with the remaining budget;
every 429 carries Retry-After.
Compare plans →
Errors and how to handle them
404 — No catalog row matches the code or any zero-padding variant. Body: {"error":"Not found","message":"No food with this barcode"}.
Offer photo identification or a text search; cache the miss for the 60 s allowed.
401 — The X-API-Key header is missing or the key is invalid.
Check the header name and the key in your dashboard; proxy the call through your backend rather than shipping the key in a mobile app.
429 — Per-second burst exceeded for this key, or the account's daily quota is used up.
Wait the seconds given in Retry-After, then retry once.
Compared to other APIs
Most barcode APIs answer with a name, a brand and maybe an image; FoodBase answers with the full food record, so one scan gives you ingredients, allergens, Nutri-Score and a nutrition summary without a second call. The catalog is Open Food Facts plus USDA FoodData Central plus GS1 and Metro Bulgaria, which makes it strong for European and US groceries and weaker outside those markets. GTIN zero-padding is handled on the server, so you need no normalization layer. There is no guarantee that every code exists; budget for the 404 path.
Frequently asked questions
Do I need to convert a UPC-A code to EAN-13 before calling? +
No. Send the digits your scanner produced. FoodBase tries the exact string first and then the zero-padding equivalents, so a 12-digit UPC-A finds the 13-digit EAN-13 form and 0000080042556 finds the product stored as 80042556. The barcode field in the response shows the stored form.
What happens if two products share the same barcode? +
The most popular catalog row wins and the pick is deterministic, so repeated lookups return the same product. If you need the alternatives, search by name with the Food Search API instead.
Is the response the same as fetching the product by id? +
Yes. It is the record the Food Database API returns for GET /v1/foods/{id}, including nutrition_summary, categories_tags and the multilingual name and ingredients arrays.
How do I show the product name in the user's language? +
Read the name array and pick the entry whose lang matches; the example carries bg (Кока-Кола), fr (Coca-Cola en canette), de, it, es and more. Fall back to name_default, which is the lang main entry. The ?lang= parameter localizes category and allergens only.
Can I cache barcode results on my side? +
Yes, and you should. Hits arrive with Cache-Control: public, max-age=300 and misses are cacheable for 60 seconds. A shared cache in front of your backend means repeat scans of the same code do not touch your daily quota.
Why does a real product come back 404? +
Coverage follows the sources: Open Food Facts, USDA FoodData Central, GS1 Bulgaria and Metro Bulgaria, so European and US retail products are best covered. Private-label lines and recently launched products are the usual gaps; fall back to the Food Image Recognition API or a text search.
Explore more endpoints
Food Image Recognition API
Turn a photo into food data. Four vision endpoints cover the whole camera flow: decode a barcode, identify a product from its front, read the nutrition label and ingredients, or analyze a plate of food with portion estimates — matched against 6.1M products, in six languages including Cyrillic.
Learn more →Nutrition API
Go beyond the headline macros. Pull a detailed breakdown of 40+ nutrients per product — energy, protein, carbs, fats, sugars, fiber, sodium, vitamins, and minerals — all per 100g, for any food by ID.
Learn more →Food Search API
Full-text search across 6.1 million food products by name, brand, or category — relevance-ranked, paginated, and fast. Search in any language the label was printed in (Cyrillic included), and pass lang=en|fr|es|de|it|bg for localized names, categories and allergens. One GET request returns clean JSON your app can render immediately.
Learn more →Start building with the Barcode Lookup API
Free to start. No credit card required.
Get API key Go to Dashboard