DoorDash Store & Menu Scraper
Pricing
from $1.33 / 1,000 results
DoorDash Store & Menu Scraper
Extract a DoorDash restaurant's full menu with prices, plus its street address, city and cuisine category. Reads the page's structured data, so every section and item comes back with its price. HTTP-only, no account, no browser.
Pricing
from $1.33 / 1,000 results
Rating
0.0
(0)
Developer
Farhan Febrian Nauval
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Extract a DoorDash restaurant's full menu with prices, plus its street address, city and cuisine category. HTTP-only, no account, no browser.
Input
| Field | Type | Default | Description |
|---|---|---|---|
stores | array | required | Store URLs or bare numeric store ids |
maxProfiles | integer | 6 | TLS profiles to try before giving up on a store |
maxConcurrency | integer | 3 | Stores in parallel |
proxyConfiguration | object | RESIDENTIAL | Residential strongly recommended |
The slug in a store URL is decoration. /store/panda-express-san-francisco-303380/ and
/store/rosalind-coffee-company-garland-303380/ both resolve to store 303380 — only the
trailing number is identity. The actor extracts it and rebuilds a canonical URL.
Output
{"_input": "…/store/rosalind-coffee-company-garland-303380/","_source": "S1-jsonld","_scrapedAt": "2026-09-07T02:03:40Z","_tlsProfile": "chrome124", // which profile got through"storeId": "303380","name": "Rosalind Coffee Company","address": "107 N 6th St, Garland, TX 75040, USA","city": "Garland","cuisine": "Coffee","menuSectionCount": 4,"menuItemCount": 22,"menuSections": [{ "name": "Not Coffee", "itemCount": 8, "items": [{ "name": "Matcha", "description": null,"price": "$4.75", "priceAmount": 4.75, "priceCurrency": "USD" }]}],"faqs": [ { "question": "…", "answer": "…" } ],"breadcrumbs": [ { "position": 1, "name": "Home", "item": "/" } ]}
Every item in testing came back priced — 57 of 57 across three stores.
How it works
All of it comes from the page's JSON-LD, which carries more than the rendered page conveniently
shows: a Menu block with every section and item, a FAQPage whose answers contain the street
address, and a BreadcrumbList giving the city and cuisine DoorDash files the store under.
Cloudflare: the TLS ladder is the normal path, not a fallback
No single TLS profile is reliable here. Measured over six independent rounds from Apify
residential, the winning profile changed almost every time — safari180 twice, chrome124
twice, chrome131 once, and one round where none of six landed. A fixed profile fails often
enough to look like an outage.
So the actor rotates a six-profile ladder, each rung on its own session and its own exit IP (Cloudflare decides per connection, so reusing a session would make the attempts correlated). That took the hit rate to 5 of 6 rounds, and 2 of 2 on a re-run.
A block is detected from a positive data marker — the presence of the JSON-LD Menu block —
not from the status code, because Cloudflare serves some interstitials as a 200 with a large
body.
Prices come back in two different spellings
The same store returned "$4.75" on one fetch and "USD 4.75" (ISO code, non-breaking
space) on the next, minutes apart. The raw string is passed through unchanged, and
priceAmount (float) and priceCurrency are added alongside it so you never have to handle
both.
Parsing respects separator conventions: 1,234.56 and 1.234,56 are both 1234.56, and 3,50
is 3.50 — whichever of . or , comes last is the decimal separator. Getting that backwards
inflates a price 100-fold without any error, which is why it is unit-tested rather than
eyeballed.
Known limits
| Limit | Detail |
|---|---|
| No ratings or reviews | Not present in the structured data on this surface |
| No opening hours | Not in the JSON-LD; the FAQ answers sometimes mention delivery/pickup availability |
| Deleted vs blocked | DoorDash does not 404 a missing store — it serves a challenge, so a bad id reports blocked rather than not_found |
| Cost per store | A store can take several requests when the first profiles are challenged |