Food & Nutrition API

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:

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:

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
Your key is private. Do not embed it in client-side code that ships to end users (a server-side proxy is best). A missing or wrong key returns 401.

Base URL

https://api.dayform.day

Lookup a food

GET /api/v1/food/lookup

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 drinkCelsius 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

ParamTypeDescription
q requiredstringThe food name, e.g. Big Mac or Starbucks Grande Caramel Macchiato. (name is accepted as an alias.)
portionstringOptional portion hint, e.g. 2 tbsp or large.
keystringAPI 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": "..." }
GET /api/v1/food/search

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

ParamTypeDescription
q requiredstringSearch text (min 2 characters), e.g. chicken sandwich or starbucks.
pageinteger1-based page number. Default 1.
limitintegerResults per page, 1 to 100. Default 20.
keystringAPI 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": []
    }
  ]
}
To page through results, increment 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

FieldTypeDescription
namestringCleaned, canonical product name (corrected spelling and casing, brand prefixed).
brandstring | nullBrand 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.
detailobjectRich 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.
confidencenumber0 to 1, how confident the value matches the named item.
sourcesarrayWeb pages used, as { title, url }. Empty for curated database entries.
originstringseed (curated database) or llm (a prior web lookup).
cachedbooleanLookup only. true if served from the database with no model call.
createdAt / updatedAtstring | nullLookup only. When the entry was first stored / last updated.

Errors

Errors return a JSON body of the shape { "error": "message" } with an HTTP status:

StatusMeaning
400Missing required parameter (e.g. no q).
401Missing, invalid, expired, or revoked API key.
404Query is a real food but too vague to resolve; a suggestions array of close matches is returned.
429Rate limit reached (your key's daily limit, or a global cap). Retry later.
502Lookup or search failed upstream. Retry.
503API 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