Shake Shack API endpoint
Returns one Shake Shack location's full menu as a category tree: every category and its products, with name, description, base calories, allergens, a promotional/dietary badge when published (e.g. "LIMITED TIME ONLY", "VEGETARIAN"), image, and a full customization tree (size, additions, removals, allergen flags), each choice carrying its own price and calorie delta. location_id comes from GET /shakeshack/nearby. Many items, most notably burgers, price entirely through a required size selection rather than a base price, so price is commonly 0 for those items -- the real price lives on the size modifier's own choices.
/shakeshack/menuReturns one Shake Shack location's full menu as a category tree. This reads the same Olo-backed ordering API as /api/v1/shakeshack/nearby, not the main site's Drupal pages.
Query parameters:
location_id (required) -- location id from /api/v1/shakeshack/nearby. A location_id that does not resolve to a real location returns a 404.Response includes location_id, categories[], category_count, product_count, source_url, and fetched_at. Each category has name and products[]. Each product has chain_product_id (stable across every Shake Shack location's copy of the same item -- this response shape carries no location-scoped product id), name, description, price, base_calories, allergens (upstream's own free text, e.g. "dairy allergy, egg allergy, wheat allergy, soy allergy, sesame allergy"), badge (a short promotional/dietary label Shake Shack publishes for some items, e.g. LIMITED TIME ONLY, VEGETARIAN, NEW VEGGIE), image_url, and modifiers[].
Many items, most notably burgers, price entirely through a required size selection rather than a base price, so price is commonly 0 for those items -- not a parsing gap. The real price lives on the size modifier's own choices (e.g. ShackBurger: price: 0 at the top level, with its "Select Size:" modifier group carrying Single $7.99, Double $10.49, Triple $13.79).
Each modifiers[] entry is a group: group_id, chain_modifier_id, name (e.g. "Select Size:", "Remove Lettuce:"), type (upstream's own classification, observed values: ADDITION, MODIFICATION, REMOVAL, SIZE, SIZE/FLAVOR, ALLERGEN, UNCLASSIFIED), and choices[]. Each choice has option_id, chain_option_id, name, price (that choice's price delta), is_default, calorie_delta (can be negative, e.g. removing an ingredient), and its own nested groups[] (the schema supports further nesting, though no menu item observed at build time actually nests beyond one level).
| Field | Filled in, of 12 checked | Values in the sample |
|---|---|---|
| name | 12 | “Limited Time Only” and 11 other values |
| products | 11 | median 8 entries |
| Parameter | Type | Required | Default | Description | Example |
|---|---|---|---|---|---|
| location_id | integer | Yes | Location id from /shakeshack/nearby | 14992 | |
| x-api-key (header) | string | Yes | API key required |
curl -X GET "https://api.crawlora.net/api/v1/shakeshack/menu?location_id=14992" \ -H "x-api-key: $CRAWLORA_API_KEY"
{
"code": 200,
"msg": "OK",
"data": {
"location_id": 60161,
"categories": [
{
"name": "Burgers & More",
"products": [
{
"chain_product_id": 1119032,
"name": "ShackBurger",
"description": "A made-to-order quarter pound* per patty of 100% Angus beef with American cheese, lettuce, tomato, and ShackSauce on a toasted potato bun.",
"price": 0,
"base_calories": 760,
"allergens": "dairy allergy, egg allergy, wheat allergy, soy allergy, sesame allergy",
"modifiers": [
{
"group_id": 890226902,
"name": "Select Size:",
"type": "SIZE",
"choices": [
{
"option_id": 4127201664,
"name": "Single",
"price": 7.99,
"is_default": false
},
{
"option_id": 4127201665,
"name": "Double",
"price": 10.49,
"is_default": true
}
]
}
]
}
]
}
],
"category_count": 8,
"product_count": 42,
"source_url": "https://ssma24.com/v3/locations/60161/menu?platform=web&includeCombo=true&includeSoldOut=true",
"fetched_at": "2026-09-04T12:00:00Z"
}
}Request schema
No body schema
Response schema
#/definitions/shakeshack.menuResponseDoc
| 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 | shakeshack.MenuResponse | No | ||||
| data.categories | array | No | ||||
| data.categories[].name | string | No | Burgers & More | |||
| data.categories[].products | array | No | ||||
| data.categories[].products[].allergens | string | No | dairy allergy, egg allergy, soy allergy, wheat allergy, sesame allergy | Allergens is upstream's own comma-separated free text, passed through as published rather than split into a fixed list. | ||
| data.categories[].products[].badge | string | No | VEGETARIAN | Badge is a short promotional/dietary label Shake Shack publishes for some items, e.g. "LIMITED TIME ONLY", "VEGETARIAN". | ||
| data.categories[].products[].base_calories | integer | No | 880 | BaseCalories is 0 when upstream reports none. | ||
| data.categories[].products[].chain_product_id | integer | No | 1119032 | |||
| data.categories[].products[].description | string | No | ||||
| data.categories[].products[].image_url | string | No | ||||
| data.categories[].products[].modifiers | array | No | ||||
| data.categories[].products[].modifiers[].chain_modifier_id | integer | No | 38041 | |||
| data.categories[].products[].modifiers[].choices | array | No | ||||
| data.categories[].products[].modifiers[].choices[].calorie_delta | integer | No | -1 | |||
| data.categories[].products[].modifiers[].choices[].chain_option_id | integer | No | 213463 | |||
| data.categories[].products[].modifiers[].choices[].groups | array | No | ||||
| data.categories[].products[].modifiers[].choices[].is_default | boolean | No | ||||
| data.categories[].products[].modifiers[].choices[].name | string | No | No Lettuce | |||
| data.categories[].products[].modifiers[].choices[].option_id | integer | No | 4127201667 | |||
| data.categories[].products[].modifiers[].choices[].price | number | No | 0 | |||
| data.categories[].products[].modifiers[].group_id | integer | No | 890226904 | |||
| data.categories[].products[].modifiers[].name | string | No | Remove Lettuce: | |||
| data.categories[].products[].modifiers[].type | string | No | REMOVAL | |||
| data.categories[].products[].name | string | No | West Coast Burger | |||
| data.categories[].products[].price | number | No | 0 | Price is this item's base price. Many items price entirely through a required size/flavor modifier rather than a base price, so 0 is common here -- not a parsing gap (same shape as this repo's other Olo-backed menus, e.g. Jimmy John's). | ||
| data.category_count | integer | No | 11 | |||
| data.fetched_at | string | No | ||||
| data.location_id | integer | No | 14992 | |||
| data.product_count | integer | No | 76 | |||
| 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 |
| 404 | Not Found | #/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/shakeshack/menu?location_id=14992" \
-H "x-api-key: $CRAWLORA_API_KEY"