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.

Search, open, add up
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.

StatusCodeMeaning
400bad_requestA parameter or the body is not valid. The message says which one.
400unknown_versionThe since version does not exist. Fetch without since.
401unauthorizedNo key, or the key is revoked.
413too_largeA request body over 8 KB.
404not_foundNo such type, item or photo in the published catalog.
429rate_limitedToo many requests for this key this minute.
500server_errorSomething 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.

GET/v1/foods

Search 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
NameInTypeDescription
qquerystringWords to search for. Empty lists popular foods.
kindquerystringgeneric, dish or packaged.
dietquerystringveg, egg, non_veg or vegan. Only foods whose diet is known.
indianquerybooleantrue for Indian foods and brands only.
sourcequerystringusda_foundation, usda_sr, usda_fndds, cnf, cofid, in_pkg or off.
limitqueryinteger1 to 100. Default 20.
offsetqueryintegerFor paging. Default 0.
Request
curl "https://plate.rachnalabs.com/v1/foods?q=moong%20dal&limit=5" \
  -H "X-Plate-Key: $PLATE_KEY"
Response
{
  "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"
      }
    }
  ]
}
GET/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
NameInTypeDescription
id*pathstringA food id from search.
Request
curl "https://plate.rachnalabs.com/v1/foods/usda-2707713" \
  -H "X-Plate-Key: $PLATE_KEY"
Response
{
  "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"
  }
}
GET/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
NameInTypeDescription
code*pathstring6 to 14 digits.
Request
curl "https://plate.rachnalabs.com/v1/foods/barcode/{code}" \
  -H "X-Plate-Key: $PLATE_KEY"
Response
{
  "id": "off-8901719134845",
  "name": "Parle-G Biscuit",
  "brand": "Parle",
  "barcode": "8901719134845",
  "...": "..."
}
POST/v1/nutrition

Nutrition 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
NameInTypeDescription
items*bodyobject[]Each item: food_id, and either grams, or serving (a label from the food's servings) with an optional count.
Request
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}]}'
Response
{
  "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.

GET/v1/catalog/version

Current 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
Request
curl "https://plate.rachnalabs.com/v1/catalog/version" \
  -H "X-Plate-Key: $PLATE_KEY"
Response
{
  "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
  }
}
GET/v1/catalog

The 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
NameInTypeDescription
sincequerystringA catalog version you already have. Only items changed after it are returned, deleted ones as tombstones.
Request
curl "https://plate.rachnalabs.com/v1/catalog?since=2026.10.04" \
  -H "X-Plate-Key: $PLATE_KEY"
Response
{
  "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": []
}
GET/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
NameInTypeDescription
type*pathstringOne of the content types.
sincequerystringA catalog version you already have. Only items changed after it are returned, deleted ones as tombstones.
stagequerystringDishes only. One of period, follicular, ovulation, luteal, everyday, vrat.
mealquerystringDishes only. One of breakfast, lunch, snack, dinner.
preferencequerystringDishes only. One of veg, egg, non_veg, jain, vegan.
regionquerystringDishes only. One of north, south, east, west, any.
avoidquerystringDishes only. Comma separated: dairy, gluten, nuts.
nutrientquerystringDishes only. One of iron, protein, calcium, slowCarbs, omega3, b6, vitaminC.
qquerystringDishes only. Words in the name or detail.
Request
curl "https://plate.rachnalabs.com/v1/dishes?since=2026.10.04&q=ragi" \
  -H "X-Plate-Key: $PLATE_KEY"
Response
{
  "version": "2026.10.04",
  "since": null,
  "items": [
    {
      "id": "ragi-dosa",
      "name": "Ragi dosa",
      "...": "..."
    }
  ]
}
GET/v1/{type}/{id}

One item

One published item by its id.

Key
Required
Caching
ETag, If-None-Match, max-age 300
NameInTypeDescription
type*pathstringThe content type.
id*pathstringThe item's stable id.
Request
curl "https://plate.rachnalabs.com/v1/symptom-cards/cramps" \
  -H "X-Plate-Key: $PLATE_KEY"
Response
{
  "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
}
GET/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
NameInTypeDescription
id*pathstringA photo id from any item's photo_id.
wqueryintegerWidth in pixels, 64 to 2400. Default 900.
formatquerystringwebp (default) or jpeg.
Request
curl "https://plate.rachnalabs.com/v1/photos/photo-moong-dal-chilla?w=600&format=webp"
Response
image/webp
POST/v1/recommendations

A 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
NameInTypeDescription
phase*bodystringmenstrual, follicular, ovulation, luteal, delayed, unknown
local_date*bodystringYYYY-MM-DD, only to rotate dishes by day.
period_daybodyintegerDay of the period, 0 when not on it.
cycle_daybodyinteger0 if unknown.
days_until_periodbodyinteger | nullWhen known.
days_latebodyintegerWhen the period is late.
symptomsbodystring[]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
moodsbodystring[]happy, calm, energetic, tired, sad, anxious, irritated, angry
flowbodystring | nullnone, spotting, light, medium, heavy, veryHeavy
conditionsbodystring[]pcos, endometriosis, thyroid, anemia, pmdd, fibroids, diabetes
preferencebodystringveg, egg, non_veg, jain, vegan
regionbodystringnorth, south, east, west, any
goalsbodystring[]easeCramps, moreEnergy, steadyMood, buildIron, pcosFriendly
avoidbodystring[]dairy, gluten, nuts
catalog_versionbodystringThe version the client has cached, so answers match its photos.
Request
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"}'
Response
{
  "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.

GET/v1/meta

Nutrients 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
Request
curl "https://plate.rachnalabs.com/v1/meta"
Response
{
  "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
    }
  ]
}
GET/v1/health

Is Plate up

For uptime monitors. Open without a key and never cached; it checks that the database answers.

Key
Not needed
Caching
no-store
Request
curl "https://plate.rachnalabs.com/v1/health"
Response
{
  "status": "ok",
  "catalog_version": "2026.10.04",
  "db_ms": 4,
  "time": "2026-10-04T10:00:00.000Z"
}