Food logs
Create and query an end user's food diary. A food log's id can be used to fetch, update, or delete that entry.
Each operation acts on one end user (End-user identity). Client-token scopes: food_logs:write to create, update, or delete; food_logs:read to list, summarize, or get.
Timestamps and ranges
created_atis when the meal was eaten (Days and timezones). Omit it on create to log the meal now, or on update to leave it unchanged.Log a food by sending the food
idand servingidfrom a search or food-analysis result asfood_idandserving_id. Pass them back exactly as returned (Identifiers).An update (
PATCH) replaces any subset offoods,created_at, andname, and must set at least one of them.The list and the summary take inclusive local calendar dates in an IANA
timezone. The list spans at most 60 days and the summary at most 366, grouped by day or by week; a longer range fails with400 date_range_too_large.Creating a log is not idempotent: check with the list or get operation before you retry a create that timed out. Deleting is idempotent, and an unknown or already-deleted log also returns
204.
Each operation's description states what it costs — see Credits and pricing.
API key or client token.
Free up to your plan's free-call limit, then 1 credit per 100 calls.
Creates a food log from food + serving ids (from search or food-analysis results). The response echoes the log hydrated with full nutrition — save its id to update or delete the log, or fetch it again with GET /v1.2/food-logs/{log_id}. Not idempotent: verify with the list or get endpoint before retrying a timed-out create.
Callable with a client token carrying the food_logs:write scope.
Your credential, sent as Authorization: Bearer <credential>. Two kinds reach this API, and the prefix tells them apart.
API key (sk-…) — created in the Developer Dashboard, it authenticates your whole account. Use it only where you control the machine — never inside a mobile app or a browser. Every endpoint accepts it.
Client token (ct-…) — a short-lived credential bound to one of your end users, minted from your API key by POST /v1.2/auth/client-tokens on your backend and relayed to the device. Safe to hold on a phone: it expires within 2 hours and acts only as the user it was minted for. Endpoints that open with API key or client token accept one and name the scope it must carry; endpoints that open with API key only refuse it.
Your stable ID for the end user whose logs this request reads or writes. Opaque to January — use the same ID your system already uses for them.
| Credential | Header | Result |
|---|---|---|
API key (sk-…) | absent | 400 end_user_id_required |
API key (sk-…) | present | the request acts on that end user |
Client token (ct-…) | absent | filled in from the token |
Client token (ct-…) | the end user the token is bound to | accepted |
Client token (ct-…) | any other end user | 403 end_user_id_mismatch |
When the meal was eaten — any ISO-8601 offset; stored and returned in UTC with milliseconds. Omitted = now.
2024-09-13T11:34:56ZBreakfastThe created log, hydrated with resolved food and nutrition; its id is in the Location header.
A field is missing or malformed; the message names it.
The request carried no Authorization header, or the credential in it was rejected.
unauthorized — the header is missing or malformed, or the key is not one we recognise. A valid key belonging to the other API version is 403 forbidden instead.
A client token is rejected in one of three ways, and only the first should be handled automatically:
token_expired— the token is past its TTL. Mint a fresh one from your backend and retry the request once. This is routine and expected once per TTL window.token_invalid— no such token: it was never issued, or it has been purged, which happens shortly after it expires. This code is not an automatic token-refresh signal.token_revoked— the token was revoked, byPOST /v1.2/auth/client-token-revocationsor from the dashboard. The end user signs in again in your app, and your backend decides whether to mint another; a device that just re-mints defeats the revocation.
The credential is valid but is not allowed to make this request. forbidden is the general case — a key issued for the other API version, for example. A client token adds client_token_not_allowed, meaning the endpoint takes only an sk- API key — its description opens with API key only. (in this API: POST /v1.2/auth/client-tokens, POST /v1.2/auth/client-token-revocations, POST /v1.2/chat/completions, POST /v1.2/literature/search, POST /v1.2/extractions, GET /v1.2/extractions/{job_id}, GET /v1.2/patients/{patient_id}/snapshot, and GET /v1.2/credits).
On an endpoint that opens with API key or client token., scope_insufficient means the token was minted without the scope named at the end of that endpoint’s description, and end_user_id_mismatch means the January-End-User-ID header disagrees with the end user the token is bound to — omit it, or send exactly that id.
Two failures share this status; use code to choose the response.
rate_limited— a rolling abuse window was reached: the partner-wide v1.2 minute cap, a configured short-window limit, or a client-token limit. Back off and retry;Retry-Aftercarries the wait when Redis reports it.credit_limit_exceeded— this call costs more credits than you have left for the billing period, or they are spent. Never returned to an Enterprise account, whose usage past the allowance is billed as overage instead.
Credits reset at the end of your billing period; GET /v1.2/credits reports that instant as resets_at and remains available when either limit refuses other operations.
Any other error: the HTTP status plus { code, message }. Retry only rate_limited and the transient 5xx codes.
POST /v1.2/food-logs HTTP/1.1
Host: partners.january.ai
Authorization: Bearer YOUR_SECRET_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 129
{
"foods": [
{
"food_id": "101963552",
"serving_id": "68051535",
"quantity": 1.4
}
],
"created_at": "2024-09-13T11:34:56Z",
"name": "Breakfast"
}{
"id": "78129823-8ba2-4183-b13b-71f0e963c606",
"foods": [
{
"food_id": "101963552",
"name": "Greek Yogurt, Plain, Whole Milk",
"brand_name": null,
"image_url": null,
"glycemic_index": 11.3,
"glycemic_load": 1.4,
"nutrients": {
"calories": {
"value": 300,
"unit": "g"
},
"protein": {
"value": 300,
"unit": "g"
},
"carbohydrates": {
"value": 300,
"unit": "g"
},
"net_carbohydrates": {
"value": 300,
"unit": "g"
},
"total_fat": {
"value": 300,
"unit": "g"
},
"trans_fat": {
"value": 300,
"unit": "g"
},
"saturated_fat": {
"value": 300,
"unit": "g"
},
"fiber": {
"value": 300,
"unit": "g"
},
"total_sugars": {
"value": 300,
"unit": "g"
},
"added_sugars": {
"value": 300,
"unit": "g"
},
"cholesterol": {
"value": 300,
"unit": "g"
},
"calcium": {
"value": 300,
"unit": "g"
},
"iron": {
"value": 300,
"unit": "g"
},
"potassium": {
"value": 300,
"unit": "g"
},
"sodium": {
"value": 300,
"unit": "g"
},
"vitamin_d": {
"value": 300,
"unit": "g"
}
},
"quantity": 1.4,
"serving": {
"id": "34237662",
"unit": "cup",
"weight_grams": 60,
"quantity": 1
}
}
],
"created_at": "2024-09-13T11:34:56.000Z",
"name": "Breakfast"
}API key or client token.
Free up to your plan's free-call limit, then 1 credit per 100 calls.
Returns the logs between start_date and end_date (both inclusive local calendar dates in timezone; the range spans at most 60 days), ordered by timestamp. An empty list is a valid result.
Callable with a client token carrying the food_logs:read scope.
Your credential, sent as Authorization: Bearer <credential>. Two kinds reach this API, and the prefix tells them apart.
API key (sk-…) — created in the Developer Dashboard, it authenticates your whole account. Use it only where you control the machine — never inside a mobile app or a browser. Every endpoint accepts it.
Client token (ct-…) — a short-lived credential bound to one of your end users, minted from your API key by POST /v1.2/auth/client-tokens on your backend and relayed to the device. Safe to hold on a phone: it expires within 2 hours and acts only as the user it was minted for. Endpoints that open with API key or client token accept one and name the scope it must carry; endpoints that open with API key only refuse it.
First local calendar date in timezone, inclusive.
2024-09-01Last local calendar date in timezone, inclusive. May equal start_date for a single day. The inclusive range may not exceed 60 calendar days.
2024-09-08IANA timezone that defines the local calendar days this range covers. Required: without it the days would be cut in UTC, which silently shifts anything logged near midnight into the wrong day for anyone not on UTC.
America/Los_AngelesYour stable ID for the end user whose logs this request reads or writes. Opaque to January — use the same ID your system already uses for them.
| Credential | Header | Result |
|---|---|---|
API key (sk-…) | absent | 400 end_user_id_required |
API key (sk-…) | present | the request acts on that end user |
Client token (ct-…) | absent | filled in from the token |
Client token (ct-…) | the end user the token is bound to | accepted |
Client token (ct-…) | any other end user | 403 end_user_id_mismatch |
The logs in the requested date range, ordered by timestamp; an empty array is a valid result.
A date is missing, malformed, or the range is inverted or exceeds 60 days; or timezone is missing or not a valid IANA name.
The request carried no Authorization header, or the credential in it was rejected.
unauthorized — the header is missing or malformed, or the key is not one we recognise. A valid key belonging to the other API version is 403 forbidden instead.
A client token is rejected in one of three ways, and only the first should be handled automatically:
token_expired— the token is past its TTL. Mint a fresh one from your backend and retry the request once. This is routine and expected once per TTL window.token_invalid— no such token: it was never issued, or it has been purged, which happens shortly after it expires. This code is not an automatic token-refresh signal.token_revoked— the token was revoked, byPOST /v1.2/auth/client-token-revocationsor from the dashboard. The end user signs in again in your app, and your backend decides whether to mint another; a device that just re-mints defeats the revocation.
The credential is valid but is not allowed to make this request. forbidden is the general case — a key issued for the other API version, for example. A client token adds client_token_not_allowed, meaning the endpoint takes only an sk- API key — its description opens with API key only. (in this API: POST /v1.2/auth/client-tokens, POST /v1.2/auth/client-token-revocations, POST /v1.2/chat/completions, POST /v1.2/literature/search, POST /v1.2/extractions, GET /v1.2/extractions/{job_id}, GET /v1.2/patients/{patient_id}/snapshot, and GET /v1.2/credits).
On an endpoint that opens with API key or client token., scope_insufficient means the token was minted without the scope named at the end of that endpoint’s description, and end_user_id_mismatch means the January-End-User-ID header disagrees with the end user the token is bound to — omit it, or send exactly that id.
A log in or next to the range references a food or serving that is no longer available, so the list cannot be returned. The message names the log and, when known, the unavailable foods, and says how to fix it.
Two failures share this status; use code to choose the response.
rate_limited— a rolling abuse window was reached: the partner-wide v1.2 minute cap, a configured short-window limit, or a client-token limit. Back off and retry;Retry-Aftercarries the wait when Redis reports it.credit_limit_exceeded— this call costs more credits than you have left for the billing period, or they are spent. Never returned to an Enterprise account, whose usage past the allowance is billed as overage instead.
Credits reset at the end of your billing period; GET /v1.2/credits reports that instant as resets_at and remains available when either limit refuses other operations.
Any other error: the HTTP status plus { code, message }. Retry only rate_limited and the transient 5xx codes.
GET /v1.2/food-logs?start_date=2024-09-01&end_date=2024-09-08&timezone=America%2FLos_Angeles HTTP/1.1
Host: partners.january.ai
Authorization: Bearer YOUR_SECRET_TOKEN
Accept: */*
{
"items": [
{
"id": "78129823-8ba2-4183-b13b-71f0e963c606",
"foods": [
{
"food_id": "101963552",
"name": "Greek Yogurt, Plain, Whole Milk",
"brand_name": null,
"image_url": null,
"glycemic_index": 11.3,
"glycemic_load": 1.4,
"nutrients": {
"calories": {
"value": 300,
"unit": "g"
},
"protein": {
"value": 300,
"unit": "g"
},
"carbohydrates": {
"value": 300,
"unit": "g"
},
"net_carbohydrates": {
"value": 300,
"unit": "g"
},
"total_fat": {
"value": 300,
"unit": "g"
},
"trans_fat": {
"value": 300,
"unit": "g"
},
"saturated_fat": {
"value": 300,
"unit": "g"
},
"fiber": {
"value": 300,
"unit": "g"
},
"total_sugars": {
"value": 300,
"unit": "g"
},
"added_sugars": {
"value": 300,
"unit": "g"
},
"cholesterol": {
"value": 300,
"unit": "g"
},
"calcium": {
"value": 300,
"unit": "g"
},
"iron": {
"value": 300,
"unit": "g"
},
"potassium": {
"value": 300,
"unit": "g"
},
"sodium": {
"value": 300,
"unit": "g"
},
"vitamin_d": {
"value": 300,
"unit": "g"
}
},
"quantity": 1.4,
"serving": {
"id": "34237662",
"unit": "cup",
"weight_grams": 60,
"quantity": 1
}
}
],
"created_at": "2024-09-13T11:34:56.000Z",
"name": "Breakfast"
}
]
}API key or client token.
Aggregates the logs between start_date and end_date (both inclusive local calendar dates in timezone) into per-day or per-week buckets, each with summed nutrients, plus totals for the range and an average per logged day. The range spans at most 366 days — wide enough for a year at a time; the 366-day cap bounds the number of buckets. The buckets tile the whole range in chronological order: a day or week with no logs is still returned, with zero counts, and under group_by=week the first and last buckets are clipped to the dates you asked for. nutrients is sparse, so read logs_count to tell a bucket with no logs from one whose logs could not be resolved.
Callable with a client token carrying the food_logs:read scope.
Your credential, sent as Authorization: Bearer <credential>. Two kinds reach this API, and the prefix tells them apart.
API key (sk-…) — created in the Developer Dashboard, it authenticates your whole account. Use it only where you control the machine — never inside a mobile app or a browser. Every endpoint accepts it.
Client token (ct-…) — a short-lived credential bound to one of your end users, minted from your API key by POST /v1.2/auth/client-tokens on your backend and relayed to the device. Safe to hold on a phone: it expires within 2 hours and acts only as the user it was minted for. Endpoints that open with API key or client token accept one and name the scope it must carry; endpoints that open with API key only refuse it.
First local calendar date in timezone, inclusive.
2024-09-01Last local calendar date in timezone, inclusive. May equal start_date for a single day. The inclusive range may not exceed 366 calendar days.
2024-09-30IANA timezone that defines the local calendar days this range covers. Required: without it the days would be cut in UTC, which silently shifts anything logged near midnight into the wrong day for anyone not on UTC.
America/Los_AngelesBucket size. day is one bucket per local calendar date; week is one per week, with the first and last clipped to the range.
dayPossible values: Which weekday a week bucket begins on. Ignored when group_by=day, where the response reports week_start: null.
mondayPossible values: Your stable ID for the end user whose logs this request reads or writes. Opaque to January — use the same ID your system already uses for them.
| Credential | Header | Result |
|---|---|---|
API key (sk-…) | absent | 400 end_user_id_required |
API key (sk-…) | present | the request acts on that end user |
Client token (ct-…) | absent | filled in from the token |
Client token (ct-…) | the end user the token is bound to | accepted |
Client token (ct-…) | any other end user | 403 end_user_id_mismatch |
The aggregated summary. A range with no logs is a valid result: buckets are still returned, with zero counts.
The bucket size used, echoing the request.
weekPossible values: The weekday week buckets begin on. Always present; null when group_by=day, where it does not apply.
mondayPossible values: The IANA timezone the buckets were cut in — the canonical spelling of what was requested.
America/Los_AngelesFirst local calendar date of the summarized range, echoing the request.
2024-09-01Last local calendar date of the summarized range, inclusive.
2024-09-30A date is missing, malformed, or the range is inverted or exceeds 366 days; timezone is missing or not a valid IANA name; or group_by/week_start is not one of the documented values.
The request carried no Authorization header, or the credential in it was rejected.
unauthorized — the header is missing or malformed, or the key is not one we recognise. A valid key belonging to the other API version is 403 forbidden instead.
A client token is rejected in one of three ways, and only the first should be handled automatically:
token_expired— the token is past its TTL. Mint a fresh one from your backend and retry the request once. This is routine and expected once per TTL window.token_invalid— no such token: it was never issued, or it has been purged, which happens shortly after it expires. This code is not an automatic token-refresh signal.token_revoked— the token was revoked, byPOST /v1.2/auth/client-token-revocationsor from the dashboard. The end user signs in again in your app, and your backend decides whether to mint another; a device that just re-mints defeats the revocation.
The credential is valid but is not allowed to make this request. forbidden is the general case — a key issued for the other API version, for example. A client token adds client_token_not_allowed, meaning the endpoint takes only an sk- API key — its description opens with API key only. (in this API: POST /v1.2/auth/client-tokens, POST /v1.2/auth/client-token-revocations, POST /v1.2/chat/completions, POST /v1.2/literature/search, POST /v1.2/extractions, GET /v1.2/extractions/{job_id}, GET /v1.2/patients/{patient_id}/snapshot, and GET /v1.2/credits).
On an endpoint that opens with API key or client token., scope_insufficient means the token was minted without the scope named at the end of that endpoint’s description, and end_user_id_mismatch means the January-End-User-ID header disagrees with the end user the token is bound to — omit it, or send exactly that id.
Two failures share this status; use code to choose the response.
rate_limited— a rolling abuse window was reached: the partner-wide v1.2 minute cap, a configured short-window limit, or a client-token limit. Back off and retry;Retry-Aftercarries the wait when Redis reports it.credit_limit_exceeded— this call costs more credits than you have left for the billing period, or they are spent. Never returned to an Enterprise account, whose usage past the allowance is billed as overage instead.
Credits reset at the end of your billing period; GET /v1.2/credits reports that instant as resets_at and remains available when either limit refuses other operations.
Any other error: the HTTP status plus { code, message }. Retry only rate_limited and the transient 5xx codes.
GET /v1.2/food-logs/summary?start_date=2024-09-01&end_date=2024-09-30&timezone=America%2FLos_Angeles HTTP/1.1
Host: partners.january.ai
Authorization: Bearer YOUR_SECRET_TOKEN
Accept: */*
{
"group_by": "week",
"week_start": "monday",
"timezone": "America/Los_Angeles",
"start_date": "2024-09-01",
"end_date": "2024-09-30",
"buckets": [
{
"start_date": "2024-09-01",
"end_date": "2024-09-07",
"logs_count": 5,
"days_with_logs": 3,
"nutrients": {
"calories": {
"value": 300,
"unit": "g"
},
"protein": {
"value": 300,
"unit": "g"
},
"carbohydrates": {
"value": 300,
"unit": "g"
},
"net_carbohydrates": {
"value": 300,
"unit": "g"
},
"total_fat": {
"value": 300,
"unit": "g"
},
"trans_fat": {
"value": 300,
"unit": "g"
},
"saturated_fat": {
"value": 300,
"unit": "g"
},
"fiber": {
"value": 300,
"unit": "g"
},
"total_sugars": {
"value": 300,
"unit": "g"
},
"added_sugars": {
"value": 300,
"unit": "g"
},
"cholesterol": {
"value": 300,
"unit": "g"
},
"calcium": {
"value": 300,
"unit": "g"
},
"iron": {
"value": 300,
"unit": "g"
},
"potassium": {
"value": 300,
"unit": "g"
},
"sodium": {
"value": 300,
"unit": "g"
},
"vitamin_d": {
"value": 300,
"unit": "g"
}
}
}
],
"totals": {
"logs_count": 41,
"days_with_logs": 28,
"nutrients": {
"calories": {
"value": 300,
"unit": "g"
},
"protein": {
"value": 300,
"unit": "g"
},
"carbohydrates": {
"value": 300,
"unit": "g"
},
"net_carbohydrates": {
"value": 300,
"unit": "g"
},
"total_fat": {
"value": 300,
"unit": "g"
},
"trans_fat": {
"value": 300,
"unit": "g"
},
"saturated_fat": {
"value": 300,
"unit": "g"
},
"fiber": {
"value": 300,
"unit": "g"
},
"total_sugars": {
"value": 300,
"unit": "g"
},
"added_sugars": {
"value": 300,
"unit": "g"
},
"cholesterol": {
"value": 300,
"unit": "g"
},
"calcium": {
"value": 300,
"unit": "g"
},
"iron": {
"value": 300,
"unit": "g"
},
"potassium": {
"value": 300,
"unit": "g"
},
"sodium": {
"value": 300,
"unit": "g"
},
"vitamin_d": {
"value": 300,
"unit": "g"
}
}
},
"average_per_logged_day": {
"nutrients": {
"calories": {
"value": 300,
"unit": "g"
},
"protein": {
"value": 300,
"unit": "g"
},
"carbohydrates": {
"value": 300,
"unit": "g"
},
"net_carbohydrates": {
"value": 300,
"unit": "g"
},
"total_fat": {
"value": 300,
"unit": "g"
},
"trans_fat": {
"value": 300,
"unit": "g"
},
"saturated_fat": {
"value": 300,
"unit": "g"
},
"fiber": {
"value": 300,
"unit": "g"
},
"total_sugars": {
"value": 300,
"unit": "g"
},
"added_sugars": {
"value": 300,
"unit": "g"
},
"cholesterol": {
"value": 300,
"unit": "g"
},
"calcium": {
"value": 300,
"unit": "g"
},
"iron": {
"value": 300,
"unit": "g"
},
"potassium": {
"value": 300,
"unit": "g"
},
"sodium": {
"value": 300,
"unit": "g"
},
"vitamin_d": {
"value": 300,
"unit": "g"
}
}
}
}API key or client token.
Free up to your plan's free-call limit, then 1 credit per 100 calls.
Fetches one food log by the id returned when it was created.
Callable with a client token carrying the food_logs:read scope.
Your credential, sent as Authorization: Bearer <credential>. Two kinds reach this API, and the prefix tells them apart.
API key (sk-…) — created in the Developer Dashboard, it authenticates your whole account. Use it only where you control the machine — never inside a mobile app or a browser. Every endpoint accepts it.
Client token (ct-…) — a short-lived credential bound to one of your end users, minted from your API key by POST /v1.2/auth/client-tokens on your backend and relayed to the device. Safe to hold on a phone: it expires within 2 hours and acts only as the user it was minted for. Endpoints that open with API key or client token accept one and name the scope it must carry; endpoints that open with API key only refuse it.
The log id returned when the log was created.
78129823-8ba2-4183-b13b-71f0e963c606Pattern: ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$Your stable ID for the end user whose logs this request reads or writes. Opaque to January — use the same ID your system already uses for them.
| Credential | Header | Result |
|---|---|---|
API key (sk-…) | absent | 400 end_user_id_required |
API key (sk-…) | present | the request acts on that end user |
Client token (ct-…) | absent | filled in from the token |
Client token (ct-…) | the end user the token is bound to | accepted |
Client token (ct-…) | any other end user | 403 end_user_id_mismatch |
The requested food log, hydrated with resolved food and nutrition.
Save this id to update or delete the log. Null only when the upstream sent a log with no id — such a log cannot be addressed.
78129823-8ba2-4183-b13b-71f0e963c606When the meal was eaten. UTC, with milliseconds.
2024-09-13T11:34:56.000ZNull when no name was given.
Breakfastlog_id is not a UUID.
The request carried no Authorization header, or the credential in it was rejected.
unauthorized — the header is missing or malformed, or the key is not one we recognise. A valid key belonging to the other API version is 403 forbidden instead.
A client token is rejected in one of three ways, and only the first should be handled automatically:
token_expired— the token is past its TTL. Mint a fresh one from your backend and retry the request once. This is routine and expected once per TTL window.token_invalid— no such token: it was never issued, or it has been purged, which happens shortly after it expires. This code is not an automatic token-refresh signal.token_revoked— the token was revoked, byPOST /v1.2/auth/client-token-revocationsor from the dashboard. The end user signs in again in your app, and your backend decides whether to mint another; a device that just re-mints defeats the revocation.
The credential is valid but is not allowed to make this request. forbidden is the general case — a key issued for the other API version, for example. A client token adds client_token_not_allowed, meaning the endpoint takes only an sk- API key — its description opens with API key only. (in this API: POST /v1.2/auth/client-tokens, POST /v1.2/auth/client-token-revocations, POST /v1.2/chat/completions, POST /v1.2/literature/search, POST /v1.2/extractions, GET /v1.2/extractions/{job_id}, GET /v1.2/patients/{patient_id}/snapshot, and GET /v1.2/credits).
On an endpoint that opens with API key or client token., scope_insufficient means the token was minted without the scope named at the end of that endpoint’s description, and end_user_id_mismatch means the January-End-User-ID header disagrees with the end user the token is bound to — omit it, or send exactly that id.
No log with this id for this user.
The log references a food or serving that is no longer available. The message names them when known, and says how to fix it.
Two failures share this status; use code to choose the response.
rate_limited— a rolling abuse window was reached: the partner-wide v1.2 minute cap, a configured short-window limit, or a client-token limit. Back off and retry;Retry-Aftercarries the wait when Redis reports it.credit_limit_exceeded— this call costs more credits than you have left for the billing period, or they are spent. Never returned to an Enterprise account, whose usage past the allowance is billed as overage instead.
Credits reset at the end of your billing period; GET /v1.2/credits reports that instant as resets_at and remains available when either limit refuses other operations.
Any other error: the HTTP status plus { code, message }. Retry only rate_limited and the transient 5xx codes.
GET /v1.2/food-logs/{log_id} HTTP/1.1
Host: partners.january.ai
Authorization: Bearer YOUR_SECRET_TOKEN
Accept: */*
{
"id": "78129823-8ba2-4183-b13b-71f0e963c606",
"foods": [
{
"food_id": "101963552",
"name": "Greek Yogurt, Plain, Whole Milk",
"brand_name": null,
"image_url": null,
"glycemic_index": 11.3,
"glycemic_load": 1.4,
"nutrients": {
"calories": {
"value": 300,
"unit": "g"
},
"protein": {
"value": 300,
"unit": "g"
},
"carbohydrates": {
"value": 300,
"unit": "g"
},
"net_carbohydrates": {
"value": 300,
"unit": "g"
},
"total_fat": {
"value": 300,
"unit": "g"
},
"trans_fat": {
"value": 300,
"unit": "g"
},
"saturated_fat": {
"value": 300,
"unit": "g"
},
"fiber": {
"value": 300,
"unit": "g"
},
"total_sugars": {
"value": 300,
"unit": "g"
},
"added_sugars": {
"value": 300,
"unit": "g"
},
"cholesterol": {
"value": 300,
"unit": "g"
},
"calcium": {
"value": 300,
"unit": "g"
},
"iron": {
"value": 300,
"unit": "g"
},
"potassium": {
"value": 300,
"unit": "g"
},
"sodium": {
"value": 300,
"unit": "g"
},
"vitamin_d": {
"value": 300,
"unit": "g"
}
},
"quantity": 1.4,
"serving": {
"id": "34237662",
"unit": "cup",
"weight_grams": 60,
"quantity": 1
}
}
],
"created_at": "2024-09-13T11:34:56.000Z",
"name": "Breakfast"
}API key or client token.
Free up to your plan's free-call limit, then 1 credit per 100 calls.
Replaces any subset of the log: foods, created_at, name. Omitted fields are left unchanged.
Callable with a client token carrying the food_logs:write scope.
Your credential, sent as Authorization: Bearer <credential>. Two kinds reach this API, and the prefix tells them apart.
API key (sk-…) — created in the Developer Dashboard, it authenticates your whole account. Use it only where you control the machine — never inside a mobile app or a browser. Every endpoint accepts it.
Client token (ct-…) — a short-lived credential bound to one of your end users, minted from your API key by POST /v1.2/auth/client-tokens on your backend and relayed to the device. Safe to hold on a phone: it expires within 2 hours and acts only as the user it was minted for. Endpoints that open with API key or client token accept one and name the scope it must carry; endpoints that open with API key only refuse it.
The log id returned when the log was created.
78129823-8ba2-4183-b13b-71f0e963c606Pattern: ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$Your stable ID for the end user whose logs this request reads or writes. Opaque to January — use the same ID your system already uses for them.
| Credential | Header | Result |
|---|---|---|
API key (sk-…) | absent | 400 end_user_id_required |
API key (sk-…) | present | the request acts on that end user |
Client token (ct-…) | absent | filled in from the token |
Client token (ct-…) | the end user the token is bound to | accepted |
Client token (ct-…) | any other end user | 403 end_user_id_mismatch |
When the meal was eaten — any ISO-8601 offset; stored and returned in UTC with milliseconds. Omit to leave it unchanged.
2024-09-13T11:34:56ZBreakfastThe updated log, hydrated with resolved food and nutrition, reflecting the applied changes.
Save this id to update or delete the log. Null only when the upstream sent a log with no id — such a log cannot be addressed.
78129823-8ba2-4183-b13b-71f0e963c606When the meal was eaten. UTC, with milliseconds.
2024-09-13T11:34:56.000ZNull when no name was given.
BreakfastA field is malformed; the message names it.
The request carried no Authorization header, or the credential in it was rejected.
unauthorized — the header is missing or malformed, or the key is not one we recognise. A valid key belonging to the other API version is 403 forbidden instead.
A client token is rejected in one of three ways, and only the first should be handled automatically:
token_expired— the token is past its TTL. Mint a fresh one from your backend and retry the request once. This is routine and expected once per TTL window.token_invalid— no such token: it was never issued, or it has been purged, which happens shortly after it expires. This code is not an automatic token-refresh signal.token_revoked— the token was revoked, byPOST /v1.2/auth/client-token-revocationsor from the dashboard. The end user signs in again in your app, and your backend decides whether to mint another; a device that just re-mints defeats the revocation.
The credential is valid but is not allowed to make this request. forbidden is the general case — a key issued for the other API version, for example. A client token adds client_token_not_allowed, meaning the endpoint takes only an sk- API key — its description opens with API key only. (in this API: POST /v1.2/auth/client-tokens, POST /v1.2/auth/client-token-revocations, POST /v1.2/chat/completions, POST /v1.2/literature/search, POST /v1.2/extractions, GET /v1.2/extractions/{job_id}, GET /v1.2/patients/{patient_id}/snapshot, and GET /v1.2/credits).
On an endpoint that opens with API key or client token., scope_insufficient means the token was minted without the scope named at the end of that endpoint’s description, and end_user_id_mismatch means the January-End-User-ID header disagrees with the end user the token is bound to — omit it, or send exactly that id.
No log with this id for this user.
Two failures share this status; use code to choose the response.
rate_limited— a rolling abuse window was reached: the partner-wide v1.2 minute cap, a configured short-window limit, or a client-token limit. Back off and retry;Retry-Aftercarries the wait when Redis reports it.credit_limit_exceeded— this call costs more credits than you have left for the billing period, or they are spent. Never returned to an Enterprise account, whose usage past the allowance is billed as overage instead.
Credits reset at the end of your billing period; GET /v1.2/credits reports that instant as resets_at and remains available when either limit refuses other operations.
Any other error: the HTTP status plus { code, message }. Retry only rate_limited and the transient 5xx codes.
PATCH /v1.2/food-logs/{log_id} HTTP/1.1
Host: partners.january.ai
Authorization: Bearer YOUR_SECRET_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 129
{
"foods": [
{
"food_id": "101963552",
"serving_id": "68051535",
"quantity": 1.4
}
],
"created_at": "2024-09-13T11:34:56Z",
"name": "Breakfast"
}{
"id": "78129823-8ba2-4183-b13b-71f0e963c606",
"foods": [
{
"food_id": "101963552",
"name": "Greek Yogurt, Plain, Whole Milk",
"brand_name": null,
"image_url": null,
"glycemic_index": 11.3,
"glycemic_load": 1.4,
"nutrients": {
"calories": {
"value": 300,
"unit": "g"
},
"protein": {
"value": 300,
"unit": "g"
},
"carbohydrates": {
"value": 300,
"unit": "g"
},
"net_carbohydrates": {
"value": 300,
"unit": "g"
},
"total_fat": {
"value": 300,
"unit": "g"
},
"trans_fat": {
"value": 300,
"unit": "g"
},
"saturated_fat": {
"value": 300,
"unit": "g"
},
"fiber": {
"value": 300,
"unit": "g"
},
"total_sugars": {
"value": 300,
"unit": "g"
},
"added_sugars": {
"value": 300,
"unit": "g"
},
"cholesterol": {
"value": 300,
"unit": "g"
},
"calcium": {
"value": 300,
"unit": "g"
},
"iron": {
"value": 300,
"unit": "g"
},
"potassium": {
"value": 300,
"unit": "g"
},
"sodium": {
"value": 300,
"unit": "g"
},
"vitamin_d": {
"value": 300,
"unit": "g"
}
},
"quantity": 1.4,
"serving": {
"id": "34237662",
"unit": "cup",
"weight_grams": 60,
"quantity": 1
}
}
],
"created_at": "2024-09-13T11:34:56.000Z",
"name": "Breakfast"
}API key or client token.
Free up to your plan's free-call limit, then 1 credit per 100 calls.
Idempotent: deleting an unknown or already-deleted log answers the same 204, so it is safe to retry.
Callable with a client token carrying the food_logs:write scope.
Your credential, sent as Authorization: Bearer <credential>. Two kinds reach this API, and the prefix tells them apart.
API key (sk-…) — created in the Developer Dashboard, it authenticates your whole account. Use it only where you control the machine — never inside a mobile app or a browser. Every endpoint accepts it.
Client token (ct-…) — a short-lived credential bound to one of your end users, minted from your API key by POST /v1.2/auth/client-tokens on your backend and relayed to the device. Safe to hold on a phone: it expires within 2 hours and acts only as the user it was minted for. Endpoints that open with API key or client token accept one and name the scope it must carry; endpoints that open with API key only refuse it.
The log id returned when the log was created.
78129823-8ba2-4183-b13b-71f0e963c606Pattern: ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$Your stable ID for the end user whose logs this request reads or writes. Opaque to January — use the same ID your system already uses for them.
| Credential | Header | Result |
|---|---|---|
API key (sk-…) | absent | 400 end_user_id_required |
API key (sk-…) | present | the request acts on that end user |
Client token (ct-…) | absent | filled in from the token |
Client token (ct-…) | the end user the token is bound to | accepted |
Client token (ct-…) | any other end user | 403 end_user_id_mismatch |
The log was deleted; empty body. Idempotent — deleting an already-deleted or unknown log also answers 204.
No content
log_id is not a UUID.
The request carried no Authorization header, or the credential in it was rejected.
unauthorized — the header is missing or malformed, or the key is not one we recognise. A valid key belonging to the other API version is 403 forbidden instead.
A client token is rejected in one of three ways, and only the first should be handled automatically:
token_expired— the token is past its TTL. Mint a fresh one from your backend and retry the request once. This is routine and expected once per TTL window.token_invalid— no such token: it was never issued, or it has been purged, which happens shortly after it expires. This code is not an automatic token-refresh signal.token_revoked— the token was revoked, byPOST /v1.2/auth/client-token-revocationsor from the dashboard. The end user signs in again in your app, and your backend decides whether to mint another; a device that just re-mints defeats the revocation.
The credential is valid but is not allowed to make this request. forbidden is the general case — a key issued for the other API version, for example. A client token adds client_token_not_allowed, meaning the endpoint takes only an sk- API key — its description opens with API key only. (in this API: POST /v1.2/auth/client-tokens, POST /v1.2/auth/client-token-revocations, POST /v1.2/chat/completions, POST /v1.2/literature/search, POST /v1.2/extractions, GET /v1.2/extractions/{job_id}, GET /v1.2/patients/{patient_id}/snapshot, and GET /v1.2/credits).
On an endpoint that opens with API key or client token., scope_insufficient means the token was minted without the scope named at the end of that endpoint’s description, and end_user_id_mismatch means the January-End-User-ID header disagrees with the end user the token is bound to — omit it, or send exactly that id.
Two failures share this status; use code to choose the response.
rate_limited— a rolling abuse window was reached: the partner-wide v1.2 minute cap, a configured short-window limit, or a client-token limit. Back off and retry;Retry-Aftercarries the wait when Redis reports it.credit_limit_exceeded— this call costs more credits than you have left for the billing period, or they are spent. Never returned to an Enterprise account, whose usage past the allowance is billed as overage instead.
Credits reset at the end of your billing period; GET /v1.2/credits reports that instant as resets_at and remains available when either limit refuses other operations.
Any other error: the HTTP status plus { code, message }. Retry only rate_limited and the transient 5xx codes.
DELETE /v1.2/food-logs/{log_id} HTTP/1.1
Host: partners.january.ai
Authorization: Bearer YOUR_SECRET_TOKEN
Accept: */*
No content
Last updated
Was this helpful?