# 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.