DayForm Food API
Look up calories, macros, and full nutrition labels for thousands of foods, restaurant items, and supplements. Anything not already in the database is looked up on the web, cleaned up, and cached for next time.
Overview
The Food API is a read-only HTTP API backed by a curated nutrition database of roughly 8,000 foods (restaurant chains, grocery and packaged brands, supplements, and generic whole foods), most with a full nutrition-facts label. It has two endpoints:
- Lookup: resolve a single food by name. Cache-first, with a live web-search fallback for items not yet stored. The result is saved so the next lookup of that item is instant.
- Search: search the existing database by keyword, paginated. Database-only (no web fallback), so it is always fast and free.
All responses are JSON. All requests are GET. Calories and macros are returned as low and high ranges to reflect normal portion variation.
Request a key
API keys are issued by hand so we can keep the shared database clean and prevent abuse. To request one, email [email protected] with the subject “DayForm Food API key request” and include:
- Your app or project name and a one-line description of what you're building.
- Roughly how many lookups per day or month you expect.
- The best contact email.
We'll reply with a key, its daily limit, and any expiry. Each key is independently rate-limited and revocable, and you can ask us for your usage at any time.
Authentication
Every request requires an API key. Pass it as a bearer token (preferred) or as a query parameter:
Authorization: Bearer YOUR_API_KEY # or ?key=YOUR_API_KEY
401.Base URL
https://api.dayform.day
Lookup a food
Resolve a single food by name. If it is already in the database it returns instantly (cached: true). If not, it is looked up on the web, cleaned into a canonical name, stored, and returned (cached: false, origin: "llm").
Matching is fuzzy and semantic, not exact string match: spacing and accents (BigMac = Big Mac), minor typos, and natural phrasing (a celsius orange energy drink → Celsius Sparkling Orange) all resolve to the right existing item via vector similarity, so the same product is never looked up (or paid for) twice. Different products are kept distinct: a zero / diet / sugar-free variant never matches the regular item. A query that is real food but too generic to pin down (e.g. a sandwich) returns 404 with a suggestions array of close matches rather than guessing.
Query parameters
| Param | Type | Description |
|---|---|---|
| q required | string | The food name, e.g. Big Mac or Starbucks Grande Caramel Macchiato. (name is accepted as an alias.) |
| portion | string | Optional portion hint, e.g. 2 tbsp or large. |
| key | string | API key, if not sent via the Authorization header. |
Example request
curl "https://api.dayform.day/api/v1/food/lookup?q=Big%20Mac" \ -H "Authorization: Bearer YOUR_API_KEY"
Example response (200)
{
"ok": true,
"query": "Big Mac",
"name": "McDonald's Big Mac",
"brand": "McDonald's",
"calories": { "low": 580, "high": 580 },
"macros": {
"low": { "protein_g": 26, "carbs_g": 44, "fat_g": 33 },
"high": { "protein_g": 26, "carbs_g": 44, "fat_g": 33 }
},
"detail": {
"servingText": "1 sandwich",
"category": "Burger",
"ingredients": "Beef patties, bun, special sauce, lettuce, cheese, pickles, onions",
"allergens": "Wheat, milk, soy, egg",
"description": "Two beef patties with special sauce on a sesame bun.",
"fiber_g": 3, "sugar_g": 9, "sodium_mg": 1010,
"nutrients": [
{ "name": "Saturated Fat", "amount": 15, "unit": "g" },
{ "name": "Cholesterol", "amount": 85, "unit": "mg" }
]
},
"confidence": 0.95,
"assumptions": "Per the official McDonald's US nutrition page.",
"sources": [ { "title": "Big Mac - McDonald's", "url": "https://www.mcdonalds.com/..." } ],
"cached": true,
"origin": "seed",
"createdAt": "2026-06-21 12:00:00",
"updatedAt": "2026-06-21 12:00:00"
}
Not a food
The query is moderated. If it is not a real food, drink, or edible product, you get:
{ "ok": false, "isFood": false, "query": "..." }
Search (paginated)
Search the existing database by keyword. Matches every word in the query against the food name, ranked by popularity. Database-only (no web lookup), so it never bills a model call. Results are paginated.
Query parameters
| Param | Type | Description |
|---|---|---|
| q required | string | Search text (min 2 characters), e.g. chicken sandwich or starbucks. |
| page | integer | 1-based page number. Default 1. |
| limit | integer | Results per page, 1 to 100. Default 20. |
| key | string | API key, if not sent via the Authorization header. |
Example request
curl "https://api.dayform.day/api/v1/food/search?q=chicken%20sandwich&page=1&limit=20" \ -H "Authorization: Bearer YOUR_API_KEY"
Example response (200)
{
"ok": true,
"query": "chicken sandwich",
"page": 1,
"limit": 20,
"total": 137,
"totalPages": 7,
"hasMore": true,
"items": [
{
"name": "Chick-fil-A Chicken Sandwich",
"brand": "Chick-fil-A",
"calories": { "low": 440, "high": 440 },
"macros": {
"low": { "protein_g": 28, "carbs_g": 41, "fat_g": 19 },
"high": { "protein_g": 28, "carbs_g": 41, "fat_g": 19 }
},
"detail": { "servingText": "1 sandwich", "category": "Chicken sandwich", "...": "..." },
"confidence": 0.9,
"origin": "seed",
"sources": []
}
]
}
page until hasMore is false (or page reaches totalPages). An empty or too-short query returns total: 0 with an empty items array, not an error.Response fields
| Field | Type | Description |
|---|---|---|
| name | string | Cleaned, canonical product name (corrected spelling and casing, brand prefixed). |
| brand | string | null | Brand or restaurant, or null for a generic food. |
| calories | { low, high } | Calorie range (kcal) for one serving. |
| macros | { low, high } | Each side has protein_g, carbs_g, fat_g. |
| detail | object | Rich label: servingText, category, ingredients, allergens, description, fiber_g, sugar_g, sodium_mg, and nutrients (an array of { name, amount, unit } rows for the full nutrition facts). Any field may be absent. |
| confidence | number | 0 to 1, how confident the value matches the named item. |
| sources | array | Web pages used, as { title, url }. Empty for curated database entries. |
| origin | string | seed (curated database) or llm (a prior web lookup). |
| cached | boolean | Lookup only. true if served from the database with no model call. |
| createdAt / updatedAt | string | null | Lookup only. When the entry was first stored / last updated. |
Errors
Errors return a JSON body of the shape { "error": "message" } with an HTTP status:
| Status | Meaning |
|---|---|
| 400 | Missing required parameter (e.g. no q). |
| 401 | Missing, invalid, expired, or revoked API key. |
| 404 | Query is a real food but too vague to resolve; a suggestions array of close matches is returned. |
| 429 | Rate limit reached (your key's daily limit, or a global cap). Retry later. |
| 502 | Lookup or search failed upstream. Retry. |
| 503 | API not configured or temporarily over capacity. |
JavaScript example
const res = await fetch(
"https://api.dayform.day/api/v1/food/lookup?q=" + encodeURIComponent("chicken biryani"),
{ headers: { Authorization: "Bearer " + process.env.DAYFORM_FOOD_API_KEY } }
);
const food = await res.json();
if (food.ok) {
const avg = (food.calories.low + food.calories.high) / 2;
console.log(food.name, avg, "kcal");
}
Notes & limits
- Caching. Lookups are cache-first and shared across all callers, so popular items are instant and free. A cache miss does a single web search, then stores the result.
- Ranges. Calories and macros are low/high bands. For a single number, use the midpoint.
- Moderation. Non-food queries return
{ ok: false, isFood: false }and are never stored in a way that surfaces them. - Search vs lookup. Use search to browse what already exists (fast, free, paginated). Use lookup when you need a specific item resolved, including ones not yet in the database.
- Method. Both endpoints are
GETand read-only. There are no write endpoints. - Rate limits. Each key has its own daily request limit (set when issued), on top of global caps. Exceeding a limit returns
429; it resets at midnight UTC. Need more headroom? Just ask.