Papa John's API endpoint
Use Crawlora's Papa John's Menu API to extract supported public Papa John's data as structured JSON. This page includes request parameters, cURL examples, response schema, error behavior, credit cost, and a Playground link for testing before integration.
/papajohns/menuReturns a Papa John's US or Canada restaurant's full menu with that restaurant's own prices. Prices are per restaurant, not national -- the same 14-inch pizza can differ by several dollars between two stores -- so pass a store_id from GET /papajohns/store or GET /papajohns/nearby. Omitting it returns the national default menu, flagged as national_default in the response. Each category is returned with its sections and product groups, and every orderable SKU carries its price, calories, serving size, slice count and image. Set include_options to also get each product group's customization axes: every size and crust it is sold in, and every preparation instruction (how much cheese, how well done) with its choices. This is the US and Canada menu -- for India use GET /papajohns/india/menu, and for Chile, Costa Rica, Guatemala, Panama, Portugal or Spain use GET /papajohns/intl/menu. Developers commonly use this endpoint for data enrichment, monitoring, research dashboards, internal automation, and agent-native workflows that need repeatable structured public web data. Authentication uses the documented Crawlora headers, and usage is metered with the credit cost shown on this page.
Request parameters are generated from the active endpoint catalog. Required values must be sent before Crawlora can call the upstream public web data source.
| Parameter | Type | Required | Default | Description | Example |
|---|---|---|---|---|---|
| store_id | string | No | Restaurant id from GET /papajohns/store or GET /papajohns/nearby. Omit for the national default menu. | ||
| category | string | No | Return only one category. One of: dippingsauces, desserts, drinks, extras, papabowls, pizza, sandwiches, sides, wings Allowed values: dippingsauces, desserts, drinks, extras, papabowls, pizza, sandwiches, sides, wings | ||
| include_options | boolean | No | Include each product group's sizes, crusts and preparation instructions. Default false. | ||
| x-api-key (header) | string | Yes | API key required |
curl -X GET "https://api.crawlora.net/api/v1/papajohns/menu?category=dippingsauces&include_options=true" \ -H "x-api-key: $CRAWLORA_API_KEY"
Send your scraping API key in the x-api-key header. Use the console API Keys page to rotate or select the active key.
Endpoint usage is metered in credits. The plan prices, included credits, limits, and overage rates below match the active backend billing configuration.
| Plan | Price | Included credits | Daily cap | Rate limit | Overage |
|---|---|---|---|---|---|
| Free | $0/mo | 2,000 | 500 daily credits | 5/min | No overage |
| Starter | $9/mo | 20,000 | 5,000 daily credits | 15/min | $0.75/1,000 overage credits when enabled |
| Growth | $29/mo | 100,000 | 25,000 daily credits | 45/min | $0.45/1,000 overage credits when enabled |
| Pro | $79/mo | 400,000 | No daily cap | 120/min | $0.30/1,000 overage credits |
| Business | $199/mo | 1,200,000 | No daily cap | 300/min | $0.20/1,000 overage credits |
| Enterprise | $499/mo | 5,000,000 | No daily cap | 1,000/min | $0.12/1,000 overage credits |
This endpoint is executed through Crawlora's managed scraping infrastructure.
- `national_default` is `true` when no `store_id` was supplied, so a caller can always tell the fallback menu from a real restaurant's prices. - Each category contains sections, each section contains product groups, and each group contains the orderable SKUs with `price`, `calories`, `serving_label`, `slices` and an image. `price_from` on a group is its cheapest priced SKU. - A build-your-own SKU whose price depends entirely on the customer's choices reports `price` `0`; read the group's size and crust options instead. - Sections upstream hides, and groups whose SKUs carry no price, are omitted rather than returned looking free. - `available_topping_ids` are upstream's own numeric topping ids. This upstream publishes no names for them, so they are exposed as ids rather than invented labels; `max_toppings`, `max_same_topping` and `allow_half_toppings` give the rules that go with them. - An unknown `category` or a negative/non-numeric `store_id` returns a `400`; a `store_id` the upstream has no menu for returns a `404`. Example response: ```json {"code":200,"msg":"OK","data":{"store_id":5054,"national_default":false,"categories":[{"id":"pizza","name":"Pizza","sections":[{"id":"most_popular","name":"Most Popular","product_groups":[{"id":"cyo","title":"Create Your Own Pizza","description":"Build your own custom pizza and we'll handcraft it for you.","customizable":"full","price_from":14.99,"max_toppings":7,"max_same_topping":2,"allow_half_toppings":true,"products":[{"sku":"1-1-2-115","name":"10 Inch Original Pepperoni Pizza","title":"Small Original Crust - Pepperoni","size":"Small","price":14.99,"regular_price":14.99,"code":"PR10Q1","size_id":2,"crust_id":1,"calories":210,"serving_label":"/slice, 6 slices","slices":6}]}]}],"product_count":96}],"product_count":206,"source_url":"https://www.papajohns.com/api/trpc/menuCategory.getByStore?input={\"json\":{\"storeId\":5054}}","fetched_at":"2026-09-06T09:00:00Z"}} ```
Crawlora does not silently return bad data when the upstream page cannot be used.
| Status | Common failure case |
|---|---|
| 400 | Invalid input or missing required parameter |
| 429 | Plan or endpoint rate limit exceeded |
| 500 | Internal execution error |
| 502 | Upstream platform failed, returned unusable HTML, or served a challenge page that could not be resolved |
When possible, Crawlora returns structured error context so your integration can retry, back off, or inspect the request.
| 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 |
{
"code": 200,
"msg": "OK",
"data": {
"store_id": 5054,
"national_default": false,
"categories": [
{
"id": "pizza",
"name": "Pizza",
"sections": [
{
"id": "most_popular",
"name": "Most Popular",
"product_groups": [
{
"id": "cyo",
"title": "Create Your Own Pizza",
"description": "Build your own custom pizza and we'll handcraft it for you.",
"customizable": "full",
"price_from": 14.99,
"max_toppings": 7,
"max_same_topping": 2,
"allow_half_toppings": true,
"products": [
{
"sku": "1-1-2-115",
"name": "10 Inch Original Pepperoni Pizza",
"title": "Small Original Crust - Pepperoni",
"size": "Small",
"price": 14.99,
"regular_price": 14.99,
"code": "PR10Q1",
"size_id": 2,
"crust_id": 1,
"calories": 210,
"serving_label": "/slice, 6 slices",
"slices": 6
}
]
}
]
}
],
"product_count": 96
}
],
"product_count": 206,
"source_url": "https://www.papajohns.com/api/trpc/menuCategory.getByStore?input={\"json\":{\"storeId\":5054}}",
"fetched_at": "2026-09-06T09:00:00Z"
}
}Request schema
No body schema
Response schema
#/definitions/papajohns.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 | papajohns.MenuResponse | No | ||||
| data.categories | array | No | ||||
| data.categories[].id | string | No | pizza | |||
| data.categories[].image_url | string | No | ||||
| data.categories[].name | string | No | Pizza | |||
| data.categories[].product_count | integer | No | 96 | ProductCount is how many priced SKUs this category contains. | ||
| data.categories[].sections | array | No | ||||
| data.categories[].sections[].description | string | No | ||||
| data.categories[].sections[].id | string | No | most_popular | |||
| data.categories[].sections[].name | string | No | Most Popular | |||
| data.categories[].sections[].product_groups | array | No | ||||
| data.categories[].sections[].product_groups[].allow_half_toppings | boolean | No | true | |||
| data.categories[].sections[].product_groups[].available_topping_ids | array | No | ||||
| data.categories[].sections[].product_groups[].crusts | array | No | ||||
| data.categories[].sections[].product_groups[].crusts[].code | string | No | PIZZA_ORIGINAL | |||
| data.categories[].sections[].product_groups[].crusts[].default | boolean | No | true | |||
| data.categories[].sections[].product_groups[].crusts[].id | integer | No | 1 | |||
| data.categories[].sections[].product_groups[].crusts[].name | string | No | Original Crust | |||
| data.categories[].sections[].product_groups[].crusts[].sub_type | string | No | Original | |||
| data.categories[].sections[].product_groups[].customizable | string | No | full | Customizable reports how far the product can be customized, as upstream classifies it, e.g. full or none. | ||
| data.categories[].sections[].product_groups[].description | string | No | ||||
| data.categories[].sections[].product_groups[].featured | boolean | No | ||||
| data.categories[].sections[].product_groups[].free_toppings | integer | No | ||||
| data.categories[].sections[].product_groups[].id | string | No | cyo | |||
| data.categories[].sections[].product_groups[].image_url | string | No | ||||
| data.categories[].sections[].product_groups[].instructions | array | No | ||||
| data.categories[].sections[].product_groups[].instructions[].choices | array | No | ||||
| data.categories[].sections[].product_groups[].instructions[].choices[].code | string | No | ILC | |||
| data.categories[].sections[].product_groups[].instructions[].choices[].default | boolean | No | false | |||
| data.categories[].sections[].product_groups[].instructions[].choices[].id | integer | No | 6 | |||
| data.categories[].sections[].product_groups[].instructions[].choices[].name | string | No | Light Cheese | |||
| data.categories[].sections[].product_groups[].instructions[].description | string | No | How much cheese? | |||
| data.categories[].sections[].product_groups[].instructions[].id | integer | No | 2 | |||
| data.categories[].sections[].product_groups[].instructions[].name | string | No | CHEESE | |||
| data.categories[].sections[].product_groups[].max_same_topping | integer | No | 2 | |||
| data.categories[].sections[].product_groups[].max_toppings | integer | No | 7 | Topping rules. AvailableToppingIDs are upstream's own numeric ids; this API publishes no name for them, so they are exposed as ids rather than invented labels. | ||
| data.categories[].sections[].product_groups[].price_from | number | No | 14.99 | PriceFrom is the cheapest priced SKU in the group, so a caller can rank products without walking every SKU. | ||
| data.categories[].sections[].product_groups[].products | array | No | ||||
| data.categories[].sections[].product_groups[].products[].calories | integer | No | 210 | Calories is per the serving described by serving_label. | ||
| data.categories[].sections[].product_groups[].products[].code | string | No | PR10Q1 | |||
| data.categories[].sections[].product_groups[].products[].crust_id | integer | No | 1 | |||
| data.categories[].sections[].product_groups[].products[].description | string | No | ||||
| data.categories[].sections[].product_groups[].products[].disclaimer | string | No | ||||
| data.categories[].sections[].product_groups[].products[].image_url | string | No | ||||
| data.categories[].sections[].product_groups[].products[].name | string | No | 10 Inch Original Pepperoni Pizza | |||
| data.categories[].sections[].product_groups[].products[].price | number | No | 14.99 | Price is this store's price. A build-your-own SKU whose price depends entirely on the customer's choices reports 0. | ||
| data.categories[].sections[].product_groups[].products[].regular_price | number | No | 14.99 | RegularPrice is the pre-promotion menu price where it differs. | ||
| data.categories[].sections[].product_groups[].products[].serving_label | string | No | /slice, 6 slices | ServingLabel describes what calories are counted per, e.g. "/slice, 6 slices". | ||
| data.categories[].sections[].product_groups[].products[].size | string | No | Small | Size is the size label this SKU represents within its group. | ||
| data.categories[].sections[].product_groups[].products[].size_id | integer | No | 2 | |||
| data.categories[].sections[].product_groups[].products[].sku | string | No | 1-1-2-115 | |||
| data.categories[].sections[].product_groups[].products[].slices | integer | No | 6 | |||
| data.categories[].sections[].product_groups[].products[].surcharge | number | No | ||||
| data.categories[].sections[].product_groups[].products[].title | string | No | Title is the shorter in-menu label, e.g. "Small Original Crust - Pepperoni". | |||
| data.categories[].sections[].product_groups[].sizes | array | No | Sizes, Crusts and Instructions are populated only when include_options=true. | |||
| data.categories[].sections[].product_groups[].sizes[].default | boolean | No | false | |||
| data.categories[].sections[].product_groups[].sizes[].id | integer | No | 2 | |||
| data.categories[].sections[].product_groups[].sizes[].measure | string | No | 10" | Measure is the physical size where the group has one, e.g. 10". | ||
| data.categories[].sections[].product_groups[].sizes[].name | string | No | Small | Name is the size's marketing name, e.g. Small. | ||
| data.categories[].sections[].product_groups[].title | string | No | Create Your Own Pizza | |||
| data.category | string | No | pizza | |||
| data.fetched_at | string | No | ||||
| data.national_default | boolean | No | false | NationalDefault is true when no store was selected, so a caller can tell a real store's prices from the fallback menu. | ||
| data.product_count | integer | No | 206 | |||
| data.source_url | string | No | ||||
| data.store_id | integer | No | 5054 | StoreID echoes the restaurant the prices belong to. 0 is the national default menu. | ||
| msg | unknown | No | Msg is the message that describes the result of the request @example "Request successful" |
Use environment variables for secrets and keep Crawlora API keys server-side.
curl -X GET "https://api.crawlora.net/api/v1/papajohns/menu?category=dippingsauces&include_options=true" \
-H "x-api-key: $CRAWLORA_API_KEY"Crawlora is designed for responsible structured public web data workflows. Customers are responsible for using Crawlora in compliance with applicable laws, third-party rights, target-platform rules, and Crawlora terms.
Read Crawlora terms