# DoorDash Menu Scraper — Prices, Calories & Items | $0.59/1K (`bovi/doordash-scraper`) Actor

Scrape **DoorDash** chain menus: **prices, calories, item modifiers/options, descriptions, images** + store rating, delivery fee & ETA. Cloudflare-free `/business/` path, no browser. **One record per menu item**, numeric prices, structural RSC parser with drift signal + fallback.

- **URL**: https://apify.com/bovi/doordash-scraper.md
- **Developed by:** [Vitalii Bondarev](https://apify.com/bovi) (community)
- **Categories:** Other, MCP servers
- **Stats:** 11 total users, 3 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.57 / 1,000 menu items

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## DoorDash Menu Scraper — Prices & Calories | from $0.59/1K | No Cloudflare Block

Built for restaurant market analysts tracking chain pricing, nutrition app developers enriching food databases with calorie data, and franchise consultants benchmarking menu structures across competing chains.

**Pricing:** Pay per menu item — $0.59/1K (full menu). Free-plan previews return up to 10 items. Failed, empty, or low-confidence parses do not push dataset rows, so they do not create per-item charges.

**No Cloudflare. No Playwright. No browser required. Adaptive charging — never pay for bad data.**

Scrape **DoorDash chain restaurant pages** and extract full menus with prices, calories, item descriptions, and restaurant metadata. Uses the Cloudflare-free `/business/` chain path — **no browser required, no Playwright overhead**. Returns one record per menu item. Structural RSC parser with `parse_confidence` drift detection. **Pay per menu item.**

Get every Chipotle, McDonald's, or Starbucks menu item — with calories and prices — in one structured JSON output.

***

### vs. competitors

DoorDash's consumer `/store/` pages sit behind Cloudflare and require a full browser (15–25s per restaurant, frequent breakage). Most scrapers target this path. This actor targets the `/business/` chain path — served **without** Cloudflare challenge and containing full menu data as RSC streaming payloads. No browser, no Playwright, fast.

| Feature | This actor | Typical alternatives |
|---|---|---|
| Cloudflare challenge | No (uses `/business/` path) | Yes — 15–25s/store, fragile |
| Prices | **Numeric** (`price_cents` + `price_usd`) | Strings like `"$8.50"` |
| Item modifiers / options | **Yes** — option groups + per-option prices | No |
| Store metadata on every row | Rating, delivery fee, ETA | Usually omitted |
| Calories | Yes (null when undisclosed, not a fake `0`) | Often missing |
| Item IDs | Yes (`item_id`) | Rarely |
| Parser type | Structural RSC keys (no CSS classes) | CSS class-based (breaks on updates) |
| Parser health signal | `parse_confidence` + `parse_source` + `warnings` | None |
| Fallback when format drifts | **Yes** — RSC → `__NEXT_DATA__` → JSON scan | None (hard break) |
| Charge on failure | **Never** | Often (broken runs still billed) |

***

### Output Schema — Per Menu Item

| Field | Type | Description |
|-------|------|-------------|
| `restaurant_name` | string | Chain name, e.g. "Chipotle Mexican Grill" |
| `business_id` | string | DoorDash store/business ID |
| `city` | string | Store city |
| `state` | string | Store state/region code |
| `category` | string | Menu section name, e.g. "Entree", "Drinks" |
| `item_name` | string | Menu item name |
| `description` | string | Item description |
| `price_cents` | integer | Price in cents (e.g. 1320 = $13.20) |
| `price_usd` | float | Price in USD (e.g. 13.20) |
| `calories` | number | Calorie count — `null` when the chain doesn't disclose it (never a misleading `0`) |
| `options` | array | Option groups / modifiers: `[{name, min, max, options:[{name, price_usd, price_cents}]}]`. Empty list when the item has none. |
| `image_url` | string | CDN image URL |
| `item_id` | string | DoorDash item ID |
| `is_popular` | boolean | Marked as popular/featured |
| `rating` | float | Store average rating (repeated on each row) |
| `num_ratings` | integer | Number of store ratings |
| `delivery_fee` | string | Displayed delivery fee, e.g. `"$0.99"` |
| `eta_minutes` | integer | Estimated delivery time in minutes |
| `source_url` | string | The `/business/` URL scraped |
| `parse_confidence` | float | 0.0–1.0 structural parse quality score |
| `parse_source` | string | Which parser produced the row: `rsc` (primary) or `next_data` (fallback) |
| `warnings` | array | Machine-readable drift signals |
| `scraped_at` | string | ISO-8601 UTC timestamp |

Each menu-item row is **self-contained** — it repeats the store name, rating, delivery fee, and ETA, so you can analyze a single row without joining back to a restaurant record.

***

### How to Use

#### Option A — direct URLs

```json
{
  "businessUrls": [
    "https://www.doordash.com/business/chipotle-mexican-grill-115/",
    "https://www.doordash.com/business/mcdonald-s-36398/"
  ]
}
```

Find the business URL by visiting any chain's DoorDash page and copying the `/business/` path URL.

#### Option B — slug + ID

```json
{
  "businesses": [
    {"slug": "chipotle-mexican-grill", "businessId": "115"},
    {"slug": "mcdonald-s", "businessId": "36398"}
  ]
}
```

#### Bulk menu comparison

```json
{
  "businessUrls": [
    "https://www.doordash.com/business/chipotle-mexican-grill-115/",
    "https://www.doordash.com/business/taco-bell-6174/",
    "https://www.doordash.com/business/subway-3796/"
  ]
}
```

***

### Use Cases

#### Restaurant menu intelligence

Pull every item name, price, and calorie count from competing chains. Track price changes across locations or compare value-per-calorie across menus.

#### Food delivery market research

Analyze menu structure, category organization, and item naming conventions across fast-food chains. Identify menu gaps or pricing anomalies.

#### Nutrition & calorie tracking apps

Build or enrich food databases with DoorDash menu data. Filter by calorie count, price range, or `is_popular` flag.

#### Franchise pricing analysis

Compare prices for the same chain across different city/state combinations. Identify regional price variation in franchise menus.

#### Competitive menu benchmarking

Restaurants building new menus can compare their pricing and item structure against established chains at scale.

***

### Scope & Limitations

**Supported**: DoorDash `/business/` chain pages only — these contain the full embedded menu without Cloudflare challenge.

**Not supported**: Consumer `/store/` delivery pages (which show delivery ETA, address-gated menus, and real-time availability). Those require Playwright and are a Phase-2 add-on.

- Menu items without a section name get `category: "Menu"`
- Calories are `null` when the chain doesn't publicly disclose them (never a misleading `0`)
- Prices reflect the chain's listed menu price, not delivery-fee-adjusted totals

***

### Proxy

**RESIDENTIAL proxy is strongly recommended.** Datacenter IPs may be rate-limited on high-volume runs. The actor defaults to `useApifyProxy: true, groups: ["RESIDENTIAL"]`. Proxy usage is billed to the run owner at standard Apify platform pricing.

***

### Parser Resilience (the B-plan)

RSC payload formats drift. This actor degrades gracefully instead of breaking:

1. **Primary — RSC parse.** Reassembles the React-Flight stream across `__next_f.push()` chunk boundaries (a single menu node frequently spans two pushes; naive parsers drop it). Nodes are matched by **stable keys/shape**, never by positional hex IDs or CSS classes.
2. **Fallback — `__NEXT_DATA__` / embedded JSON.** If no RSC stream is present, the actor harvests menu data from the legacy `__NEXT_DATA__` script or any inline `application/json` block. Rows are tagged `parse_source: "next_data"`.
3. **Fail-soft everywhere.** A truncated or changed node is skipped, never crashes the run. `parse_confidence` drops to reflect the gap.

Every output record includes `parse_confidence` (0.0–1.0), `parse_source`, and `warnings`. A score below 0.5 signals likely structural change; below 0.3 the actor skips output so buyers are not billed for low-confidence rows. Apify's daily default-run health test catches drift before buyers report it.

***

### Pricing

Pay per menu item scraped (`apify-default-dataset-item` event, **$0.59/1K**). A 50-item menu = 50 charges; a 150-item menu = 150 charges. You are **never** charged for:

- the actor starting up,
- failed or blocked fetches,
- restaurants that return no items,
- low-confidence parses (`parse_confidence < 0.3`).

Set `maxItems` to cap spend: it's a strict budget enforced even mid-restaurant.

***

### Pricing example

| Volume | Cost |
|---|---|
| 50 items (1 Chipotle) | $0.075 |
| 100 items (~2 restaurants) | $0.15 |
| 1,000 items (~15 restaurants) | $1.50 |
| 10,000 items | $15.00 |

A typical chain restaurant has 30–80 menu items. First 10 items free. Low-confidence parses (`parse_confidence < 0.3`) are pushed for transparency but not charged.

### FAQ

**Do I need a DoorDash account or API key?**
No. This actor uses the public `/business/` chain pages — no login, no credentials required.

**Do I need a proxy?**
RESIDENTIAL proxy is strongly recommended — datacenter IPs are commonly blocked by DoorDash on higher volumes. The actor defaults to `useApifyProxy: true, groups: ["RESIDENTIAL"]`. Proxy costs are billed to the run owner at standard Apify pricing.

**What output formats are available?**
JSON (default), CSV, and Excel — via the Apify dataset export or API.

**What if the actor returns no menu items?**
Ensure the URL is a `/business/` chain page (not a `/store/` consumer delivery page). If parsing fails, check `parse_confidence` in logs — a score below 0.5 means DoorDash's RSC format may have changed. The actor auto-retries with the `__NEXT_DATA__` fallback before giving up.

### Calorie data use case

Nutrition app building example: scrape Chipotle's full menu, filter `calories > 0`, deduplicate by `item_name` — instant structured nutrition database. Health-conscious buyers pay a premium for calorie-guaranteed data.

### Use with AI agents (MCP)

This actor is tagged `MCP_SERVERS` — compatible with Claude, GPT-4o, and other MCP-aware agents:

```
https://mcp.apify.com/?tools=bovi/doordash-scraper
```

*Not affiliated with DoorDash. Data is publicly available on doordash.com.*

### Integrations

Built for restaurant market analysts and nutrition-app developers extracting chain menu prices and calorie data at scale — the JSON/dataset output drops into the tools you already run, no glue code:

- **n8n / Make / Zapier** — trigger a run or pipe every new dataset item into 500+ apps (Google Sheets, Airtable, Slack, HubSpot, your database) with no code: [n8n](https://docs.apify.com/platform/integrations/n8n), [Make](https://docs.apify.com/platform/integrations/make), [Zapier](https://docs.apify.com/platform/integrations/zapier).
- **Webhooks** — fire your own endpoint the moment a run finishes, to push results straight into your pipeline ([docs](https://docs.apify.com/platform/integrations/webhooks)).
- **MCP server** — expose this actor as a tool to Claude, Cursor, or any [MCP client](https://mcp.apify.com) so an AI agent can pull this data mid-conversation ([guide](https://blog.apify.com/how-to-use-mcp/)).
- **API & SDKs** — fetch the dataset as JSON, CSV, or Excel through the Apify REST API or the Python / JS SDKs.

See all [Apify integrations](https://apify.com/integrations).

### More scrapers from our toolkit

Building a data pipeline? These actors pair well with this one — each runs on your own Apify account with the same pay-per-result pricing, no subscription:

- [Uber Eats Menu Scraper](https://apify.com/bovi/ubereats-menu)
- [Opentable Scraper](https://apify.com/bovi/opentable-scraper)

Chain any of them together from the **Integrations** tab (the *Run succeeded* trigger) to build a multi-step workflow — one actor's output feeds the next.

# Actor input Schema

## `businessUrls` (type: `array`):

List of DoorDash /business/ chain restaurant URLs to scrape. Each URL must be in the format https://www.doordash.com/business/{slug}-{businessId}/. Example: \['https://www.doordash.com/business/chipotle-mexican-grill-115/']. These pages are CF-free and return full menu data.

## `businesses` (type: `array`):

Alternative input: list of {slug, businessId} objects. Slug is the URL-safe chain name (e.g. 'chipotle-mexican-grill'), businessId is the numeric DoorDash store ID. Example: \[{"slug": "chipotle-mexican-grill", "businessId": "115"}].

## `maxItems` (type: `integer`):

Hard cap on total menu item records returned across all URLs. 0 = unlimited. This is a strict budget: the actor stops charging once the cap is hit, even mid-restaurant — each menu item is exactly one PPE charge. A typical chain restaurant has 30–80 items.

## `requestDelay` (type: `number`):

Seconds to wait between fetching consecutive URLs. Default 1.5s is polite and reduces ban risk. Increase for large batches.

## `proxyConfiguration` (type: `object`):

Proxy settings. Default: Apify RESIDENTIAL proxy (strongly recommended — datacenter IPs are frequently blocked by DoorDash). Set useApifyProxy:true with groups:\["RESIDENTIAL"]. Buyers pay for proxy usage via Apify platform compute.

## Actor input object example

```json
{
  "businessUrls": [
    "https://www.doordash.com/business/chipotle-mexican-grill-115/"
  ],
  "maxItems": 0,
  "requestDelay": 1.5,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `results` (type: `string`):

Dataset containing Doordash Scraper records (restaurant\_name, item\_name, category, price\_usd, rating, calories, is\_popular, city, state, source\_url, scraped\_at, parse\_confidence).

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "businessUrls": [
        "https://www.doordash.com/business/chipotle-mexican-grill-115/"
    ],
    "maxItems": 0
};

// Run the Actor and wait for it to finish
const run = await client.actor("bovi/doordash-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "businessUrls": ["https://www.doordash.com/business/chipotle-mexican-grill-115/"],
    "maxItems": 0,
}

# Run the Actor and wait for it to finish
run = client.actor("bovi/doordash-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "businessUrls": [
    "https://www.doordash.com/business/chipotle-mexican-grill-115/"
  ],
  "maxItems": 0
}' |
apify call bovi/doordash-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,bovi/doordash-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/yZhjxlIBoYfcivk8c/builds/tZdgUfdM8cFMgypVx/openapi.json
