CORTEX api developer reference

Getting started

Introduction

What the API does and the typical workflow.

The CORTEX api by Fitshield lets your application search our food database, calculate the full nutrition of a cooked or raw dish, send dishes for certified-nutritionist verification, track their status, download nutrition fact sheets, and raise support requests.

It is a JSON-over-HTTPS API. All request and response bodies are application/json (UTF-8), unless a binary file is being downloaded.

Typical workflow

  1. 1Search for each ingredient and get its food_code.
  2. 2Calculate the dish, sending ingredients + cooking info, and receive a dish_id and the nutrition result.
  3. 3Send to nutritionist for verification.
  4. 4Check status until it is approved or rejected.
  5. 5Get the fact sheet once the dish is approved.

Base URL & versioning

Where requests go.

All endpoints are relative to the versioned base URL:

http
https://api.fitshield.in/v1

Example: the Search endpoint is GET /foods/search, i.e. GET https://api.fitshield.in/v1/foods/search.

Proposed. The host name is a placeholder. Replace it with the URLs issued for your test and live environments.

Authentication

Bearer API keys for test and live.

The API is a paid service. Each account is issued API keys from the developer dashboard: a test key (sk_test_…) and a live key (sk_live_…). Send the key as a Bearer token on every request:

http
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx
  • Keep keys secret; use them server-side only.
  • Requests with a missing or invalid key return 401 Unauthorized.
  • Test-key requests operate on sandbox data and are not billed.

Conventions

Response envelope, IDs, units and timestamps.

Response envelope

Every JSON response uses a consistent envelope. On success:

json
{
  "success": true,
  "data": {}
}

On failure:

json
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Human-readable explanation.",
    "details": [
      {
        "field": "ingredients",
        "issue": "required"
      }
    ]
  }
}

General

  • IDs are opaque strings (e.g. dsh_8f3a2b). Do not parse them.
  • Timestamps are ISO 8601 UTC (e.g. 2026-09-09T10:22:31Z).
  • Quantities use units g, ml, or piece.
  • Nutrient values are returned as a nutrition-label object with { value, unit } leaves.
  • All requests should send Accept: application/json.

Error handling

HTTP status plus a precise error code.

The HTTP status code gives the broad outcome; error.code gives the precise reason.

CodeHTTPMeaning
INVALID_REQUEST400Malformed request, bad JSON or missing/incorrect parameters.
MISSING_API_KEY / INVALID_API_KEY401No key, or an invalid/revoked key.
FORBIDDEN403Key valid but not permitted for this action.
RESOURCE_NOT_FOUND404The dish or food does not exist.
ALREADY_SUBMITTED409Dish is already under verification.
NOT_APPROVED409Fact sheet requested before approval.
VALIDATION_ERROR422A field failed validation.
RATE_LIMITED429Too many requests / quota exceeded, see Retry-After.
INTERNAL_ERROR500Server error. Safe to retry later.

Example error

http
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "The dish must contain at least one ingredient.",
    "details": [
      {
        "field": "ingredients",
        "issue": "required"
      }
    ]
  }
}

Rate limits & quota

Per-key limits and the headers to watch.

Requests are limited per API key according to your plan. When exceeded, the API returns 429 Too Many Requests with a Retry-After header (seconds). Every response includes:

  • X-RateLimit-Limit, requests allowed per window
  • X-RateLimit-Remaining, requests left in the current window

Proposed. Default suggestion: 60 requests/minute per key, with a monthly quota by plan. Confirm final limits.

Endpoints

Calculate dish nutrition

Compute nutrition, claims & allergens.

POST/dishes/calculate

Calculates the nutrition of a dish from its ingredients and quantities. Ingredients may be identified by food_code (from Search) or by a plain name.

Cooking is optional and can be given at two independent levels:

  • Per ingredient: an ingredient carries its own cooking object, applied to just that ingredient.
  • Whole dish: a top-level cooking object, applied to the combined dish.

If an ingredient has its own cooking and a dish-level cooking is also provided, both are applied in sequence: the ingredient is cooked first, then the whole-dish cooking is applied on top (cooking is applied twice). Omit cooking entirely for a raw dish.

The result is saved and returned with a dish_id used by all later endpoints.

Request body

FieldTypeRequiredDescription
dish_namestringYesName of the dish.
cookingobjectNoWhole-dish cooking, applied to the combined dish. Omit for no dish-level cooking.
cooking.methodstringConditionalRequired inside any cooking object. raw, boil, steam, fry, deep_fry, saute, simmer, bake, roast, grill, pressure_cook.
cooking.time_minutesnumberNoCooking time in minutes.
cooking.temperature_cnumberNoCooking temperature in °C.
ingredientsarrayYesOne or more ingredients (at least one).
ingredients[].food_codestringConditionalPreferred identifier (from Search). Provide this or name.
ingredients[].namestringConditionalIngredient name, used if food_code is absent.
ingredients[].quantitynumberYesAmount of the ingredient.
ingredients[].unitstringYesg, ml, or piece.
ingredients[].cookingobjectNoCooking applied to just this ingredient. Same shape as the top-level cooking.

Example request (whole-dish cooking)

bash
curl -X POST "https://api.fitshield.in/v1/dishes/calculate" \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
  "dish_name": "Paneer Butter Masala",
  "cooking": {
    "method": "simmer",
    "time_minutes": 20,
    "temperature_c": 150
  },
  "ingredients": [
    {
      "food_code": "F1024",
      "quantity": 250,
      "unit": "g"
    },
    {
      "name": "Butter",
      "quantity": 30,
      "unit": "g"
    },
    {
      "name": "Tomato puree",
      "quantity": 150,
      "unit": "ml"
    }
  ]
}'

Example request (per-ingredient cooking + whole-dish cooking)

Here each ingredient is cooked on its own, then the whole dish is simmered, so the fried ingredient is effectively cooked twice.

bash
curl -X POST "https://api.fitshield.in/v1/dishes/calculate" \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
  "dish_name": "Mixed Veg Sabzi",
  "cooking": {
    "method": "simmer",
    "time_minutes": 8
  },
  "ingredients": [
    {
      "food_code": "F3001",
      "quantity": 100,
      "unit": "g",
      "cooking": {
        "method": "fry",
        "time_minutes": 5,
        "temperature_c": 180
      }
    },
    {
      "food_code": "F3002",
      "quantity": 120,
      "unit": "g",
      "cooking": {
        "method": "boil",
        "time_minutes": 10
      }
    }
  ]
}'

Example response (201 Created)

json
{
  "success": true,
  "data": {
    "dish_id": "dsh_8f3a2b",
    "dish_name": "Paneer Butter Masala",
    "total_weight_g": 430,
    "nutrition": {
      "basis": "per_dish",
      "energy": {
        "value": 892,
        "unit": "kcal"
      },
      "protein": {
        "value": 28.6,
        "unit": "g"
      },
      "carbohydrate": {
        "total": {
          "value": 22.1,
          "unit": "g"
        },
        "added_sugars": {
          "value": 3,
          "unit": "g"
        }
      },
      "fat": {
        "total": {
          "value": 71.2,
          "unit": "g"
        },
        "saturated": {
          "value": 36,
          "unit": "g"
        },
        "trans": {
          "value": 0.4,
          "unit": "g"
        }
      },
      "sodium": {
        "value": 410,
        "unit": "mg"
      }
    },
    "government_claims": [
      "High in Protein",
      "Source of Calcium"
    ],
    "allergen_claims": {
      "contains": [
        "Milk"
      ],
      "may_contain": [
        "Tree nuts"
      ]
    },
    "verification_status": "not_submitted",
    "created_at": "2026-09-09T10:22:31Z"
  }
}

Response fields

FieldTypeDescription
dish_idstringUse this in all later endpoints.
total_weight_gnumberTotal finished weight of the dish.
nutritionobjectNutrition-label panel. See the Nutrition model.
nutrition.basisstringWhat the values represent: per_dish (default) or per_100g.
government_claimsstring[]Regulatory nutrition/health claims the dish qualifies for (e.g. FSSAI). Empty if none.
allergen_claimsobjectAllergen declarations: contains and may_contain lists.
verification_statusstringnot_submitted until sent to a nutritionist.

Edit dish

Update a dish and recalculate.

PUT/dishes/{dish_id}

Update a previously calculated dish (change its name, cooking details, or ingredients) and recalculate its nutrition. Send the full updated dish definition (same body as Calculate); it replaces the stored definition.

Editing recalculates nutrition, government claims and allergen claims. If the dish was already submitted or approved, editing resets verification_status to not_submitted and invalidates any existing fact sheet. You must send it for verification again.

Path parameters

FieldTypeRequiredDescription
dish_idstringYesThe dish to edit.

Request body

Identical to POST /dishes/calculate. Send the full, updated dish_name, optional cooking, and ingredients (each with optional per-ingredient cooking).

Example request

bash
curl -X PUT "https://api.fitshield.in/v1/dishes/dsh_8f3a2b" \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
  "dish_name": "Paneer Butter Masala (less oil)",
  "cooking": {
    "method": "simmer",
    "time_minutes": 18,
    "temperature_c": 150
  },
  "ingredients": [
    {
      "food_code": "F1024",
      "quantity": 250,
      "unit": "g"
    },
    {
      "name": "Butter",
      "quantity": 15,
      "unit": "g"
    },
    {
      "name": "Tomato puree",
      "quantity": 150,
      "unit": "ml"
    }
  ]
}'

Example response (200 OK)

Returns the same object as Calculate, with recalculated nutrition, government_claims, allergen_claims, and verification_status reset to not_submitted.

json
{
  "success": true,
  "data": {
    "dish_id": "dsh_8f3a2b",
    "dish_name": "Paneer Butter Masala (less oil)",
    "total_weight_g": 415,
    "nutrition": {
      "basis": "per_dish",
      "energy": {
        "value": 804,
        "unit": "kcal"
      },
      "protein": {
        "value": 28.4,
        "unit": "g"
      },
      "carbohydrate": {
        "total": {
          "value": 21.6,
          "unit": "g"
        },
        "added_sugars": {
          "value": 3,
          "unit": "g"
        }
      },
      "fat": {
        "total": {
          "value": 60.1,
          "unit": "g"
        },
        "saturated": {
          "value": 30.2,
          "unit": "g"
        },
        "trans": {
          "value": 0.3,
          "unit": "g"
        }
      },
      "sodium": {
        "value": 395,
        "unit": "mg"
      }
    },
    "government_claims": [
      "High in Protein"
    ],
    "allergen_claims": {
      "contains": [
        "Milk"
      ],
      "may_contain": [
        "Tree nuts"
      ]
    },
    "verification_status": "not_submitted",
    "updated_at": "2026-09-09T12:05:00Z"
  }
}

Send to nutritionist

Submit a dish for verification.

POST/dishes/{dish_id}/send-to-nutritionist

Submits a calculated dish for verification by a certified nutritionist. Verification is asynchronous, so poll the status endpoint. The dish_id comes from the Calculate response.

Path parameters

FieldTypeRequiredDescription
dish_idstringYesThe dish to submit.

Request body (optional)

FieldTypeRequiredDescription
notestringNoA message for the nutritionist.

Example request

bash
curl -X POST "https://api.fitshield.in/v1/dishes/dsh_8f3a2b/send-to-nutritionist" \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
  "note": "Please confirm the oil quantity."
}'

Example response (202 Accepted)

json
{
  "success": true,
  "data": {
    "dish_id": "dsh_8f3a2b",
    "verification_status": "pending",
    "submitted_at": "2026-09-09T10:25:00Z"
  }
}

A dish already under review returns 409 with code ALREADY_SUBMITTED.

Check verification status

Status of a single dish.

GET/dishes/{dish_id}/verification-status

Returns the current verification status of a dish previously sent for review.

Path parameters

FieldTypeRequiredDescription
dish_idstringYesThe dish whose status you want.

Example request

bash
curl -X GET "https://api.fitshield.in/v1/dishes/dsh_8f3a2b/verification-status" \
  -H "Authorization: Bearer sk_live_xxx"

Example response (200 OK)

json
{
  "success": true,
  "data": {
    "dish_id": "dsh_8f3a2b",
    "verification_status": "approved",
    "remarks": "Values verified against standard recipe.",
    "reviewed_by": "Dr. A. Sharma",
    "updated_at": "2026-09-09T11:40:00Z"
  }
}

verification_status values

ValueDescription
not_submittedCalculated but not yet sent for verification.
pendingSubmitted, awaiting review.
approvedVerified. The fact sheet can now be downloaded.
rejectedNot approved; see remarks.

View all dishes & status

List all dishes with status (filter + paginate).

GET/dishes

Lists all dishes created under your account, each with its current verification status. Supports filtering and pagination.

Query parameters

FieldTypeRequiredDescription
statusstringNoFilter by not_submitted, pending, approved, or rejected.
qstringNoFilter by dish name (partial match).
pageintegerNoPage number, starting at 1. Default 1.
limitintegerNoResults per page. Default 20, maximum 100.

Example request

bash
curl -X GET "https://api.fitshield.in/v1/dishes?status=pending&page=1&limit=20" \
  -H "Authorization: Bearer sk_live_xxx"

Example response (200 OK)

json
{
  "success": true,
  "data": {
    "dishes": [
      {
        "dish_id": "dsh_8f3a2b",
        "dish_name": "Paneer Butter Masala",
        "verification_status": "pending",
        "created_at": "2026-09-09T10:22:31Z",
        "updated_at": "2026-09-09T10:25:00Z"
      },
      {
        "dish_id": "dsh_44c1d0",
        "dish_name": "Mixed Veg Sabzi",
        "verification_status": "approved",
        "created_at": "2026-09-07T09:10:00Z",
        "updated_at": "2026-09-07T14:02:00Z"
      }
    ],
    "pagination": {
      "page": 1,
      "limit": 20,
      "total": 2,
      "total_pages": 1
    }
  }
}

Response fields

FieldTypeDescription
dishes[].dish_idstringDish identifier.
dishes[].dish_namestringDish name.
dishes[].verification_statusstringnot_submitted, pending, approved, or rejected.
dishes[].created_atstringWhen the dish was first calculated.
dishes[].updated_atstringWhen the dish or its status last changed.
paginationobjectpage, limit, total, total_pages.

Get fact sheet

Download the fact sheet (after approval).

GET/dishes/{dish_id}/fact-sheet

Downloads the nutrition fact sheet for a dish. Available only after the dish is approved. By default returns a JSON object with a time-limited download link; pass format=pdf to stream the PDF directly.

Path & query parameters

FieldTypeRequiredDescription
dish_idstringYesThe approved dish.
formatstringNojson (default) returns a link; pdf streams the file.

Example request

bash
curl -X GET "https://api.fitshield.in/v1/dishes/dsh_8f3a2b/fact-sheet" \
  -H "Authorization: Bearer sk_live_xxx"

Example response (200 OK, format=json)

json
{
  "success": true,
  "data": {
    "dish_id": "dsh_8f3a2b",
    "format": "pdf",
    "download_url": "https://cdn.fitshield.in/factsheets/dsh_8f3a2b.pdf?token=...",
    "expires_at": "2026-09-09T12:40:00Z"
  }
}

If the dish is not yet approved, the API returns 409 with code NOT_APPROVED. With format=pdf the response body is the file (Content-Type: application/pdf, Content-Disposition: attachment).

Support request

Send a query / request; replied to by email.

POST/support-requests

Send a request to the Fitshield team. Use it to ask a nutritionist a question about a dish, request that a new packaged food be added to the database, or raise any other query. Our team resolves the request and replies to the email you provide.

  • Nutritionist question about a dish: put the dish id and topic in subject, your question in body, and (optionally) the dish_id.
  • Request a new packaged food: describe the product in subject and body.
  • Anything else: use subject and body freely.

Request body

FieldTypeRequiredDescription
emailstringYesReply-to email address. We respond here once the request is resolved.
subjectstringYesShort summary of the request.
bodystringYesThe full message / details of the request.
dish_idstringNoLink the request to a specific dish (for nutritionist questions).
categorystringNonutritionist_query, new_food_request, or other. Helps route the request.

Example request

bash
curl -X POST "https://api.fitshield.in/v1/support-requests" \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "dev@example.com",
  "subject": "Question on dish dsh_8f3a2b (vitamin C retention)",
  "body": "Could a nutritionist confirm the vitamin C retention after cooking for this dish?",
  "dish_id": "dsh_8f3a2b",
  "category": "nutritionist_query"
}'

Example response (201 Created)

json
{
  "success": true,
  "data": {
    "request_id": "req_9x2k",
    "status": "received",
    "email": "dev@example.com",
    "created_at": "2026-09-09T09:00:00Z"
  }
}

The team will reply to the email you provided once the request is resolved. A missing email, subject, or body returns 422 VALIDATION_ERROR.

Reference

Data models

Ingredient, Cooking, Nutrition, claims.

Ingredient (request)

FieldTypeRequiredDescription
food_codestringConditionalFood code from Search; preferred identifier.
namestringConditionalUsed if food_code is not supplied.
quantitynumberYesAmount of the ingredient.
unitstringYesg, ml, or piece.
cookingobjectNoCooking applied to just this ingredient (optional).

Cooking (request)

Used both at the top level (whole dish) and inside an ingredient. If both are present for an ingredient, they apply in sequence.

FieldTypeRequiredDescription
methodstringYesRequired within a cooking object. raw, boil, steam, fry, deep_fry, saute, simmer, bake, roast, grill, pressure_cook.
time_minutesnumberNoCooking time in minutes.
temperature_cnumberNoCooking temperature in °C.

Nutrition (response)

A nutrition-label panel. Each leaf is { "value": number, "unit": string }.

FieldUnitDescription
basisper_dish (default) or per_100g.
energykcalEnergy.
proteingProtein.
carbohydrate.totalgTotal carbohydrate.
carbohydrate.added_sugarsgAdded sugars.
fat.totalgTotal fat.
fat.saturatedgSaturated fat.
fat.transgTrans fat.
sodiummgSodium.

Government claims (response)

string[], regulatory claims automatically evaluated from the nutrition values (e.g. High in Protein, Low Fat, Source of Calcium). Empty when the dish qualifies for none.

Allergen claims (response)

FieldTypeDescription
containsstring[]Allergens definitely present (e.g. Milk, Wheat, Soy).
may_containstring[]Possible cross-contamination allergens.

Endpoint summary

All eight endpoints at a glance.

EndpointMethod & pathPurpose
Search foodsGET /foods/searchAutocomplete foods from the database.
Calculate dish nutritionPOST /dishes/calculateCompute nutrition, claims & allergens.
Edit dishPUT /dishes/{dish_id}Update a dish and recalculate.
Send to nutritionistPOST /dishes/{dish_id}/send-to-nutritionistSubmit a dish for verification.
Check verification statusGET /dishes/{dish_id}/verification-statusStatus of a single dish.
View all dishes & statusGET /dishesList all dishes with status (filter + paginate).
Get fact sheetGET /dishes/{dish_id}/fact-sheetDownload the fact sheet (after approval).
Support requestPOST /support-requestsSend a query / request; replied to by email.

Need API keys or have a question?

The CORTEX api is a paid service. Talk to our team for access, pricing and integration help, or write to hello@fitshield.in.

Request API access

© 2026 Fitshield Dietfood Private Limited · CORTEX api v1.0. Developer documentation. Base URL, authentication and rate limits shown are proposed and subject to change.