# DoorDash Grocery & Retail Scraper — Prices, Deals, Stock, UPC (`yugenox/doordash-retail-catalog-scraper`) Actor

Scrape full product catalogs of DoorDash grocery, convenience, pharmacy, alcohol, pet and retail stores: price, regular vs sale price, % off, stock status, verified UPC/GTIN, size, category, image. Whole stores or every store of a chain in a region (US, Canada, Australia, NZ). No login.

- **URL**: https://apify.com/yugenox/doordash-retail-catalog-scraper.md
- **Developed by:** [Yugenox Corp](https://apify.com/yugenox) (community)
- **Categories:** E-commerce, Marketing, Automation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.50 / 1,000 product rows

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## DoorDash Grocery & Retail Product Scraper

Scrape the **full product catalog** of any DoorDash grocery, convenience, pharmacy, alcohol, pet, beauty or retail store: **price, regular vs. sale price, % off, stock status, UPC / GTIN, pack size, category, description and image** for every product.

Point it at a store link and get the whole shelf (a 45,000-product Walmart in about 3 minutes), or let it **find every store of a chain in a region** — "every Shoppers Drug Mart in Ontario", "all 7-Elevens in California" — for store-to-store price comparison. No DoorDash account needed.

### What you get

One row per product per store:

| Field | Example | Notes |
|---|---|---|
| `name`, `size` | Doritos Sweet Chili Heat Tortilla Chips (235 g), `235 g` | size parsed from the listing |
| `price`, `priceText`, `currency` | `5.98`, `CA$5.98`, `CAD` | current shelf price on DoorDash |
| `regularPrice`, `isOnSale`, `discountPct` | `10.97`, `true`, `45` | struck-through regular price when on sale |
| `stockStatus`, `stockBadge`, `isOutOfStock` | `IN_STOCK`, `Many in stock` | `IN_STOCK` / `LOW_STOCK` / `OUT_OF_STOCK`, or empty when the store shows no stock info |
| `gtin` | `060410054420` | UPC-A / EAN-13, only when the retailer's SKU really is one (check digit verified) |
| `merchantSku` | `060410054420` | the retailer's own product code (UPC for many chains, internal SKU for others) |
| `unitPriceText`, `priceUnit`, `purchaseType`, `approxWeight` | `CA$4.20/kg`, `/ea`, `EACH_PRICED_BY_WEIGHT`, `Approx 0.11 kg each` | produce, deli and meat sold by weight |
| `category`, `categoryId` | `Snacks`, `758` | the store aisle the product was found in |
| `description`, `imageUrl`, `badges` | …, image link, `["Value size"]` | |
| `storeId`, `storeName`, `storeAddress`, `city`, `country`, `businessName` | | on every row, so multi-store datasets are ready to pivot |
| `itemId`, `ddSic`, `menuId`, `scrapedAt` | | stable ids for tracking prices over time |

Plus, optionally, **one summary row per store** (`dataType: "store"`): address, rating, all categories, how many products the store lists vs. how many were collected (`completenessPct`), on-sale and out-of-stock counts, and any category that could not be read.

### How to use it

**A whole store** — paste the store link from your browser:

```json
{ "storeUrls": ["https://www.doordash.com/en-CA/convenience/store/dollarama-oakville-24915989/"] }
```

**Only some aisles** — add category names (or paste a category link):

```json
{ "storeUrls": ["https://www.doordash.com/convenience/store/7-eleven-los-angeles-24954368/"], "categories": ["Drinks", "Snacks"] }
```

**Every store of a chain in a region**:

```json
{ "brands": ["Shoppers Drug Mart"], "regions": ["Ontario"], "cities": ["Toronto", "North York"], "maxStores": 50, "categories": ["Personal Care"] }
```

**Deals and product tracking across stores**:

```json
{ "brands": ["Walmart"], "regions": ["ON"], "maxStores": 20, "onlyOnSale": true, "keywords": ["coca-cola", "pepsi"] }
```

#### Input options

- **Store URLs or IDs** — store links (any language/country version), category links, or numeric store ids.
- **Brands / Regions / Cities** — find stores for you. Regions are state/province codes or names (`ON`, `Ontario`, `NY`, `California`) or `Canada` / `USA`. Brands must match the store name on DoorDash.
- **Max stores found by brand / region** — default 10.
- **Categories** — store aisles to include (empty = whole store).
- **Keywords** — keep products whose name contains any of the words.
- **Only products on sale**, **Skip out-of-stock products**.
- **Max products per store**, **Max products in total**.
- **Include product descriptions**, **Add one summary row per store**.
- **Proxy** — datacenter by default; the Actor switches to residential automatically if it is rate-limited and your Apify plan includes residential proxies.

Common alternative names are accepted too (`startUrls`, `url`, `storeIds`, `brand`, `state`, `city`, `category`, `keyword`, `maxResults`, `limit`, `maxProductsPerStore`…), so an input written for another tool usually just works.

#### What happens when things go wrong

The run always finishes with a status message that says what it got. If DoorDash rate-limits or blocks, the Actor retries on fresh IPs, moves to residential proxies (or, if your plan has none, slows down and waits the rate limit out), and if that fails too it stops early with everything collected so far (`Partial — …`). If the run hits its time limit, everything already fetched is saved. A category that could not be read is listed in the store summary row (`categoriesFailed`) and lowers `completenessPct`, so a partial catalog never looks complete. You are only ever charged for product rows that are in your dataset.

### Output example

```json
{
  "dataType": "product",
  "storeId": "31135053",
  "storeName": "Walmart",
  "itemId": "19624445413",
  "name": "Janes Pub Style Chicken Burgers (700 g)",
  "size": "700 g",
  "price": 5.98,
  "priceText": "CA$5.98",
  "currency": "CAD",
  "regularPrice": 10.97,
  "regularPriceText": "CA$10.97",
  "isOnSale": true,
  "discountPct": 45,
  "stockStatus": "IN_STOCK",
  "stockBadge": "Many in stock",
  "isOutOfStock": false,
  "badges": ["45% off"],
  "merchantSku": "6000199788985",
  "gtin": null,
  "description": "Janes Pub Style Chicken burgers are fully cooked and breaded, made with white meat…",
  "imageUrl": "https://img.cdn4dd.com/cdn-cgi/image/fit=contain,width=1200,height=672,format=auto/https://doordash-static.s3.amazonaws.com/media/photosV2/5702ac90-8899-4bec-a900-06c3379deddf-retina-large.jpg",
  "purchaseType": "UNIT",
  "soldByWeight": false,
  "category": "Meat",
  "storeAddress": "2525 St Clair Ave W, Toronto ON M6N 4Z5, Canada",
  "city": "Toronto",
  "country": "CA",
  "businessName": "Walmart",
  "storeUrl": "https://www.doordash.com/convenience/store/31135053/",
  "scrapedAt": "2026-09-24T09:00:01.678Z"
}
```

Store summary row:

```json
{
  "dataType": "store",
  "storeName": "Walmart",
  "status": "complete",
  "completenessPct": 100,
  "itemsListed": 45308,
  "uniqueItemsFound": 45299,
  "itemsSaved": 45299,
  "onSaleCount": 4238,
  "outOfStockCount": 44,
  "categoriesTotal": 39,
  "categoriesCrawled": 39,
  "categoriesFailed": [],
  "storeAddress": "2525 St Clair Ave W, Toronto ON M6N 4Z5, Canada",
  "rating": 4.6,
  "ratingCount": 3475
}
```

### Use cases

- **Price monitoring & MAP / price-parity checks** — track what a brand's products sell for at every Shoppers, Walmart, 7-Eleven or Safeway on DoorDash.
- **Promotion intelligence** — which products are on sale, how deep the discount is, and where.
- **Assortment & availability** — which products each store carries, and what is out of stock.
- **Product matching** — `gtin` joins DoorDash listings with your own catalog, other retailers or other marketplaces.
- **Retail & grocery research** — dollar-store, pharmacy and convenience pricing across cities, provinces and states.

### FAQ

**Which stores work?** DoorDash's retail storefronts — grocery (Walmart, Safeway, Fred Meyer, Metro, Sobeys, FreshCo, No Frills, Food Basics…), convenience (7-Eleven, Circle K…), pharmacy (Shoppers Drug Mart, Walgreens, Rexall, Chemist Warehouse…), alcohol (LCBO, Wine Rack, The Beer Store…), pet (PetSmart, Pet Valu…), dollar stores (Dollarama, Dollar Tree, Dollar General), office, beauty and hardware — in the US, Canada, Australia and New Zealand. Restaurant menus are not a product catalog; use a DoorDash restaurant scraper for those.

**How many products per store, and how fast?** Everything the store lists on DoorDash — from a few hundred at a convenience store to 45,000+ at a large Walmart. A convenience store takes seconds; a 45,000-product supercenter about 3 minutes.

**Why is `gtin` empty for some stores?** Some retailers list products under their own internal codes instead of UPCs (Walmart Canada, Safeway, 7-Eleven, Walgreens, LCBO). The Actor verifies every code and only fills `gtin` with genuine UPC / EAN numbers; the retailer's code is always in `merchantSku`.

**Are these the in-store prices?** They are the prices DoorDash shows to any visitor for that store. They can differ from shelf prices in the physical store, and member / loyalty prices are only flagged (`hasLoyaltyPrice`) when DoorDash shows them.

**What does `completenessPct` mean?** The share of the products the store says it lists that were collected. If a category could not be read even after retries, it is named in `categoriesFailed` and completeness is reduced accordingly — nothing is dropped silently.

**The brand search finds fewer stores than I expected.** It uses DoorDash's published store lists, which are incomplete for a few regions. You can always add any store directly by its link or id.

**Do I need a DoorDash account or cookies?** No.

**Do I need residential proxies?** No, but they help with big runs. The default datacenter proxy works, and DoorDash rate-limits it on large stores, so the Actor switches to residential proxies on its own if your Apify plan includes them. If it doesn't, the Actor stays on datacenter IPs at a slower, steadier pace, waits out rate limits and retries failed categories a second time, and the run's status message tells you if anything was still missed and what to change.

**Is it legal to scrape DoorDash?** This Actor collects only public data: the store details and product listings (names, prices, stock, product codes, images) that DoorDash shows to any visitor without signing in. Scraping publicly available data is generally legal, but how you use the results is up to you: follow the laws that apply to you, such as GDPR, PIPEDA and CCPA, and DoorDash's terms, and check with a lawyer if you are unsure about your use case. Apify's guide [Is web scraping legal?](https://blog.apify.com/is-web-scraping-legal/) is a good starting point.

**Does it access any private data?** No. It reads only the public store and product pages any visitor can open: store name, address and rating, and the products the store lists. It never signs in, uses no DoorDash account or cookies, and collects nothing about customers, orders, Dashers or payments.

**How much does it cost?** You pay per product row saved to your dataset. Store summary rows are free, and rows removed by your filters (on sale only, in stock only, keywords) are never charged. If a run stops early, you pay only for the products that were delivered.

# Actor input Schema

## `storeUrls` (type: `array`):

DoorDash store links (any country/language version), category links, or bare numeric store ids. Example: https://www.doordash.com/en-CA/convenience/store/dollarama-oakville-24915989/ — a category link (…/category/snacks-758) scrapes just that category.

## `brands` (type: `array`):

Find every store of a chain in the regions below, e.g. "Shoppers Drug Mart", "7-Eleven", "Walmart", "Dollarama", "Safeway", "LCBO". Must match the store name as shown on DoorDash ("Safeway" does not include "Safeway Pets"). Leave empty to take every retail store in the regions.

## `regions` (type: `array`):

State / province codes or names: "ON", "Ontario", "BC", "NY", "California", or whole countries "Canada" / "USA". Required when you use Brands or Cities.

## `cities` (type: `array`):

Only stores in these cities, e.g. "Toronto", "North York", "Portland".

## `maxStores` (type: `integer`):

Cap on stores taken from the brand / region search. Store links you list above are always scraped.

## `categories` (type: `array`):

Only these store categories, e.g. "Snacks", "Dairy & Eggs", "Personal Care". Leave empty for the whole store. Names are matched case-insensitively.

## `keywords` (type: `array`):

Keep only products whose name contains any of these words, e.g. "Coca-Cola", "Tylenol". Case-insensitive.

## `onlyOnSale` (type: `boolean`):

Keep only products shown with a discounted price (regular price struck through).

## `excludeOutOfStock` (type: `boolean`):

Drop products DoorDash marks as out of stock.

## `maxItemsPerStore` (type: `integer`):

Stop each store after this many products. Leave empty for the whole catalog.

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

Stop the run after this many products. Leave empty for no limit.

## `includeDescription` (type: `boolean`):

Turn off for a lighter dataset.

## `includeStoreSummary` (type: `boolean`):

A row with dataType "store": address, rating, categories, how many products the store lists vs. how many were collected, on-sale and out-of-stock counts.

## `maxConcurrency` (type: `integer`):

Leave empty for the tuned default (3 on datacenter proxies, 6 on residential). Higher values mostly add rate-limit retries.

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

Datacenter proxy is the default; the Actor switches to residential proxies automatically if it gets rate-limited and your Apify plan includes them.

## `_noResidential` (type: `boolean`):

Internal (canaries): behave exactly like an account without the RESIDENTIAL proxy group.

## Actor input object example

```json
{
  "storeUrls": [
    "https://www.doordash.com/en-CA/convenience/store/dollarama-oakville-24915989/"
  ],
  "maxStores": 10,
  "categories": [
    "Snacks",
    "Drinks"
  ],
  "onlyOnSale": false,
  "excludeOutOfStock": false,
  "includeDescription": true,
  "includeStoreSummary": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "_noResidential": false
}
```

# Actor output Schema

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

Every row this run saved, as JSON.

## `resultsCsv` (type: `string`):

The same rows as CSV, ready for a spreadsheet.

## `resultsTable` (type: `string`):

The same rows narrowed to the key columns (the "overview" view).

# 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 = {
    "storeUrls": [
        "https://www.doordash.com/en-CA/convenience/store/dollarama-oakville-24915989/"
    ],
    "categories": [
        "Snacks",
        "Drinks"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("yugenox/doordash-retail-catalog-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 = {
    "storeUrls": ["https://www.doordash.com/en-CA/convenience/store/dollarama-oakville-24915989/"],
    "categories": [
        "Snacks",
        "Drinks",
    ],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("yugenox/doordash-retail-catalog-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 '{
  "storeUrls": [
    "https://www.doordash.com/en-CA/convenience/store/dollarama-oakville-24915989/"
  ],
  "categories": [
    "Snacks",
    "Drinks"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call yugenox/doordash-retail-catalog-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,yugenox/doordash-retail-catalog-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/8q4BCK722dP8vb5gs/builds/jyRQPCgS3qGERGWNH/openapi.json
