# Calorie API - Full API Documentation
> Food calorie API for developers: REST search, barcode lookup, macros per 100g, and autocomplete across our food database API. Free tier for nutrition apps.
This file is the complete plain-text API documentation for LLMs and crawlers.
Site: https://calorieapi.com · Summary index: https://calorieapi.com/llms.txt
# API Reference
## Authentication
URL: https://calorieapi.com/docs/authentication
Updated: 2026-07-03
All API requests are authenticated with an API key sent in the X-API-Key header. Keys are generated in the developer dashboard after creating an account, and each key is tied to your plan’s rate limits and monthly quota.
Authenticated request:
```
curl "https://calorieapiadmin.com/api/v1/search/foods?q=apple" \
-H "X-API-Key: your_api_key_here"
```
### Getting an API key
- Create a free account, no credit card required for the Free tier.
- Choose a plan (you can start on Free and upgrade later).
- Generate a key from the dashboard under API Keys.
### Keep your API key secure
Never expose your API key in client-side code or public repositories. Call the API from your backend, or proxy requests through your own server, and store keys in environment variables or a secrets manager. Keys are hashed at rest on our side and can be revoked and regenerated from the dashboard at any time.
### Authentication errors
Auth-related status codes:
- 401: Missing or invalid API key. Check the X-API-Key header.
- 402: Monthly quota exceeded. Upgrade your plan or wait for the cycle reset.
- 403: Commercial use not allowed on your plan, or food coverage cap reached.
Dashboard sessions use JWT authentication after sign-in; API traffic from your applications should always use API keys.
### FAQ
Q: Can I use the API key in a mobile app directly?
A: Avoid shipping raw API keys in mobile binaries, they can be extracted. Route requests through your backend so the key stays server-side, and enforce your own per-user limits in front of it.
Q: How do I rotate an API key?
A: Generate a new key in the dashboard, deploy it to your services, then revoke the old key. Revocation is immediate.
Q: Do I need different keys for staging and production?
A: It is good practice to create separate keys per environment so you can revoke or monitor them independently in the dashboard.
---
## Food Search
URL: https://calorieapi.com/docs/food-search
Updated: 2026-07-03
Search foods by name or brand with multi-word matching and relevance ranking. Results include complete nutrition data, per-100g macros, the nutrients array, and serving metadata, and only foods with complete macro data are returned. An empty query returns common and popular foods.
GET /api/v1/search/foods:
```
curl "https://calorieapiadmin.com/api/v1/search/foods?q=chicken+breast&match_mode=all" \
-H "X-API-Key: your_api_key_here"
```
Query parameters:
- q: Search query (min 2 characters; empty returns common foods)
- limit: Results per page, 1-100 (default: 30). k is accepted as an alias.
- skip: Offset for pagination (default: 0)
- brand: Filter results to a brand name
- match_mode: "any" (default) or "all", whether every word must match
- verified_only: true to return only verified foods with curated macro data
Response:
```json
{
"data": [
{
"id": 12345,
"name": "Apple, raw",
"brand": "Generic",
"calories": 52,
"protein": 0.3,
"carbs": 13.8,
"fat": 0.2
}
],
"total": 1,
"skip": 0,
"limit": 30
}
```
### How ranking works
- Exact phrase at the start of the name ("chicken breast" matches "Chicken breast, raw") ranks first.
- Exact phrase anywhere in the name ranks next.
- All words present in any order, or most words for long queries, follows.
- First word or any word present ranks last.
### Autocomplete suggest
For typeahead UIs, the suggest endpoint returns lightweight results (id, name, brand_name) so you can render suggestions fast, then fetch full nutrition data with the food details endpoint after the user selects one. Debounce keystrokes in your client to conserve quota.
GET /api/v1/search/suggest:
```
curl "https://calorieapiadmin.com/api/v1/search/suggest?q=chick&limit=10" \
-H "X-API-Key: your_api_key_here"
```
Query parameters:
- q: Prefix to autocomplete (min 1 character)
- limit: Suggestions to return, 1-20 (default: 15)
### FAQ
Q: How do I paginate through search results?
A: Use skip and limit together; the response envelope includes total, so you can compute page counts. limit accepts values up to 100.
Q: When should I use match_mode=all?
A: Use "all" when precision matters more than recall, for example matching a logged meal name exactly. The default "any" is better for exploratory search UIs.
Q: What does verified_only do?
A: It restricts results to curated foods with complete, quality-checked macro data. Use it when data accuracy matters more than catalog coverage.
---
## Barcode Lookup
URL: https://calorieapi.com/docs/barcode-lookup
Updated: 2026-07-03
Resolve a UPC or EAN barcode to product and nutrition data. The API checks the local food database first; when no match is found, it falls back to Open Food Facts and returns only the fields needed for logging: product details, serving size, macros per 100 g (and per serving when available), and micronutrients when present.
GET /api/v1/search/barcode/{upc}:
```
curl "https://calorieapiadmin.com/api/v1/search/barcode/3017620422003" \
-H "X-API-Key: your_api_key_here"
```
Path parameters:
- upc: UPC/EAN barcode (digits; dashes are stripped)
Response example (Nutella):
```json
{
"barcode": "3017620422003",
"product": {
"name": "Nutella",
"brand": "Nutella",
"category": "Pâtes à tartiner aux noisettes et au cacao, en:Pâtes à tartiner",
"generic_name": "Pâte à tartiner aux noisettes et au cacao",
"quantity": null,
"ingredients": "sugar, palm oil, hazelnuts 13%, low-fat cocoa 7.4%, skimmed milk powder 6.6%, whey powder, emulsifiers: lecithins [soya], vanillin, gluten-free,",
"allergens": [
"milk",
"nuts",
"soybeans"
]
},
"serving": {
"label": "100g",
"quantity": null,
"unit": "g"
},
"nutrition_per_100g": {
"energy_kcal": 539,
"protein_g": 6.3,
"carbohydrates_g": 57.5,
"fat_g": 30.9,
"fiber_g": null,
"sugars_g": 56.3,
"saturated_fat_g": 10.6,
"salt_g": 0.107,
"sodium_g": 0.0428,
"added_sugars_g": 38.35
},
"nutrition_per_serving": null,
"micronutrients_per_100g": []
}
```
### Response fields
Response fields:
- barcode: Normalized UPC/EAN digits
- product.name: Product title
- product.brand: Primary brand name
- product.category: Food category label
- product.generic_name: Short product description when available
- product.quantity: Package size when available
- product.ingredients: Ingredients text
- product.allergens: Allergen list
- serving.label: Human-readable serving size
- serving.quantity: Numeric serving amount when available
- serving.unit: Serving unit (g, ml, etc.)
- nutrition_per_100g: Macros per 100 g: energy_kcal, protein_g, carbohydrates_g, fat_g, fiber_g, sugars_g, saturated_fat_g, salt_g, sodium_g, added_sugars_g
- nutrition_per_serving: Same macro fields per labeled serving when Open Food Facts provides serving data
- micronutrients_per_100g: Vitamins and minerals per 100 g when present (name, amount, unit)
Open Food Facts raw payloads include thousands of metadata fields; this endpoint strips that down to logging-ready product and nutrition data, so the response shape is identical whether the product came from the local catalog or the fallback.
### Handling misses
If neither the local database nor Open Food Facts knows the barcode, the API returns HTTP 404 with a "Food not found for barcode" message. A good pattern is to fall back to food search in your UI so the user can log the item manually.
### FAQ
Q: Which barcode formats are supported?
A: UPC and EAN barcodes. Pass the digits in the URL path; dashes are stripped automatically.
Q: Is the Open Food Facts fallback automatic?
A: Yes. The lookup order is always local catalog first, then Open Food Facts, with a single normalized response shape either way, your client code does not need to know the source.
Q: Why are some nutrition fields null?
A: Label data varies by product and region. Fields the source does not provide (for example fiber or per-serving values) are null rather than omitted, so your parsers stay simple.
---
## Food Details
URL: https://calorieapi.com/docs/food-details
Updated: 2026-07-03
Fetch the complete nutrition payload for a single food by its ID, typically after a user picks a result from search or autocomplete suggest. The response includes per-100g macro values, the full structured nutrients array (micronutrients when available), and serving metadata for meal logging.
GET /api/v1/foods/{id}:
```
curl "https://calorieapiadmin.com/api/v1/foods/12345" \
-H "X-API-Key: your_api_key_here"
```
Path parameters:
- id: Food ID from search, suggest, or list responses
### Typical flow
- Autocomplete with /search/suggest while the user types (lightweight id + name payloads).
- Fetch /foods/{id} when the user selects a suggestion to get full macros and nutrients.
- Store the food ID with the log entry so re-logging skips the search step.
Food IDs are stable, so caching details client-side or in your backend is safe and saves quota for frequently logged foods.
### FAQ
Q: Are food IDs stable across time?
A: Yes, IDs are stable identifiers, so you can persist them with user logs and re-fetch details later.
Q: Does the details endpoint include micronutrients?
A: Yes, when available. The nutrients array covers vitamins and minerals alongside the guaranteed per-100g macro set.
---
## Nutrients, Brands & Categories
URL: https://calorieapi.com/docs/reference-data
Updated: 2026-07-03
Three paginated reference endpoints expose the taxonomy behind the food catalog. Use them to build filter dropdowns, map your own nutrition models, or validate data coming from search.
### Nutrients
List the nutrient definitions used in food payloads, macronutrients, vitamins, and minerals with their units.
GET /api/v1/foods/nutrients/:
```
curl "https://calorieapiadmin.com/api/v1/foods/nutrients/" \
-H "X-API-Key: your_api_key_here" \
-G -d "limit=50" -d "skip=0"
```
### Brands
Browse brand records referenced by branded foods. Combine with the brand filter on food search to scope results to a manufacturer.
GET /api/v1/foods/brands/:
```
curl "https://calorieapiadmin.com/api/v1/foods/brands/" \
-H "X-API-Key: your_api_key_here" \
-G -d "limit=100" -d "skip=0"
```
### Categories
Access food categories and subcategories for organizing and filtering foods in your UI.
GET /api/v1/foods/categories/:
```
curl "https://calorieapiadmin.com/api/v1/foods/categories/" \
-H "X-API-Key: your_api_key_here" \
-G -d "limit=50" -d "skip=0"
```
Query parameters (all three endpoints):
- limit: Results per page (default and max vary by endpoint, up to 100)
- skip: Offset for pagination (default: 0)
All three return the standard paginated envelope (data, total, skip, limit). Reference data changes rarely, so it is a good candidate for caching in your application.
### FAQ
Q: How often does reference data change?
A: Rarely; nutrients, brands, and categories evolve with catalog updates but are stable enough to cache for a day or longer in your application.
Q: Can I search brands by name?
A: Yes, GET /api/v1/search/brands?q={name} performs a name search over brands, complementing the paginated listing endpoint.
---
## Rate Limits & Quotas
URL: https://calorieapi.com/docs/rate-limits
Updated: 2026-07-03
Limits apply per account (user id), not per IP, which keeps NAT and multi-tenant apps safe. Each plan combines a per-minute rate limit with a monthly request quota; see the pricing page for current quotas.
Per-minute rate limits by plan:
- Free: 10 requests/min
- Basic: 200 requests/min
- Core: 500 requests/min
- Plus: 5,000 requests/min · response caching
- Enterprise: Custom (negotiated)
### Rate-limit headers
Rate-limited (429) responses include headers you can use for client-side backoff.
Response headers:
- X-RateLimit-Limit: Your plan’s per-minute request limit
- X-RateLimit-Remaining: Requests remaining in the current window
- X-RateLimit-Reset: When the current window resets
### Abuse protection & commercial use
- 5% food coverage cap: each plan may access at most 5% of distinct foods in the database per calendar month (anti-scrape).
- Commercial use requires Plus or Enterprise. Send X-API-Usage-Type: commercial only when your app is a commercial product.
- Plus and Enterprise GET search/food responses may be cached for 5 minutes per account (Redis).
### Limit-related status codes
Status codes:
- 429: Per-minute rate limit exceeded. Back off and retry after X-RateLimit-Reset.
- 402: Monthly quota exceeded. Upgrade your plan or wait for the billing cycle reset.
- 403: Commercial use not allowed on your plan, or food coverage cap reached.
### FAQ
Q: Do rate limits apply per API key or per account?
A: Per account (user id). Creating multiple keys under one account does not increase your limits.
Q: What is the 5% food coverage cap?
A: An anti-scraping protection: within a calendar month, one plan can access at most 5% of the distinct foods in the database. Normal app usage never gets close to it; bulk exports do.
Q: How do I raise my limits?
A: Upgrade your plan from the dashboard, rate limit and quota changes take effect immediately. Enterprise plans negotiate custom limits.
---
## Error Handling
URL: https://calorieapi.com/docs/errors
Updated: 2026-07-03
Errors use conventional HTTP status codes with a JSON body containing a human-readable detail message. Client errors (4xx) indicate a problem with the request or account state; 5xx indicates a problem on our side.
Error response body:
```json
{
"detail": "Search query must be at least 2 characters or empty for common foods."
}
```
Status codes:
- 400: Bad request, invalid parameters (e.g. query too short, bad match_mode).
- 401: Unauthorized, missing or invalid API key.
- 402: Monthly quota exceeded for your plan.
- 403: Forbidden, commercial use not allowed on plan, or food coverage cap reached.
- 404: Not found, unknown food ID or barcode with no match in any source.
- 429: Rate limited, per-minute limit exceeded; see X-RateLimit-Reset.
- 500: Server error, safe to retry with backoff; report persistent failures.
### Retry guidance
- 429: retry after the X-RateLimit-Reset time with exponential backoff and jitter.
- 402 and 403: do not retry, these persist until the plan or usage state changes.
- 404 on barcode lookup: fall back to food search so users can log the item manually.
- 5xx: retry with backoff; if errors persist, check the status page and contact support.
### FAQ
Q: Should I retry 402 quota errors?
A: No. 402 persists until your monthly cycle resets or you upgrade. Detect it, surface an upgrade path in your admin tooling, and stop retrying.
Q: How do I distinguish a rate limit from a quota error?
A: Per-minute rate limiting returns 429 with X-RateLimit-* headers; monthly quota exhaustion returns 402. Handle them separately, 429 is transient, 402 is not.
# Integration Guides
## Guide: Build a Food Tracking App with React Native (React Native)
URL: https://calorieapi.com/docs/guides/react-native-food-tracking
Updated: 2026-07-03
This guide wires the three endpoints a food tracker needs (autocomplete suggest, food details, and barcode lookup) into a React Native app. Requests are routed through a small backend proxy so your API key never ships inside the app binary.
### Set up a backend proxy
Mobile binaries can be decompiled, so keep the X-API-Key header server-side. A minimal Express proxy forwards search traffic and adds the key:
server.js (Express proxy):
```
const express = require('express')
const app = express()
const API_BASE = 'https://calorieapiadmin.com/api/v1'
app.get('/api/food-search', async (req, res) => {
const url = new URL(API_BASE + '/search/suggest')
url.searchParams.set('q', req.query.q ?? '')
url.searchParams.set('limit', '10')
const upstream = await fetch(url, {
headers: { 'X-API-Key': process.env.CALORIE_API_KEY },
})
res.status(upstream.status).json(await upstream.json())
})
app.listen(3001)
```
### Debounced autocomplete
Debounce keystrokes so a fast typist costs one request instead of ten. It keeps the UI responsive and conserves your monthly quota.
useFoodSuggest.ts:
```
import { useEffect, useState } from 'react'
export function useFoodSuggest(query: string) {
const [results, setResults] = useState([])
useEffect(() => {
if (query.length < 2) return
const t = setTimeout(async () => {
const res = await fetch(
`https://your-backend.example.com/api/food-search?q=${encodeURIComponent(query)}`
)
if (res.ok) setResults(await res.json())
}, 250)
return () => clearTimeout(t)
}, [query])
return results
}
```
### Fetch full nutrition on selection
Suggest responses are intentionally lightweight (id, name, brand_name). When the user picks a suggestion, fetch GET /api/v1/foods/{id} through your proxy for per-100g macros, the nutrients array, and serving metadata, then compute logged amounts from the per-100g values.
### Barcode scanning
Pair a scanner library (for example expo-barcode-scanner) with the barcode endpoint. The API resolves UPC/EAN codes against the local catalog and falls back to Open Food Facts automatically, so one code path covers both.
Scan handler:
```
const onBarCodeScanned = async ({ data: upc }) => {
const res = await fetch(
`https://your-backend.example.com/api/barcode/${upc}`
)
if (res.status === 404) {
// Unknown product, fall back to manual search
navigation.navigate('FoodSearch')
return
}
const food = await res.json()
navigation.navigate('LogFood', { food })
}
```
### Production tips
- Cache food details by ID on-device, IDs are stable, and re-logging favorites then costs zero API calls.
- Handle 429 with backoff using the X-RateLimit-Reset header; surface 402 (quota) as an app-level alert to yourself, not to end users.
- Send X-API-Usage-Type: commercial from your proxy once the app monetizes (requires the Plus plan or higher).
### FAQ
Q: Can I call the Calorie API directly from React Native?
A: Technically yes, but you would ship your API key inside the binary. Route requests through a backend proxy so the key stays secret and you can add per-user throttling.
Q: Which barcode scanner library works best?
A: Any library that returns raw UPC/EAN digits works, expo-barcode-scanner and react-native-vision-camera are common choices. The API accepts the digits as-is; dashes are stripped automatically.
---
## Guide: Build a Nutrition App with Next.js (Next.js)
URL: https://calorieapi.com/docs/guides/nextjs-nutrition-app
Updated: 2026-07-03
Next.js route handlers are a natural fit for the Calorie API: the API key lives in server-only environment variables, and fetch caching gives you request deduplication and revalidation for free.
### Server-side search route
app/api/food-search/route.ts:
```
import { NextRequest, NextResponse } from 'next/server'
const API_BASE = 'https://calorieapiadmin.com/api/v1'
export async function GET(req: NextRequest) {
const q = req.nextUrl.searchParams.get('q') ?? ''
const upstream = await fetch(
`${API_BASE}/search/foods?q=${encodeURIComponent(q)}&limit=20`,
{
headers: { 'X-API-Key': process.env.CALORIE_API_KEY! },
// Identical searches within 5 minutes hit the cache, not your quota
next: { revalidate: 300 },
}
)
return NextResponse.json(await upstream.json(), { status: upstream.status })
}
```
### Server components for food pages
For food detail pages, fetch directly inside a server component, no client JavaScript needed and the page is fully rendered for crawlers.
app/foods/[id]/page.tsx:
```
export default async function FoodPage({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
const res = await fetch(`https://calorieapiadmin.com/api/v1/foods/${id}`, {
headers: { 'X-API-Key': process.env.CALORIE_API_KEY! },
next: { revalidate: 3600 },
})
if (!res.ok) notFound()
const food = await res.json()
return (
{food.name}
{food.calories} kcal per 100g
)
}
```
### Caching strategy
- Suggest calls: keep them client → route handler with short (or no) caching; they are user-specific and cheap.
- Food details: revalidate: 3600 or longer, nutrition data for a given ID changes rarely.
- Popular searches: revalidate: 300 turns repeated queries into cache hits instead of quota spend.
### Environment setup
.env.local:
```
# Server-only, never expose with NEXT_PUBLIC_
CALORIE_API_KEY=your_api_key_here
```
### FAQ
Q: Should the API key be a NEXT_PUBLIC_ variable?
A: No, NEXT_PUBLIC_ variables are embedded in client bundles. Use a plain server-side env var and only reference it in route handlers or server components.
Q: Does fetch caching count against my API quota?
A: Cache hits are served by Next.js without contacting the API, so they cost nothing. Only cache misses and revalidations spend quota.
---
## Guide: Barcode Nutrition Scanning in Flutter (Flutter)
URL: https://calorieapi.com/docs/guides/flutter-barcode-scanning
Updated: 2026-07-03
With the mobile_scanner package and the barcode lookup endpoint, a Flutter app can go from camera frame to logged meal in one request. As with any mobile client, route API calls through your backend so the key stays server-side.
### Scanner widget
scan_screen.dart:
```
MobileScanner(
onDetect: (capture) async {
final barcode = capture.barcodes.firstOrNull?.rawValue;
if (barcode == null) return;
final food = await FoodApi.lookupBarcode(barcode);
if (food == null) {
// 404, offer manual search instead
Navigator.pushNamed(context, '/search');
} else {
Navigator.pushNamed(context, '/log', arguments: food);
}
},
)
```
### Typed lookup client
food_api.dart:
```
class FoodApi {
static const _base = 'https://your-backend.example.com/api';
static Future lookupBarcode(String upc) async {
final res = await http.get(Uri.parse('$_base/barcode/$upc'));
if (res.statusCode == 404) return null;
if (res.statusCode != 200) {
throw ApiException(res.statusCode, res.body);
}
return BarcodeFood.fromJson(jsonDecode(res.body));
}
}
class BarcodeFood {
final String name;
final String? brand;
final double? energyKcalPer100g;
BarcodeFood({required this.name, this.brand, this.energyKcalPer100g});
factory BarcodeFood.fromJson(Map json) => BarcodeFood(
name: json['product']['name'],
brand: json['product']['brand'],
energyKcalPer100g:
(json['nutrition_per_100g']?['energy_kcal'] as num?)?.toDouble(),
);
}
```
### Handling nulls and misses
- Label data varies by product, nutrition fields the source lacks are null, not omitted. Model them as nullable.
- A 404 means neither the local catalog nor Open Food Facts knows the code, fall back to text search.
- nutrition_per_serving is only present when the source provides serving data; per-100g values are always your safe base.
### FAQ
Q: Does the API care which scanner package I use?
A: No, it only needs the raw UPC/EAN digits. mobile_scanner is a well-maintained option, but any camera/barcode library that yields the code string works.
Q: Do I need separate handling for local-catalog vs Open Food Facts products?
A: No. The response shape is normalized regardless of source, so one model class covers both.
---
## Guide: Food Search with Node.js (Node.js)
URL: https://calorieapi.com/docs/guides/nodejs-food-search
Updated: 2026-07-03
A thin Node.js client around the search endpoints gives every service in your stack one quota-friendly path to food data. This guide covers the client, pagination, an in-memory cache, and retry behavior that respects the rate-limit headers.
### A minimal typed client
calorieApi.js:
```
const API_BASE = 'https://calorieapiadmin.com/api/v1'
async function apiGet(path, params = {}) {
const url = new URL(API_BASE + path)
for (const [k, v] of Object.entries(params)) {
if (v !== undefined) url.searchParams.set(k, String(v))
}
const res = await fetch(url, {
headers: { 'X-API-Key': process.env.CALORIE_API_KEY },
})
if (res.status === 429) {
const reset = res.headers.get('X-RateLimit-Reset')
throw new RateLimitError(reset)
}
if (!res.ok) throw new ApiError(res.status, await res.text())
return res.json()
}
exports.searchFoods = (q, { limit = 30, skip = 0, verifiedOnly = false } = {}) =>
apiGet('/search/foods', { q, limit, skip, verified_only: verifiedOnly })
exports.getFood = (id) => apiGet(`/foods/${id}`)
exports.lookupBarcode = (upc) => apiGet(`/search/barcode/${upc}`)
```
### Pagination
Search returns a paginated envelope (data, total, skip, limit). Page with skip/limit and stop when skip + data.length reaches total, limit maxes out at 100 per request.
### Cache before you retry
Cached food details:
```
const details = new Map()
async function getFoodCached(id) {
if (details.has(id)) return details.get(id)
const food = await getFood(id)
details.set(id, food) // IDs are stable, cache aggressively
return food
}
```
### Respecting rate limits
- On 429, wait until X-RateLimit-Reset before retrying; add jitter when many workers share the account.
- Treat 402 (monthly quota) as terminal: alert and stop retrying.
- Batch jobs should stay well under the 5% monthly food coverage cap; iterate over your users’ actual foods, not the whole catalog.
### FAQ
Q: Should each microservice get its own API key?
A: Keys share the account’s limits either way, but separate keys per service make dashboards and revocation cleaner. Rate limits apply per account, not per key.
Q: How do I avoid hitting the coverage cap in batch jobs?
A: Only fetch foods your users actually reference and cache by ID. The 5% monthly cap on distinct foods exists to block catalog scraping, not normal batch processing.
---
## Guide: Working with Nutrition Data in Python (Python)
URL: https://calorieapi.com/docs/guides/python-nutrition-data
Updated: 2026-07-03
Python is a common consumer of nutrition data for meal-plan generation, analytics, and ML features. This guide sets up a resilient client with requests, then loads results into pandas for macro analysis.
### A session with retries
client.py:
```
import os
import requests
from requests.adapters import HTTPAdapter, Retry
API_BASE = "https://calorieapiadmin.com/api/v1"
session = requests.Session()
session.headers["X-API-Key"] = os.environ["CALORIE_API_KEY"]
# Retry transient failures; 429 respects Retry-After / reset headers
session.mount(
"https://",
HTTPAdapter(max_retries=Retry(total=3, backoff_factor=1, status_forcelist=[429, 500])),
)
def search_foods(q: str, limit: int = 30, skip: int = 0, verified_only: bool = False):
res = session.get(
f"{API_BASE}/search/foods",
params={"q": q, "limit": limit, "skip": skip, "verified_only": verified_only},
timeout=10,
)
res.raise_for_status()
return res.json()
```
### Paginating a full result set
Iterate pages:
```
def iter_foods(q: str, page_size: int = 100):
skip = 0
while True:
page = search_foods(q, limit=page_size, skip=skip)
yield from page["data"]
skip += page_size
if skip >= page["total"]:
break
```
### Macro analysis with pandas
Analyze verified results:
```
import pandas as pd
rows = list(iter_foods("yogurt"))
df = pd.DataFrame(rows)[["name", "brand", "calories", "protein", "carbs", "fat"]]
# Protein density per 100 kcal, useful for ranking meal-plan candidates
df["protein_per_100kcal"] = df["protein"] / df["calories"] * 100
print(df.sort_values("protein_per_100kcal", ascending=False).head(10))
```
### Quota-aware batch work
- Use verified_only=true for analysis jobs, curated macro data avoids cleaning noisy label entries.
- Persist food details by ID between runs; IDs are stable and re-fetching is pure quota spend.
- Keep batch jobs under the 5% monthly distinct-food coverage cap: analyze the foods your product uses, not the entire catalog.
### FAQ
Q: Is there an official Python SDK?
A: The API is plain REST + JSON, so requests (or httpx) with a session as shown covers everything. The paginated envelope and stable IDs make client code short.
Q: Can I export the whole database for offline analysis?
A: No, bulk export is blocked by the 5% monthly coverage cap. Work against the foods your application actually references, and cache those locally.