Plate API v1
API reference
Read-mostly JSON over HTTPS. Fetch the catalog once, keep it on the device, and check the version to pick up changes.
Getting started
Plate gives your app nearly 30,000 foods with nutrition per 100 g, Indian portions like katori and roti, and a calculator for meals. The base URL is https://plate.rachnalabs.com/v1. Each app gets its own keys, for sandbox and for production, from the Plate team. Please write to hello@rachnalabs.com to ask for one.
A food logging screen usually needs three calls: search, the food, and the nutrition for what was eaten.
curl "https://plate.rachnalabs.com/v1/foods?q=roti&limit=5" -H "X-Plate-Key: $PLATE_KEY"
curl https://plate.rachnalabs.com/v1/foods/usda-2707713 -H "X-Plate-Key: $PLATE_KEY"
curl -X POST https://plate.rachnalabs.com/v1/nutrition -H "X-Plate-Key: $PLATE_KEY" \
-H "Content-Type: application/json" \
-d '{"items":[{"food_id":"usda-2707713","grams":80}]}'Every food tells you its source and licence. When you show Plate data, please show that attribution too (see Sources and licences).
Authentication
Every request carries an app key, except the API map, health, meta, the OpenAPI document and photos. Keys belong to a client app and an environment (sandbox or production). They never identify a person.
Send it as X-Plate-Key: <key>, or as Authorization: Bearer <key>. Keep production keys out of public repositories.
Versions and caching
Every release is a catalog version such as 2026.10.04. Pass the version you have as since and Plate returns only what changed. A removed item comes back as a tombstone, {"id": "...", "deleted": true}, so you can drop it from your copy.
Responses carry an ETag. Send it back as If-None-Match and Plate answers 304 with no body when nothing changed.
Rate limits
Each key may make 600 requests a minute. Over that, the API answers 429 with a Retry-After header. Check /v1/catalog/version first and fetch the catalog only when the version changed.
Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (seconds until the minute resets).
Errors
Errors use standard HTTP status codes and a JSON body: {"code": "...", "message": "..."}. The message is plain English and safe to log.
| Status | Code | Meaning |
|---|---|---|
| 400 | bad_request | A parameter or the body is not valid. The message says which one. |
| 400 | unknown_version | The since version does not exist. Fetch without since. |
| 401 | unauthorized | No key, or the key is revoked. |
| 413 | too_large | A request body over 8 KB. |
| 404 | not_found | No such type, item or photo in the published catalog. |
| 429 | rate_limited | Too many requests for this key this minute. |
| 500 | server_error | Something went wrong on Plate's side. Retry with backoff. |
What Plate promises
- Nutrients are per 100 g of edible food (or 100 ml). Values come only from the named source; a nutrient a source does not report is left out, never guessed.
- Every food names its source, licence and attribution. If you show Plate data, please show that attribution too, and keep Open Food Facts data under its ODbL licence.
- Read-mostly JSON over HTTPS, UTF-8. Dates as YYYY-MM-DD, timestamps in ISO 8601 UTC.
- Versioned by URL (/v1). A breaking change gets a new version and at least six months' notice.
- Every item has a stable id that never changes, so apps can remember choices across updates.
- Only published content is served. Drafts and items in review are never visible.
- Every health claim links to a published source. A claim with no source is not shown.
- Recommendations take anonymous fields only. Requests are not stored or logged.
Endpoints
Foods and nutrition
The food database, portions and nutrition. Most apps need only these.
/v1/foodsSearch foods
Search nearly 30,000 foods by name, in English or Hindi words (roti, dahi, bhindi, chana). Typo tolerant. Everyday Indian food ranks first. Each result has energy, protein, carbs, fat and fibre per 100 g, and its portions.
- Key
- Required
- Caching
- ETag, max-age 300
| Name | In | Type | Description |
|---|---|---|---|
| q | query | string | Words to search for. Empty lists popular foods. |
| kind | query | string | generic, dish or packaged. |
| diet | query | string | veg, egg, non_veg or vegan. Only foods whose diet is known. |
| indian | query | boolean | true for Indian foods and brands only. |
| source | query | string | usda_foundation, usda_sr, usda_fndds, cnf, cofid, in_pkg or off. |
| limit | query | integer | 1 to 100. Default 20. |
| offset | query | integer | For paging. Default 0. |
curl "https://plate.rachnalabs.com/v1/foods?q=moong%20dal&limit=5" \ -H "X-Plate-Key: $PLATE_KEY"
{
"query": "roti",
"total": 54,
"limit": 20,
"offset": 0,
"items": [
{
"id": "usda-2707713",
"name": "Bread, chappatti or roti",
"brand": null,
"kind": "dish",
"basis": "100g",
"nutrients": {
"energy_kcal": 299,
"protein_g": 7.85,
"carb_g": 46.1,
"fat_g": 9.2,
"fibre_g": 9.7
},
"servings": [
{
"label": "1 medium chappatti or roti (7\")",
"grams": 40
}
],
"source": {
"id": "usda_fndds",
"name": "USDA FoodData Central, FNDDS (Survey Foods)",
"licence": "CC0 1.0"
}
}
]
}/v1/foods/{id}One food, every nutrient
Up to 30 nutrients per 100 g (or 100 ml), its portions including Indian katori and spoon measures, and where the data comes from with its licence and attribution.
- Key
- Required
- Caching
- ETag, max-age 3600
| Name | In | Type | Description |
|---|---|---|---|
| id* | path | string | A food id from search. |
curl "https://plate.rachnalabs.com/v1/foods/usda-2707713" \ -H "X-Plate-Key: $PLATE_KEY"
{
"id": "usda-2707713",
"name": "Bread, chappatti or roti",
"basis": "100g",
"nutrients": {
"energy_kcal": 299,
"protein_g": 7.85,
"carb_g": 46.1,
"fat_g": 9.2,
"fibre_g": 9.7,
"iron_mg": 2.2,
"calcium_mg": 36
},
"servings": [
{
"label": "1 large chappatti or roti (8\")",
"grams": 52
},
{
"label": "1 medium chappatti or roti (7\")",
"grams": 40
}
],
"source": {
"id": "usda_fndds",
"licence": "CC0 1.0",
"attribution": "U.S. Department of Agriculture, Agricultural Research Service. FoodData Central.",
"source_id": "2707713",
"quality": "calculated from recipe"
}
}/v1/foods/barcode/{code}A packaged food by barcode
Look up a packaged food by its EAN or UPC barcode, for scanning. Coverage of Indian products grows as Open Food Facts grows.
- Key
- Required
- Caching
- ETag, max-age 3600
| Name | In | Type | Description |
|---|---|---|---|
| code* | path | string | 6 to 14 digits. |
curl "https://plate.rachnalabs.com/v1/foods/barcode/{code}" \
-H "X-Plate-Key: $PLATE_KEY"{
"id": "off-8901719134845",
"name": "Parle-G Biscuit",
"brand": "Parle",
"barcode": "8901719134845",
"...": "..."
}/v1/nutritionNutrition for portions
Send up to 100 foods with grams, or a serving label and a count, and get the nutrition for each and the total. Use it to show a meal or a day. Nothing is stored.
- Key
- Required
- Caching
- no-store
| Name | In | Type | Description |
|---|---|---|---|
| items* | body | object[] | Each item: food_id, and either grams, or serving (a label from the food's servings) with an optional count. |
curl -X POST "https://plate.rachnalabs.com/v1/nutrition" \
-H "X-Plate-Key: $PLATE_KEY" \
-H "Content-Type: application/json" \
-d '{"items":[{"food_id":"usda-2707713","serving":"1 medium chappatti or roti (7\")","count":2},{"food_id":"usda-2708264","grams":150}]}'{
"items": [
{
"food_id": "usda-2707713",
"name": "Bread, chappatti or roti",
"grams": 80,
"nutrients": {
"energy_kcal": 239.2,
"protein_g": 6.28
}
}
],
"total": {
"energy_kcal": 456.7,
"protein_g": 19.2
},
"missing": {}
}Endpoints
Cycle guidance
Food suggestions by cycle phase and logged symptoms, with symptom cards, stage plans and their sources.
/v1/catalog/versionCurrent catalog version
The latest published version, its notes and what changed. Cheap and cached: call it before anything else.
- Key
- Required
- Caching
- ETag, max-age 60
curl "https://plate.rachnalabs.com/v1/catalog/version" \ -H "X-Plate-Key: $PLATE_KEY"
{
"version": "2026.10.04",
"published_at": "2026-10-04T08:08:16.528Z",
"previous_version": null,
"notes": "First catalog.",
"changes": [
{
"type": "dishes",
"id": "moong-dal-chilla",
"op": "upsert"
}
],
"counts": {
"dishes": 110,
"recipes": 110
}
}/v1/catalogThe whole catalog, or what changed
Every published item grouped by type. With since, only what changed after that version, deleted items as tombstones.
- Key
- Required
- Caching
- ETag, If-None-Match, max-age 300
| Name | In | Type | Description |
|---|---|---|---|
| since | query | string | A catalog version you already have. Only items changed after it are returned, deleted ones as tombstones. |
curl "https://plate.rachnalabs.com/v1/catalog?since=2026.10.04" \ -H "X-Plate-Key: $PLATE_KEY"
{
"version": "2026.10.04.2",
"since": "2026.10.04",
"full": false,
"dishes": [
{
"id": "moong-dal-chilla",
"name": "Moong dal chilla",
"...": "..."
},
{
"id": "boiled-egg",
"deleted": true
}
],
"recipes": []
}/v1/{type}All items of one type
One type of content: dishes, recipes, symptom-cards, stages, nutrients, ingredients, tips, conditions, myths, sources, photos. Supports since. Dishes also take search filters.
- Key
- Required
- Caching
- ETag, If-None-Match, max-age 300
| Name | In | Type | Description |
|---|---|---|---|
| type* | path | string | One of the content types. |
| since | query | string | A catalog version you already have. Only items changed after it are returned, deleted ones as tombstones. |
| stage | query | string | Dishes only. One of period, follicular, ovulation, luteal, everyday, vrat. |
| meal | query | string | Dishes only. One of breakfast, lunch, snack, dinner. |
| preference | query | string | Dishes only. One of veg, egg, non_veg, jain, vegan. |
| region | query | string | Dishes only. One of north, south, east, west, any. |
| avoid | query | string | Dishes only. Comma separated: dairy, gluten, nuts. |
| nutrient | query | string | Dishes only. One of iron, protein, calcium, slowCarbs, omega3, b6, vitaminC. |
| q | query | string | Dishes only. Words in the name or detail. |
curl "https://plate.rachnalabs.com/v1/dishes?since=2026.10.04&q=ragi" \ -H "X-Plate-Key: $PLATE_KEY"
{
"version": "2026.10.04",
"since": null,
"items": [
{
"id": "ragi-dosa",
"name": "Ragi dosa",
"...": "..."
}
]
}/v1/{type}/{id}One item
One published item by its id.
- Key
- Required
- Caching
- ETag, If-None-Match, max-age 300
| Name | In | Type | Description |
|---|---|---|---|
| type* | path | string | The content type. |
| id* | path | string | The item's stable id. |
curl "https://plate.rachnalabs.com/v1/symptom-cards/cramps" \ -H "X-Plate-Key: $PLATE_KEY"
{
"id": "cramps",
"title": "Cramps",
"line": "Warm, gentle food and ginger",
"dish_ids": [
"adrak-nimbu-pani",
"saunth-wala-doodh"
],
"source_ids": [
"ginger",
"omega-3"
],
"food_can_help": true
}/v1/photos/{id}A photo, resized
Square food photos, resized on request and never enlarged. Public, so they load straight into an image view. WebP or JPEG.
- Key
- Not needed
- Caching
- max-age one year, immutable
| Name | In | Type | Description |
|---|---|---|---|
| id* | path | string | A photo id from any item's photo_id. |
| w | query | integer | Width in pixels, 64 to 2400. Default 900. |
| format | query | string | webp (default) or jpeg. |
curl "https://plate.rachnalabs.com/v1/photos/photo-moong-dal-chilla?w=600&format=webp"
image/webp
/v1/recommendationsA day's food for an anonymous context
Send where someone is in their cycle, what they logged and how they eat; get the day's cards, meals or picks, and key nutrients. Only the fields below are accepted. Anything else, like a name or an id, is refused. Plate does not store the request.
- Key
- Required
- Caching
- no-store
| Name | In | Type | Description |
|---|---|---|---|
| phase* | body | string | menstrual, follicular, ovulation, luteal, delayed, unknown |
| local_date* | body | string | YYYY-MM-DD, only to rotate dishes by day. |
| period_day | body | integer | Day of the period, 0 when not on it. |
| cycle_day | body | integer | 0 if unknown. |
| days_until_period | body | integer | null | When known. |
| days_late | body | integer | When the period is late. |
| symptoms | body | string[] | cramps, backPain, pelvisPain, breastTenderness, headache, migraine, severePain, bloating, nausea, vomiting, diarrhea, constipation, fatigue, dizziness, fever, chills, waterRetention, acne, moodSwings, irritability, anxiety, sadnessLowMood, brainFog, cryingSpells, lowMotivation, insomnia, restlessSleep, unusualDischargeSmell, vaginalItching |
| moods | body | string[] | happy, calm, energetic, tired, sad, anxious, irritated, angry |
| flow | body | string | null | none, spotting, light, medium, heavy, veryHeavy |
| conditions | body | string[] | pcos, endometriosis, thyroid, anemia, pmdd, fibroids, diabetes |
| preference | body | string | veg, egg, non_veg, jain, vegan |
| region | body | string | north, south, east, west, any |
| goals | body | string[] | easeCramps, moreEnergy, steadyMood, buildIron, pcosFriendly |
| avoid | body | string[] | dairy, gluten, nuts |
| catalog_version | body | string | The version the client has cached, so answers match its photos. |
curl -X POST "https://plate.rachnalabs.com/v1/recommendations" \
-H "X-Plate-Key: $PLATE_KEY" \
-H "Content-Type: application/json" \
-d '{"phase":"luteal","cycle_day":24,"symptoms":["bloating"],"moods":["tired"],"preference":"veg","region":"north","local_date":"2026-10-04"}'{
"stage": "luteal",
"logged": [
{
"card_id": "bloating",
"because": [
"bloating"
],
"dish_ids": [
"saunf-pani",
"lauki-chana-dal"
]
}
],
"food": [
{
"dish_id": "moong-dal-and-ragi-roti",
"brings": [
"luteal-calcium",
"luteal-slow-carbs"
]
}
],
"key_nutrients": [
"luteal-calcium",
"luteal-b6",
"luteal-slow-carbs"
],
"notes": [],
"catalog_version": "2026.10.04"
}Endpoints
System
Health and reference data.
/v1/metaNutrients and data sources
The nutrient keys with names and units, and every data source with its licence and how many foods it gives. Open without a key.
- Key
- Not needed
- Caching
- max-age 3600
curl "https://plate.rachnalabs.com/v1/meta"
{
"basis": "Nutrients are per 100 g of edible food, or per 100 ml where a food's basis says 100ml.",
"nutrients": [
{
"key": "energy_kcal",
"name": "Energy",
"unit": "kcal",
"group": "energy"
}
],
"sources": [
{
"id": "usda_sr",
"name": "USDA FoodData Central, SR Legacy",
"licence": "CC0 1.0",
"foods": 7793
}
]
}/v1/healthIs Plate up
For uptime monitors. Open without a key and never cached; it checks that the database answers.
- Key
- Not needed
- Caching
- no-store
curl "https://plate.rachnalabs.com/v1/health"
{
"status": "ok",
"catalog_version": "2026.10.04",
"db_ms": 4,
"time": "2026-10-04T10:00:00.000Z"
}