{"openapi":"3.1.0","info":{"title":"developer.c0r.ai — Food Database API","summary":"The food data API with Russia + Arabic GCC coverage.","description":"\n## Overview\n\n**developer.c0r.ai** provides programmatic access to 3M+ food products including:\n\n- 🌍 **OpenFoodFacts** — 2.99M products, global coverage\n- 🇺🇸 **USDA FoodData Central** — 462K products, 65+ micronutrients\n- 🕌 **GCC Arabic foods** — Halal flags, Arabic names, UAE/Saudi/Qatar markets\n- 🇷🇺 **Russian foods** — Cyrillic search, Russian brands, ChestnyZNAK barcodes\n- 🥗 **Curated database** — AI-enriched, nutritionist-verified dishes\n\n## Authentication\n\nAll endpoints require an API key. Pass it as:\n\n```\nAuthorization: Bearer fdb_your_api_key_here\n```\n\nOr via header:\n\n```\nX-API-Key: fdb_your_api_key_here\n```\n\nGet your free API key at **[developer.c0r.ai](https://developer.c0r.ai)**\n\n## Plans\n\n| Plan | Price | Requests/day | Features |\n|------|-------|-------------|---------|\n| Developer | Free | 1,000 | Search + Barcode |\n| Starter | $29/mo | 50,000 | + Nutrients data |\n| Growth | $99/mo | 500,000 | + AI Analysis + Arabic/Russian |\n| Business | $299/mo | Unlimited | + SLA 99.9% + Support |\n\n## Rate Limits\n\nRate limit status is returned in every response header:\n- `X-RateLimit-Limit` — your daily limit\n- `X-RateLimit-Remaining` — requests remaining today\n- `X-RateLimit-Reset` — UTC timestamp when counter resets (midnight)\n\n## Data License\n\n- OpenFoodFacts data: [ODbL 1.0](https://opendatacommons.org/licenses/odbl/)\n- USDA data: Public domain (CC0)\n- Curated/AI-enriched data: © developer.c0r.ai, all rights reserved\n","contact":{"name":"developer.c0r.ai Support","url":"https://developer.c0r.ai/","email":"evgeniy@erarta.ai"},"license":{"name":"Commercial — see developer.c0r.ai/terms","url":"https://developer.c0r.ai/terms"},"version":"1.0.0"},"servers":[{"url":"https://api.c0r.ai","description":"Production"},{"url":"http://localhost:8024","description":"Local development"}],"paths":{"/v1/auth/signup":{"post":{"tags":["Auth"],"summary":"Create a new developer account","operationId":"signup_v1_auth_signup_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResponse"}}}},"401":{"description":"Invalid or missing API key"},"429":{"description":"Daily rate limit exceeded"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/request-otp":{"post":{"tags":["Auth"],"summary":"Request a 6-digit OTP via email","description":"Generates a 6-digit code, stores it in Redis (TTL 5 min), and sends it via Resend to the provided email. Rate limited to 3 OTPs per email per hour.","operationId":"request_otp_v1_auth_request_otp_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OtpRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OtpRequestResponse"}}}},"401":{"description":"Invalid or missing API key"},"429":{"description":"Daily rate limit exceeded"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/verify-otp":{"post":{"tags":["Auth"],"summary":"Verify OTP and exchange for JWT (auto-creates user if first time)","description":"Verifies the 6-digit code against the one issued by /request-otp. On success: if the user does not exist yet, creates them with a passwordless account and a fresh `developer` API key, then returns JWT + (on first signup) the API key in the same response.","operationId":"verify_otp_endpoint_v1_auth_verify_otp_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OtpVerifyRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResponse"}}}},"401":{"description":"Invalid or missing API key"},"429":{"description":"Daily rate limit exceeded"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/login":{"post":{"tags":["Auth"],"summary":"Login to your account","operationId":"login_v1_auth_login_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResponse"}}}},"401":{"description":"Invalid or missing API key"},"429":{"description":"Daily rate limit exceeded"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/dashboard/keys":{"get":{"tags":["Dashboard"],"summary":"List your API keys","operationId":"list_keys_v1_dashboard_keys_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ApiKeyInfo"},"type":"array","title":"Response List Keys V1 Dashboard Keys Get"}}}},"401":{"description":"Invalid or missing API key"},"429":{"description":"Daily rate limit exceeded"},"500":{"description":"Internal server error"}}}},"/v1/dashboard/keys/rotate":{"post":{"tags":["Dashboard"],"summary":"Rotate your API key (returns new key once)","operationId":"rotate_key_v1_dashboard_keys_rotate_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResponse"}}}},"401":{"description":"Invalid or missing API key"},"429":{"description":"Daily rate limit exceeded"},"500":{"description":"Internal server error"}}}},"/v1/dashboard/usage":{"get":{"tags":["Dashboard"],"summary":"Get usage statistics","operationId":"get_usage_v1_dashboard_usage_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageStats"}}}},"401":{"description":"Invalid or missing API key"},"429":{"description":"Daily rate limit exceeded"},"500":{"description":"Internal server error"}}}},"/v1/dashboard/history":{"get":{"tags":["Dashboard"],"summary":"Get recent API request history","operationId":"get_history_v1_dashboard_history_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/HistoryEntry"},"title":"Response Get History V1 Dashboard History Get"}}}},"401":{"description":"Invalid or missing API key"},"429":{"description":"Daily rate limit exceeded"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/dashboard/checkout":{"post":{"tags":["Dashboard"],"summary":"Create a Stripe checkout session for plan upgrade","operationId":"create_checkout_v1_dashboard_checkout_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutResponse"}}}},"401":{"description":"Invalid or missing API key"},"429":{"description":"Daily rate limit exceeded"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/health":{"get":{"tags":["System"],"summary":"Health check","description":"Comprehensive health check: DB + Redis connectivity, uptime, build metadata.","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"description":"Invalid or missing API key"},"429":{"description":"Daily rate limit exceeded"},"500":{"description":"Internal server error"}}}},"/v1/plans":{"get":{"tags":["Account"],"summary":"Get available plans and pricing","description":"Returns all available subscription plans with pricing and feature details. No auth required.","operationId":"get_plans_v1_plans_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"description":"Invalid or missing API key"},"429":{"description":"Daily rate limit exceeded"},"500":{"description":"Internal server error"}}}},"/v1/search":{"get":{"tags":["Food Search"],"summary":"Search food products by name","description":"Search across 3M+ products by name in English, Russian, or Arabic.\n\n**Ranking** combines:\n- Trigram-based `word_similarity` score (typo-tolerant, threshold 0.4)\n- Prefix-match bonus (+0.30 to the score)\n- Curated/verified products promoted to the top tier\n- Products with complete nutrition data before incomplete\n\n**Typo tolerance**: queries like `chiken`, `salmn`, `banaana` will find\n`chicken`, `salmon`, `banana` via PostgreSQL `pg_trgm` fuzzy matching.\n\n**Stop-word handling**: queries that are common English stop-words\n(`and`, `the`, `for`, `our`, `is`…) OR shorter than 2 characters\nshort-circuit immediately and return an empty result set.\n\n**Response headers**:\n- `X-Search-Mode: blended` — query was answered using lexical + semantic\n  HNSW vector search blended scoring\n- `X-Search-Mode: lexical-fallback` — semantic embedder unavailable\n  (OpenAI/OpenRouter), only lexical scoring was used (typo + prefix still work)\n\n**Regional search**: `region` parameter filters by country of origin\nusing the OFF `countries_tags` field. Supported: `russia`, `gcc` (UAE\n+ Saudi + Qatar + Kuwait + Bahrain + Oman), `us`, `eu`.\n\n**Database filter**: `sources` restricts the search to specific underlying\ndatabases (`curated`, `usda`, `off`, comma-separated). Only the listed\ndatabases are queried. Each result carries `data_source` with the database\nit came from.","operationId":"search_v1_search_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":200,"description":"Search query — supports typos via trigram fuzzy","title":"Q"},"description":"Search query — supports typos via trigram fuzzy","example":"chiken breast"},{"name":"lang","in":"query","required":false,"schema":{"type":"string","pattern":"^(en|ru|ar)$","description":"Query language: 'en' (default), 'ru' (Cyrillic), or 'ar' (Arabic)","default":"en","title":"Lang"},"description":"Query language: 'en' (default), 'ru' (Cyrillic), or 'ar' (Arabic)"},{"name":"region","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(russia|gcc|us|eu|global)$"},{"type":"null"}],"description":"Filter by country of origin: 'russia', 'gcc', 'us', 'eu', or omit for global","title":"Region"},"description":"Filter by country of origin: 'russia', 'gcc', 'us', 'eu', or omit for global"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"description":"Number of results to return (1-50). Default 10.","default":10,"title":"Limit"},"description":"Number of results to return (1-50). Default 10."},{"name":"sources","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"Restrict the lookup to specific underlying databases: comma-separated list of `curated`, `usda`, `off` (e.g. `usda` or `off,curated`). Tables that are not listed are not queried at all. Omit for all databases (default). Unknown or empty values return 422.","title":"Sources"},"description":"Restrict the lookup to specific underlying databases: comma-separated list of `curated`, `usda`, `off` (e.g. `usda` or `off,curated`). Tables that are not listed are not queried at all. Omit for all databases (default). Unknown or empty values return 422.","example":"usda"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"401":{"description":"Invalid or missing API key"},"429":{"description":"Daily rate limit exceeded"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/barcode/{barcode}":{"get":{"tags":["Food Search"],"summary":"Look up product by barcode","description":"Look up a product by EAN-13, UPC-A, or USDA FDC ID.\n\nSupports barcodes from all major markets including:\n- Russian ChestnyZNAK barcodes (460/469 prefix)\n- GCC/Arabic market barcodes (628 prefix)\n- European EAN-13\n- US UPC-A","operationId":"barcode_lookup_v1_barcode__barcode__get","parameters":[{"name":"barcode","in":"path","required":true,"schema":{"type":"string","title":"Barcode"}},{"name":"sources","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"Restrict the lookup to specific underlying databases: comma-separated list of `curated`, `usda`, `off` (e.g. `usda` or `off,curated`). Tables that are not listed are not queried at all. Omit for all databases (default). Unknown or empty values return 422.","title":"Sources"},"description":"Restrict the lookup to specific underlying databases: comma-separated list of `curated`, `usda`, `off` (e.g. `usda` or `off,curated`). Tables that are not listed are not queried at all. Omit for all databases (default). Unknown or empty values return 422.","example":"usda"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BarcodeResponse"}}}},"401":{"description":"Invalid or missing API key"},"429":{"description":"Daily rate limit exceeded"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/semantic-search":{"get":{"tags":["Food Search"],"summary":"Semantic similarity search (vector embeddings)","description":"Search by semantic meaning rather than keyword matching.\n\nUses OpenAI `text-embedding-3-small` to embed the query and finds the most\nsimilar products using pgvector cosine similarity.\n\n**Use cases:**\n- Cross-language search: \"chicken\" finds \"курица\", \"دجاج\"\n- Fuzzy matching: \"fried rice\" finds \"stir-fry with rice\"\n- Photo AI integration: AI names → semantic DB lookup\n\n**Requires:** Growth or Business plan + embeddings must be generated\n(run `22_generate_embeddings.py` first).\n\nFalls back to empty results if embeddings are not yet available.","operationId":"semantic_search_endpoint_v1_semantic_search_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":2,"maxLength":200,"description":"Natural language query","title":"Q"},"description":"Natural language query","example":"grilled chicken breast"},{"name":"region","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(russia|gcc|us|eu|global)$"},{"type":"null"}],"description":"Filter by region","title":"Region"},"description":"Filter by region"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"description":"Results per page","default":10,"title":"Limit"},"description":"Results per page"},{"name":"sources","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"Restrict the lookup to specific underlying databases: comma-separated list of `curated`, `usda`, `off` (e.g. `usda` or `off,curated`). Tables that are not listed are not queried at all. Omit for all databases (default). Unknown or empty values return 422.","title":"Sources"},"description":"Restrict the lookup to specific underlying databases: comma-separated list of `curated`, `usda`, `off` (e.g. `usda` or `off,curated`). Tables that are not listed are not queried at all. Omit for all databases (default). Unknown or empty values return 422.","example":"usda"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"401":{"description":"Invalid or missing API key"},"429":{"description":"Daily rate limit exceeded"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/product/{product_id}":{"get":{"tags":["Food Search"],"summary":"Get product by ID","description":"Retrieve a specific product by its ID (from search or barcode results).","operationId":"get_product_v1_product__product_id__get","parameters":[{"name":"product_id","in":"path","required":true,"schema":{"type":"string","title":"Product Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FoodProduct"}}}},"401":{"description":"Invalid or missing API key"},"429":{"description":"Daily rate limit exceeded"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/analyze":{"post":{"tags":["AI Analysis"],"summary":"Analyze dish or meal by text description","description":"**Premium plan required (Growth or Business).**\n\nAnalyzes a text description of a meal and returns:\n- Per-ingredient breakdown with quantities\n- Total nutritional values\n- Per-100g normalized values\n- Confidence score\n\nSupports English, Russian, and Arabic descriptions.\n\n**Examples:**\n- `\"200g grilled salmon with 150g rice and salad\"`\n- `\"200г лосося на гриле с рисом и овощами\"`\n- `\"دجاج مشوي مع أرز وخضار\"`","operationId":"analyze_dish_v1_analyze_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyzeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyzeResponse"}}}},"401":{"description":"Invalid or missing API key"},"429":{"description":"Daily rate limit exceeded"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me":{"get":{"tags":["Account"],"summary":"Get current API key plan and usage","operationId":"get_plan_info_v1_me_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanInfo"}}}},"401":{"description":"Invalid or missing API key"},"429":{"description":"Daily rate limit exceeded"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"AnalyzeIngredient":{"properties":{"name":{"type":"string","title":"Name"},"quantity_g":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Quantity G"},"calories":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Calories"},"proteins":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Proteins"},"fats":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Fats"},"carbs":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Carbs"}},"type":"object","required":["name","quantity_g","calories","proteins","fats","carbs"],"title":"AnalyzeIngredient"},"AnalyzeRequest":{"properties":{"text":{"type":"string","maxLength":500,"minLength":2,"title":"Text","description":"Dish or food description to analyze","examples":["200g grilled salmon with rice and vegetables"]},"lang":{"type":"string","pattern":"^(en|ru|ar)$","title":"Lang","description":"Language of the text: en | ru | ar","default":"en"}},"type":"object","required":["text"],"title":"AnalyzeRequest"},"AnalyzeResponse":{"properties":{"dish_name":{"type":"string","title":"Dish Name"},"total_weight_g":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Total Weight G"},"per_100g":{"$ref":"#/components/schemas/Nutrition"},"total":{"$ref":"#/components/schemas/Nutrition"},"ingredients":{"items":{"$ref":"#/components/schemas/AnalyzeIngredient"},"type":"array","title":"Ingredients"},"confidence":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Confidence"}},"type":"object","required":["dish_name","total_weight_g","per_100g","total","ingredients","confidence"],"title":"AnalyzeResponse"},"ApiKeyInfo":{"properties":{"id":{"type":"string","title":"Id"},"key_prefix":{"type":"string","title":"Key Prefix"},"plan":{"type":"string","title":"Plan"},"requests_today":{"type":"integer","title":"Requests Today"},"created_at":{"type":"string","title":"Created At"},"is_active":{"type":"boolean","title":"Is Active"}},"type":"object","required":["id","key_prefix","plan","requests_today","created_at","is_active"],"title":"ApiKeyInfo"},"AuthResponse":{"properties":{"token":{"type":"string","title":"Token","description":"JWT token (expires in 7 days)"},"user":{"additionalProperties":true,"type":"object","title":"User","description":"User info (id, email, name)"},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","description":"API key (only on signup)"}},"type":"object","required":["token","user"],"title":"AuthResponse"},"BarcodeResponse":{"properties":{"product":{"anyOf":[{"$ref":"#/components/schemas/FoodProduct"},{"type":"null"}]},"found":{"type":"boolean","title":"Found"},"barcode":{"type":"string","title":"Barcode"}},"type":"object","required":["product","found","barcode"],"title":"BarcodeResponse"},"CheckoutRequest":{"properties":{"plan":{"type":"string","pattern":"^(starter|growth|business)$","title":"Plan","description":"Target plan"}},"type":"object","required":["plan"],"title":"CheckoutRequest"},"CheckoutResponse":{"properties":{"checkout_url":{"type":"string","title":"Checkout Url"}},"type":"object","required":["checkout_url"],"title":"CheckoutResponse"},"FoodProduct":{"properties":{"id":{"type":"string","title":"Id","description":"Unique product ID"},"barcode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Barcode","description":"EAN/UPC barcode"},"name":{"type":"string","title":"Name","description":"Product name (English)"},"name_ru":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name Ru","description":"Product name (Russian)"},"name_ar":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name Ar","description":"Product name (Arabic)"},"brands":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brands","description":"Brand name(s)"},"categories":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Categories","description":"Product categories"},"serving_g":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Serving G","description":"Typical serving size, grams"},"is_halal":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Halal","description":"Halal status (GCC products)"},"is_vegan":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Vegan","description":"Vegan status"},"nutrition":{"$ref":"#/components/schemas/Nutrition","description":"Nutritional values per 100g"},"food_group":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Food Group","description":"Food category (e.g. Meat & Poultry, Dairy, Beverages)"},"image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image Url","description":"Product photo URL"},"source":{"type":"string","title":"Source","description":"Data tier: curated | database"},"data_source":{"anyOf":[{"type":"string","enum":["curated","usda","off"]},{"type":"null"}],"title":"Data Source","description":"Underlying database the row comes from: curated (c0r curated foods), usda (USDA FoodData Central), off (Open Food Facts / branded products). Matches the id prefix c_ / u_ / p_ and the values accepted by `sources=`."},"verified":{"type":"boolean","title":"Verified","description":"Manually verified by nutritionist","default":false},"enriched_by_ai":{"type":"boolean","title":"Enriched By Ai","description":"Macros filled by AI enrichment","default":false}},"type":"object","required":["id","name","nutrition","source"],"title":"FoodProduct"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HistoryEntry":{"properties":{"endpoint":{"type":"string","title":"Endpoint"},"query":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Query"},"ip":{"type":"string","title":"Ip"},"created_at":{"type":"string","title":"Created At"}},"type":"object","required":["endpoint","query","ip","created_at"],"title":"HistoryEntry"},"LoginRequest":{"properties":{"email":{"type":"string","title":"Email","description":"Email address"},"password":{"type":"string","title":"Password","description":"Password"}},"type":"object","required":["email","password"],"title":"LoginRequest"},"Nutrition":{"properties":{"calories":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Calories","description":"Energy, kcal per 100g"},"proteins":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Proteins","description":"Proteins, g per 100g"},"fats":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Fats","description":"Fats, g per 100g"},"carbs":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Carbs","description":"Carbohydrates, g per 100g"},"fiber":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Fiber","description":"Dietary fiber, g per 100g"},"sugar":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Sugar","description":"Sugars, g per 100g"},"sodium":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Sodium","description":"Sodium, g per 100g"},"nutrients":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Nutrients","description":"Extended micronutrients (premium plan only)"}},"type":"object","title":"Nutrition"},"OtpRequest":{"properties":{"email":{"type":"string","maxLength":254,"minLength":5,"title":"Email","description":"Email address"}},"type":"object","required":["email"],"title":"OtpRequest","description":"F2.4 — request a 6-digit OTP via Resend email."},"OtpRequestResponse":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"expires_in":{"type":"integer","title":"Expires In","description":"OTP TTL in seconds (5 min)","default":300}},"type":"object","title":"OtpRequestResponse"},"OtpVerifyRequest":{"properties":{"email":{"type":"string","maxLength":254,"minLength":5,"title":"Email"},"code":{"type":"string","maxLength":6,"minLength":6,"pattern":"^[0-9]{6}$","title":"Code"}},"type":"object","required":["email","code"],"title":"OtpVerifyRequest","description":"F2.4 — verify OTP and exchange for JWT (auto-creates user if not exists)."},"PlanInfo":{"properties":{"plan":{"type":"string","title":"Plan"},"requests_today":{"type":"integer","title":"Requests Today"},"requests_limit":{"type":"integer","title":"Requests Limit"},"requests_remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Requests Remaining"},"regions":{"items":{"type":"string"},"type":"array","title":"Regions"},"features":{"items":{"type":"string"},"type":"array","title":"Features"}},"type":"object","required":["plan","requests_today","requests_limit","requests_remaining","regions","features"],"title":"PlanInfo"},"SearchResponse":{"properties":{"results":{"items":{"$ref":"#/components/schemas/FoodProduct"},"type":"array","title":"Results"},"total":{"type":"integer","title":"Total","description":"Total matching products"},"query":{"type":"string","title":"Query"},"lang":{"type":"string","title":"Lang"},"region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Region"},"plan":{"type":"string","title":"Plan","description":"Your API plan"}},"type":"object","required":["results","total","query","lang","region","plan"],"title":"SearchResponse"},"SignupRequest":{"properties":{"email":{"type":"string","maxLength":254,"minLength":5,"title":"Email","description":"Email address"},"password":{"type":"string","maxLength":128,"minLength":8,"title":"Password","description":"Password (min 8 chars)"},"name":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Name","description":"Display name"}},"type":"object","required":["email","password"],"title":"SignupRequest"},"UsageStats":{"properties":{"today":{"type":"integer","title":"Today"},"last_7_days":{"type":"integer","title":"Last 7 Days"},"hourly":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Hourly"}},"type":"object","required":["today","last_7_days","hourly"],"title":"UsageStats"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}}}