# Gopuff Scraper - US Quick-Commerce Prices & Assortment (`abotapi/gopuff-prices-assortment-scraper`) Actor

Scrape Gopuff, the US quick-commerce delivery store, by location or product: prices, promotions, stock and availability per fulfillment location across thousands of everyday products. Availability monitoring (NEW, UPDATED, REAPPEARED, EXPIRED), detail enrichment, incremental runs, MCP export.

- **URL**: https://apify.com/abotapi/gopuff-prices-assortment-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 product records

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

## Gopuff Scraper - US Quick-Commerce Prices & Assortment

Scrape Gopuff, the US quick-commerce delivery store, by location or by product:
per-store prices, promotions, stock counts and availability across thousands of
everyday products, exported through MCP connectors when you need them elsewhere.

### Why This Scraper?

- Blue-ocean coverage: Gopuff's micro-fulfillment catalog (thousands of products,
  \~1000 US locations, 15-minute delivery) has no dedicated storefront scraper.
- Per-location truth: Gopuff prices and assortment differ per fulfillment
  location. This actor resolves each ZIP/city you name to the storefront's own
  location and walks the catalog with that location's prices and deals.
- Promotions, not just prices: multi-buy offers ("2 for $5"), coupon-backed
  deals, unit prices, Fam-member pricing, badges and sponsored tiles are part
  of every record.
- Monitoring built in: label every record NEW / UPDATED / REAPPEARED / EXPIRED
  across runs, resume an interrupted crawl, and export to MCP connectors.
- Structured failures: a blocked run fails loudly with a clear message instead
  of returning an empty dataset.

### Data You Get

Catalog mode walks the collections of each location and returns one record per
product: product id, title, URL, image, price and currency, Fam price, unit
price, promotions (text + coupon id), badges, size label, availability, stock
quantity, SNAP eligibility, sponsored flags, category and location ids.

Product mode reads product links or ids and returns one enriched record per
product: everything above plus description, brand, SKU and the product's own
image. With the detail-enrichment option, catalog records gain the same detail
fields.

Incremental runs add `changeType`, `changedFields`, `firstSeenAt` and
`lastSeenAt` to every record.

### How to Use

1. Pick **Catalog by location** (the default), add one or more US ZIP codes or
   city names, and run. The actor walks the storefront navigation and emits
   records until Max items is reached.
2. Want specific departments? Put collection links (for example
   `https://www.gopuff.com/c/candy/txERmYDy`) or deal-page links into
   **Collections**.
3. Need individual products? Switch to **Product links or ids** mode and list
   product links or numeric ids.
4. Monitoring a catalog over time? Turn on **Incremental runs** and reuse the
   same input; the actor tracks what changed between runs.
5. Optional: connect an MCP connector to push records into Notion or another
   destination after each run.

### Input Parameters

| Parameter | Type | Default | Description |
|---|---|---|---|
| mode | select | categories | Catalog by location, or product links/ids |
| locations | array | - | Catalog mode: US ZIP codes, city names, or lat,lng pairs |
| locationId | string | empty | Advanced: pin an exact storefront location id |
| categoryInputs | array | empty | Catalog mode: collection links or bare ids; empty walks the whole navigation |
| discoverCategories | boolean | true | Read the storefront navigation when no collections are set |
| inStockOnly | boolean | false | Hide items the location currently cannot deliver |
| productInputs | array | empty | Product mode: product links or numeric ids |
| maxItems | integer | 20 | Stop after this many records (0 = unlimited); shared across locations |
| maxPages | integer | 0 | Max pages per collection (0 = every page the collection has) |
| pageSize | integer | 50 | Products per page (10-50) |
| fetchDetails | boolean | false | Catalog mode: read each product's own document (per-record surcharge) |
| incrementalMode | boolean | false | Track NEW / UPDATED / REAPPEARED / EXPIRED between runs |
| emitUnchanged | boolean | false | Also return (and bill) unchanged records |
| emitExpired | boolean | false | Also return (and bill) records gone from a complete scan |
| stateKey | string | empty | Name a monitoring campaign explicitly |
| resumeFromRunId | string | empty | Skip records already collected by a run or dataset |
| mcpConnectors | array | empty | MCP connector export |
| notionParentPageUrl | string | empty | Notion parent page for the Notion connector |
| maxNotifyListings | integer | 50 | Cap per connector per run |
| proxyConfiguration | object | proxy on | Apify proxy; the read needs one residential US exit |

Keyword search is deliberately not offered: the storefront answers keyword
queries with result counts and facets but serves its result grid only to
sessions with a delivery address, which an anonymous read cannot set. The
collection walk is the way to enumerate the catalog.

### Output Example

```json
{
  "kind": "product",
  "productId": "302563",
  "url": "https://www.gopuff.com/p/starburst-sour-fruit-chews-chewy-candy-share-size-3-45-oz/p302563",
  "title": "STARBURST Sour Fruit Chews Chewy Candy, Share Size, 3.45 oz",
  "price": 3.29,
  "currency": "USD",
  "pricePerUnitUsd": 0.954,
  "unit": "oz",
  "hasPromotion": true,
  "promotions": [{"text": "2 for $5", "couponId": "6d22245b-bd84-4dad-bf0b-bc9181d2961d", "couponSystem": "GOCOUPONS"}],
  "sizeLabel": "3.45oz",
  "availability": "in_stock",
  "quantityAvailable": 19,
  "snapEligible": true,
  "categoryId": "txERmYDy",
  "categoryName": null,
  "locationId": "691",
  "imageUrl": "https://images.gopuff.com/blob/...",
  "priceChannel": "default",
  "changeType": "NEW",
  "firstSeenAt": "2026-08-30T02:00:00+00:00",
  "lastSeenAt": "2026-08-30T02:00:00+00:00"
}
```

### Send results into your apps (MCP connectors)

Optionally pipe results into Notion, Linear, Airtable or Apify via MCP connectors, in the
**Integrations** section of the input. Each connector receives a condensed summary per product;
the complete record always stays in the dataset, so nothing is lost if you leave this off.

- `mcpConnectors` - the MCP connectors to export this run's products into (Notion, Linear,
  Airtable, Apify). Leave it empty and nothing is exported.
- `notionParentPageUrl` - Notion connector only: the page under which one child page per product is
  created. Required when `mcpConnectors` includes Notion, ignored otherwise.
- `maxNotifyListings` - how many products are exported to each connector in one run, default 50.
  This caps the export only; it never changes what the dataset returns.

Export runs after the dataset is complete and is best-effort: if a connector is unreachable the
run still succeeds with a warning, and your products are still in the dataset.

### Plan Requirement & Notes

- Pay-per-event: one unit per returned record, plus an Actor Start event, plus
  a detail-enrichment unit for each catalog record enriched from its product
  document (only when the enrichment option is on).
- The read goes out in the store's own mobile-app request shape, which the
  storefront answers directly: no browser runs in a normal pass. If the
  storefront ever challenges that shape, the actor falls back for the rest of
  the run to a browser-cleared session on the same connection. A run needs
  the Apify proxy with a residential US exit. A storefront that answers
  neither way fails the run loudly - it is never reported as an empty search.
  The default 1 GB memory covers the direct read; if a run ever needs the
  browser fallback, raise the run memory to 2 GB for it.
- Detail documents (the fetchDetails option and product mode) are priced by the
  storefront's default location rather than the walked location; catalog-mode
  prices, promotions and stock are always the walked location's own.

# Actor input Schema

## `mode` (type: `string`):

Catalog walks a storefront location's collections page by page with prices, promotions and stock. Product mode reads one or more product links or numeric ids.

## `locations` (type: `array`):

Catalog mode: where to read the catalog - one entry per location. A US ZIP code (78701), a city or place name (Austin), or exact coordinates as lat,lng (30.2672,-97.7431). Names and ZIP codes are resolved automatically and cached.

## `locationId` (type: `string`):

Catalog mode: pin an exact Gopuff fulfillment location id and skip location resolution. Location ids appear in catalog records. Leave empty to resolve from the locations above.

## `categoryInputs` (type: `array`):

Catalog mode: walk only these collections, given as links (gopuff.com/c/candy/txERmYDy) or bare collection ids (txERmYDy). Deal pages work too (gopuff.com/deals/...). Leave empty to walk the whole navigation.

## `discoverCategories` (type: `boolean`):

Catalog mode: when no collections are set above, read the storefront navigation and walk every collection it lists.

## `inStockOnly` (type: `boolean`):

Catalog mode: hide items the selected location currently cannot deliver.

## `productInputs` (type: `array`):

Product mode: numeric product ids (198176) or any product link (gopuff.com/p/banana-1ct/p198176). Multi-value supported.

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

Stop after this many records (0 = unlimited). With several locations each gets an equal share.

## `maxPages` (type: `integer`):

Catalog mode: how many pages to walk inside each collection, 1 to 100. 0 (the default) walks every page the collection has, up to the ceiling of 100. A collection ends early when its pages run out.

## `pageSize` (type: `integer`):

Catalog mode: how many products to request per page, 10 to 50.

## `fetchDetails` (type: `boolean`):

Catalog mode: also read each product's own document for description, brand, SKU and image (billed as the detail-enrichment event, one unit per enriched record). Note: detail documents are priced by the storefront's default location, not by the walked location.

## `incrementalMode` (type: `boolean`):

Remember the previous run of the same search and label every record NEW, UPDATED, UNCHANGED or REAPPEARED; records that disappeared from a complete scan are reported EXPIRED. First run reports everything as NEW.

## `emitUnchanged` (type: `boolean`):

Incremental mode: also return (and bill) records that did not change since the previous run. Off keeps runs small.

## `emitExpired` (type: `boolean`):

Incremental mode: also return (and bill) records gone from a COMPLETE scan (every location walked to its end, nothing capped, nothing resumed). Partial scans never mark records expired.

## `stateKey` (type: `string`):

Incremental mode: name a monitoring campaign so two runs share a baseline even if their input text differs. Leave empty to derive it from mode + locations + collections + filters.

## `resumeFromRunId` (type: `string`):

Skip records already collected by an earlier run or dataset so an interrupted crawl can continue. Compatible with incremental mode.

## `mcpConnectors` (type: `array`):

Send every pushed record to these Apify MCP connectors (for example a Notion database) after the run. Leave empty to skip the export.

## `notionParentPageUrl` (type: `string`):

When a Notion connector is used above, create pages under this parent.

## `maxNotifyListings` (type: `integer`):

Upper bound on records sent to the connectors above.

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

The storefront read runs through the Apify proxy; the entry session and every page must share one residential exit in the US.

## Actor input object example

```json
{
  "mode": "categories",
  "locations": [
    "78701"
  ],
  "categoryInputs": [
    "https://www.gopuff.com/c/candy/txERmYDy"
  ],
  "discoverCategories": true,
  "inStockOnly": false,
  "productInputs": [
    "https://www.gopuff.com/p/banana---1ct/p198176"
  ],
  "maxItems": 20,
  "maxPages": 0,
  "pageSize": 50,
  "fetchDetails": false,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "maxNotifyListings": 50,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

# 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 = {
    "mode": "categories",
    "locations": [
        "78701"
    ],
    "categoryInputs": [
        "https://www.gopuff.com/c/candy/txERmYDy"
    ],
    "discoverCategories": true,
    "inStockOnly": false,
    "productInputs": [
        "https://www.gopuff.com/p/banana---1ct/p198176"
    ],
    "maxItems": 20,
    "maxPages": 0,
    "pageSize": 50,
    "fetchDetails": false,
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "maxNotifyListings": 50,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/gopuff-prices-assortment-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 = {
    "mode": "categories",
    "locations": ["78701"],
    "categoryInputs": ["https://www.gopuff.com/c/candy/txERmYDy"],
    "discoverCategories": True,
    "inStockOnly": False,
    "productInputs": ["https://www.gopuff.com/p/banana---1ct/p198176"],
    "maxItems": 20,
    "maxPages": 0,
    "pageSize": 50,
    "fetchDetails": False,
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "maxNotifyListings": 50,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/gopuff-prices-assortment-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 '{
  "mode": "categories",
  "locations": [
    "78701"
  ],
  "categoryInputs": [
    "https://www.gopuff.com/c/candy/txERmYDy"
  ],
  "discoverCategories": true,
  "inStockOnly": false,
  "productInputs": [
    "https://www.gopuff.com/p/banana---1ct/p198176"
  ],
  "maxItems": 20,
  "maxPages": 0,
  "pageSize": 50,
  "fetchDetails": false,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "maxNotifyListings": 50,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call abotapi/gopuff-prices-assortment-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/gopuff-prices-assortment-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/UQP2NC5z7AhYip2RB/builds/LB1jr6Agf7mFqezJy/openapi.json
