# Pick n Pay Scraper: Prices, Specials & Smart Shopper Deals (`zaiq/pick-n-pay-scraper`) Actor

Scrape Pick n Pay (pnp.co.za) products by search term, category or URL: prices, specials, Smart Shopper member prices with dates, promotions, barcodes, stock and unit prices, store by store. Compare prices across Pick n Pay stores. For SA grocery price monitoring.

- **URL**: https://apify.com/zaiq/pick-n-pay-scraper.md
- **Developed by:** [Zaiq](https://apify.com/zaiq) (community)
- **Categories:** E-commerce, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 products

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

## Pick n Pay Scraper: Prices, Specials & Smart Shopper Deals

Get product prices, specials and Smart Shopper member prices from Pick n Pay (pnp.co.za) as structured data, store by
store. Search by keyword, open a category or give product links; choose several Pick n Pay stores to compare their
prices and stock in one run. Every request goes through South African home internet connections.

### What it does

- Searches the catalogue like the site's search box, reads categories and single products.
- Returns, for every product: price, was-price, Smart Shopper member price with its start and end date, every
  promotion ("2 For R110", "Buy 1, Save 15%", combos) with dates, barcode, pack size, unit price per kg, litre or item,
  stock status, category path, rating and review count, image, product link and the store it was read for.
- Stores: Pick n Pay prices, promotions, ranging and stock differ by store. Pick stores from the list (Constantia,
  V\&A Waterfront, Rosebank, Menlyn, Umhlanga, Gqeberha and others) or give any store code or name; each product is
  returned once per store.
- Specials only: returns just the products on promotion (the site's "On Promotion" filter).
- Read each product's page (optional): adds brand, the exact stock level, the barcode from the product page, the
  description and more of the category path, at the same price per product.

### Who uses it

- **Price comparison and basket-tracking sites** following grocery prices and specials across South African chains.
- **FMCG brands and their agencies** checking shelf prices, Smart Shopper deals and stock of their products store by
  store.
- **Retail and economic analysts** tracking food prices by region, with unit prices.
- **Shoppers and community groups** watching specials on a list of everyday products.

### Example

Two products checked at five Pick n Pay stores on 4 October 2026 (product links as input, five stores chosen):

| Store | PnP UHT Full Cream Milk 6 x 1L | Smart Shopper price | Nescafe Gold Instant Coffee 200g | Smart Shopper price |
|---|---|---|---|---|
| WC21 Constantia (Cape Town) | R99.99 | R89.99 | R209.99 | R149.99 |
| GC12 Rosebank (Johannesburg) | R104.99 | R89.99 | R209.99 | R149.99 |
| NC54 Menlyn Mall (Pretoria) | R104.99 | R89.99 | R209.99 | R149.99 |
| KC26 Hyper Prospect (Durban North) | R94.99 | none | R209.99 | R149.99 |
| EC16 Hyper Moffet Park (Gqeberha) | R94.99 | R89.99 | R209.99 | R149.99 |

The six-pack of milk cost R10 more in Gauteng than in Durban and Gqeberha. The milk deal ran to Sunday 4 October and
the coffee deal to Tuesday 6 October; stock at the stores ranged from 171 to 1,600 six-packs.

Other test runs the same day, all through South African residential connections:

- 20 everyday search terms, 60 products each: 1,168 products (some searches have fewer) in 38 seconds, no failed or
  blocked requests.
- 350 products of one search ("chocolate", 2,844 on the site), paged without repeats.
- Product pages for 50 products: the barcode taken from the listing matched the product page's unit barcode for all 50.
- The same two searches at four stores, one of them given by name (Clearwater Mall): 320 products.

### Input

```json
{
  "searchTerms": ["milk", "coffee"],
  "startUrls": [{ "url": "https://www.pnp.co.za/c/coffee1750990951" }],
  "stores": ["WC21", "GC12", "KC26"],
  "maxItemsPerSource": 100,
  "specialsOnly": false,
  "includeDetails": false
}
```

| Field | What it does |
|---|---|
| `searchTerms` | Words to search for. |
| `startUrls` | Category (`/c/...` or any `.../c/...` link), search (`/search/milk`) or product (any `.../p/000000000000349246_EA` link), or a bare product code (`349246_EA`). |
| `stores` | Stores to read; empty means WC21 Constantia, the store pnp.co.za uses before a shopper sets an address. |
| `storeCodes` | Any other online store, by code (`GC53`) or name (`Clearwater Mall`), checked against the site's store list. |
| `maxItemsPerSource` | Products per search term or URL (default 100). A product link gives one. |
| `maxItems` | Products in total for the run; 0 means no total limit. |
| `specialsOnly` | Only products on promotion. |
| `includeDetails` | Also read each product's page (brand, exact stock level, description). |
| `sort` | `relevance` (default), `price_low` or `price_high`. |
| `proxyConfiguration`, `maxConcurrency` | Advanced: South African residential connections by default; requests at once (default 6). |

### Output

One row per product (per store when several are chosen). This row is from the example above (product page read):

```json
{
  "retailer": "Pick n Pay",
  "productId": "000000000000349246_CS",
  "sku": "349246_CS",
  "barcode": "6001007164294",
  "name": "PnP UHT Full Cream Milk 6 x 1L",
  "brand": "Pick n Pay",
  "size": "6 x 1 l",
  "sizeValue": 6.0,
  "sizeUnit": "l",
  "packCount": 6,
  "soldByWeight": false,
  "price": 104.99,
  "currency": "ZAR",
  "wasPrice": null,
  "promoPrice": null,
  "onSpecial": true,
  "savings": null,
  "unitPrice": 17.5,
  "unitPriceUnit": "l",
  "loyaltyPrice": 89.99,
  "loyaltyProgram": "Pick n Pay Smart Shopper",
  "loyaltyValidFrom": "2026-09-30T22:00:00Z",
  "loyaltyValidUntil": "2026-10-04T21:59:59Z",
  "promotions": [
    {
      "text": "R89.99",
      "type": "SMART_SHOPPER",
      "memberOnly": true,
      "promoUnitPrice": 89.99,
      "minQuantity": 1,
      "validFrom": "2026-09-30T22:00:00Z",
      "validUntil": "2026-10-04T21:59:59Z",
      "active": true,
      "validityText": "Valid from Thursday, 01 October 2026 until Sunday, 04 October 2026"
    }
  ],
  "inStock": true,
  "stockStatus": "In stock",
  "stockQuantity": 434,
  "category": "Beverages > Long Life Milk > UHT Milk > Full Cream",
  "categories": ["Beverages", "Long Life Milk", "UHT Milk", "Full Cream"],
  "imageUrl": "https://cdn-prd-02.pnp.co.za/sys-master/images/hfb/h57/47618416050206/silo-product-image-v2-03Apr2026-180118-6001007164294-Straight_on-420775-197_400Wx400H",
  "url": "https://www.pnp.co.za/pnp-uht-full-cream-milk-6-x-1l/p/000000000000349246_CS",
  "rating": 3.18,
  "reviewCount": 11,
  "seller": "Pick n Pay",
  "deliveryEstimate": null,
  "store": { "key": "GC12", "name": "GC12 Rosebank", "storeCode": "GC12" },
  "position": 1,
  "sourceType": "product",
  "sourceValue": "https://www.pnp.co.za/pnp-uht-full-cream-milk-6-x-1l/p/000000000000349246_CS",
  "scrapedAt": "2026-10-04T16:12:00Z",
  "supplier": "PICK N PAY",
  "maxPerOrder": 10,
  "detailsIncluded": true,
  "description": "Ensure you always have milk in your home with our Long Life Full Cream Milk. ...",
  "attributes": { "title": "Long Life Full Cream Milk", "storage": "Store in a cool, dry place and refrigerate only when opened." }
}
```

- `price` is the shelf price for everyone; `loyaltyPrice` is the best single-item Smart Shopper price that is valid
  now, with its dates; `promoPrice` is the best single-item promotion open to every shopper. Multi-buys and combos stay
  in `promotions` with `minQuantity` and the price per item they work out to (`promoUnitPrice`).
- `barcode` comes from the product photo's file name in listings (it matched the product page in all 50 products
  checked) and from the product page when Read each product's page is on.
- `stockQuantity` is filled when product pages are read; listings give `stockStatus` only.
- `brand` is filled when product pages are read; listings give the `supplier` (for example NESTLE SOUTH AFRICA).
- Weighed products are marked `soldByWeight` with the price per kg.
- All prices are in rand, as the site shows them, VAT included.

### Use it from code, automations and AI agents

- **API**: run it and get the results in one call: `POST https://api.apify.com/v2/acts/zaiq~pick-n-pay-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN` with the input as JSON. The API tab on this page has ready-made Python, JavaScript and cURL examples.
- **Python** (`pip install apify-client`):

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("zaiq/pick-n-pay-scraper").call(run_input={'searchTerms': ['milk', 'coffee'], 'startUrls': [{'url': 'https://www.pnp.co.za/c/coffee1750990951'}], 'maxItemsPerSource': 20})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

- **No code**: connect it to Google Sheets, Zapier, Make or n8n from the Integrations tab, schedule runs in Apify Console, and get a webhook when a run finishes.
- **AI agents (MCP)**: add it as a tool to Claude, Cursor or any MCP client through Apify's MCP server: `https://mcp.apify.com?tools=zaiq/pick-n-pay-scraper`.

### Pricing

$5.00 per 1,000 products ($0.005 per product row). Apify's platform usage is included. Reading product
pages does not change the price.

Each product row is one charged event; a product at several stores is one row per store. Failed, blocked and empty
pages, retries and duplicates are free. Your spending limit is respected: the run stops cleanly before going over it.

### Limits

- Prices, promotions and stock are what pnp.co.za shows for online orders from each store. In-store shelf prices can
  differ.
- Smart Shopper prices are the deals the site lists for everyone with a card; personalised offers inside a Smart
  Shopper account are not included, and nothing here signs in.
- A few stores in the site's list are closed or not set up for online orders; those are reported as input problems.
- Use the data in line with the retailer's terms and South African law. This Actor is not affiliated with or endorsed
  by Pick n Pay.

### FAQ

**Which store does it use if I choose none?** WC21 Constantia (Cape Town), the store pnp.co.za shows prices for until
a shopper sets a delivery address.

**How do I find a store code?** Choose from the list, or type the store's name in Other stores (for example
"Clearwater Mall"); the run looks it up in the site's own store list and says if a name matches several stores.

**Why is a Smart Shopper deal missing at one store?** Promotions are set per store. The example above shows the milk
deal running in four of five stores.

**Can I get only specials?** Yes, switch on Only products on special. Combine it with a category link to list one
aisle's specials.

# Actor input Schema

## `searchTerms` (type: `array`):

Words to search for, as you would type them on the site (for example milk, coffee, nappies). A barcode (EAN-13) also works.

## `startUrls` (type: `array`):

Pick n Pay pages: a category (https://www.pnp.co.za/c/coffee1750990951 or any .../c/... link), a search (https://www.pnp.co.za/search/milk) or a product (any .../p/000000000000349246_EA link). A bare product code (349246_EA) works too.

## `maxItemsPerSource` (type: `integer`):

Stop each search term or category after this many products. A product URL always gives one.

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

Stop the whole run after this many products. 0 means no total limit (each search term or URL still stops at its own maximum, and your spending limit always applies).

## `specialsOnly` (type: `boolean`):

Return only products the site marks as on promotion (a lower price, a member price or a multi-buy deal).

## `sort` (type: `string`):

The order the site lists products in. With a product limit, this decides which products you get.

## `stores` (type: `array`):

Prices, promotions and stock differ by store. Pick one or more; every product is returned once per store. Leave empty for WC21 Constantia, the store pnp.co.za uses before a shopper sets an address.

## `storeCodes` (type: `array`):

Any other Pick n Pay online store by code (for example GC53) or by name (for example Clearwater Mall). Names are checked against the site's own store list.

## `includeDetails` (type: `boolean`):

Adds brand, exact stock level, the unit barcode from the product page, description and category path. One extra request per product, so slower; the price per product is the same.

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

By default every request goes through South African home (residential) internet connections, as a shopper in South Africa would browse.

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

How many requests run at the same time. Kept low to stay polite to the site.

## Actor input object example

```json
{
  "searchTerms": [
    "milk",
    "coffee"
  ],
  "startUrls": [
    {
      "url": "https://www.pnp.co.za/c/coffee1750990951"
    }
  ],
  "maxItemsPerSource": 20,
  "maxItems": 0,
  "specialsOnly": false,
  "sort": "relevance",
  "stores": [],
  "storeCodes": [],
  "includeDetails": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "ZA"
  },
  "maxConcurrency": 6
}
```

# Actor output Schema

## `products` (type: `string`):

Dataset with one row per product.

## `summary` (type: `string`):

Products per search term or URL, results the site reported, errors, requests and approximate traffic.

# 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 = {
    "searchTerms": [
        "milk",
        "coffee"
    ],
    "startUrls": [
        {
            "url": "https://www.pnp.co.za/c/coffee1750990951"
        }
    ],
    "maxItemsPerSource": 20,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "ZA"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("zaiq/pick-n-pay-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 = {
    "searchTerms": [
        "milk",
        "coffee",
    ],
    "startUrls": [{ "url": "https://www.pnp.co.za/c/coffee1750990951" }],
    "maxItemsPerSource": 20,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "ZA",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("zaiq/pick-n-pay-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 '{
  "searchTerms": [
    "milk",
    "coffee"
  ],
  "startUrls": [
    {
      "url": "https://www.pnp.co.za/c/coffee1750990951"
    }
  ],
  "maxItemsPerSource": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "ZA"
  }
}' |
apify call zaiq/pick-n-pay-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zaiq/pick-n-pay-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/YRgJvxF69ZFtrYmVx/builds/IgpugsX1dPnC9Mg4v/openapi.json
