Panera API endpoint
Returns one item's selectable option groups: for a combo, each group is a slot to fill (for example "pick 4 sandwiches", "pick 1 soup"), listing every eligible choice; for a single customizable item, each group is one topping/add-in/amount choice (for example No, Light, Regular, Extra). Each group has min_allowed and max_allowed (how many selections it requires). Each option is one selectable choice, with its own selectable variants (amount tiers) each carrying an item_id and price -- price 0 for a variant included at no extra charge. Panera does not publish a resolved display name for options or groups on this surface, so each option carries its raw upstream image_key as the caller-facing identifier instead of a name. An item that is not customizable, or an unknown item id, returns an empty groups list rather than an error -- Panera's own API does not distinguish the two cases. Item ids come from GET /panera/menu.
/panera/item-optionsQuery parameters:
cafe_id (required) — Panera cafe id, from /panera/locations, e.g. 601447.item_id (required) — a menu item's item_id, from /panera/menu, e.g. 643423.Panera does not publish a resolved display name for options or groups on this surface. Each option carries its raw upstream image_key as the caller-facing identifier instead of a name (the same convention this family already uses for unresolved product images — see /panera/menu's image_key field). image_key values are usually legible on their own, e.g. pecans-whole-r, bacon-turkey-bravo-sandwich-tce-half.
Response shape:
groups[] — each group has group_id, min_allowed, max_allowed (how many selections this group requires), and options[].options[] — each option has item_id, image_key, is_default, default_qty, an optional allergens statement (contains[]/may_contain[], each an {id, name} pair), and variants[].variants[] — each selectable amount/tier of one option, with its own item_id, variant_type (e.g. No, Light, Regular, Extra) and price (0 for a variant included at no extra charge).Example response (trimmed, a combo's "pick your sandwich" slot):
{"code":200,"msg":"OK","data":{"cafe_id":601447,"item_id":643423,"groups":[{"group_id":20964,"min_allowed":4,"max_allowed":4,"options":[{"item_id":649379,"image_key":"chipotle-chicken-avo-melt-half-bt","is_default":true,"default_qty":2,"allergens":{"contains":[{"id":"EGG","name":"Egg"},{"id":"MILK","name":"Milk"},{"id":"WHEAT","name":"Wheat"}],"may_contain":[{"id":"MAYSESAME","name":"Sesame"}]},"variants":[{"item_id":649379,"variant_type":"Regular","price":0}]}]}],"source_url":"https://www-api.panerabread.com/www-api/public/menu/customizations/601447/version/109855/643423/0","fetched_at":"2026-09-02T12:00:00Z"}}
Example response (trimmed, a single item's topping amount choice):
{"code":200,"msg":"OK","data":{"cafe_id":601447,"item_id":4219,"groups":[{"group_id":1080,"min_allowed":0,"max_allowed":5,"options":[{"item_id":4266,"image_key":"pecans-whole-r","is_default":true,"default_qty":1,"allergens":{"contains":[{"id":"TREENUTS","name":"Tree Nuts"}]},"variants":[{"item_id":5740,"variant_type":"No","price":0},{"item_id":5741,"variant_type":"Light","price":0.69},{"item_id":4266,"variant_type":"Regular","price":0.69},{"item_id":5744,"variant_type":"Extra","price":1.38}]}]}],"source_url":"https://www-api.panerabread.com/www-api/public/menu/customizations/601447/version/109855/4219/0","fetched_at":"2026-09-02T12:00:00Z"}}
| Field | Filled in, of 1 checked | Values in the sample |
|---|---|---|
| group_id | 1 | |
| min_allowed | 1 | 0 |
| max_allowed | 1 | 5 |
| options | 1 | median 13 entries |
| Parameter | Type | Required | Default | Description | Example |
|---|---|---|---|---|---|
| cafe_id | integer | Yes | Panera cafe id, from /panera/locations | 601447 | |
| item_id | integer | Yes | Menu item id, from /panera/menu | 643423 | |
| x-api-key (header) | string | Yes | API key required |
curl -X GET "https://api.crawlora.net/api/v1/panera/item-options?cafe_id=601447&item_id=643423" \ -H "x-api-key: $CRAWLORA_API_KEY"
{
"code": 200,
"msg": "OK",
"data": {
"cafe_id": 601447,
"item_id": 643423,
"groups": [
{
"group_id": 20964,
"min_allowed": 4,
"max_allowed": 4,
"options": [
{
"item_id": 649379,
"image_key": "chipotle-chicken-avo-melt-half-bt",
"is_default": true,
"default_qty": 2,
"allergens": {
"contains": [
{
"id": "EGG",
"name": "Egg"
},
{
"id": "MILK",
"name": "Milk"
},
{
"id": "WHEAT",
"name": "Wheat"
}
],
"may_contain": [
{
"id": "MAYSESAME",
"name": "Sesame"
}
]
},
"variants": [
{
"item_id": 649379,
"variant_type": "Regular",
"price": 0
}
]
}
]
}
],
"source_url": "https://www-api.panerabread.com/www-api/public/menu/customizations/601447/version/109855/643423/0",
"fetched_at": "2026-09-02T12:00:00Z"
}
}Request schema
No body schema
Response schema
#/definitions/panera.itemOptionsResponseDoc
| Field | Type | Required | Enum | Bounds | Example | Description |
|---|---|---|---|---|---|---|
| code | integer | No | Code is the HTTP status code or a custom code used to indicate the result of the request @example 200 | |||
| data | panera.ItemOptionsResponse | No | ||||
| data.cafe_id | integer | No | 601447 | |||
| data.fetched_at | string | No | ||||
| data.groups | array | No | ||||
| data.groups[].group_id | integer | No | 1080 | |||
| data.groups[].max_allowed | integer | No | 5 | |||
| data.groups[].min_allowed | integer | No | 0 | |||
| data.groups[].options | array | No | ||||
| data.groups[].options[].allergens | panera.MenuAllergens | No | MenuAllergens record, 6 fields: contains, may_contain. Described under Panera Bread Item Detail API. | |||
| data.groups[].options[].default_qty | integer | No | 1 | |||
| data.groups[].options[].image_key | string | No | pecans-whole-r | |||
| data.groups[].options[].is_default | boolean | No | true | |||
| data.groups[].options[].item_id | integer | No | 4266 | |||
| data.groups[].options[].variants | array | No | ||||
| data.groups[].options[].variants[].item_id | integer | No | 5741 | |||
| data.groups[].options[].variants[].price | number | No | 0.69 | |||
| data.groups[].options[].variants[].variant_type | string | No | Light | |||
| data.item_id | integer | No | 643423 | |||
| data.source_url | string | No | ||||
| msg | unknown | No | Msg is the message that describes the result of the request @example "Request successful" |
| Status | Description | Schema |
|---|---|---|
| 400 | Bad Request | #/definitions/app.Response |
| 429 | Too Many Requests | #/definitions/app.Response |
| 503 | Service Unavailable | #/definitions/app.Response |
Use environment variables for secrets and keep Crawlora API keys server-side.
curl -X GET "https://api.crawlora.net/api/v1/panera/item-options?cafe_id=601447&item_id=643423" \
-H "x-api-key: $CRAWLORA_API_KEY"