# Supermarket Price Scraper - Woolworths & Sainsbury's (`abdullahbilal-y/grocery-price-scraper`) Actor

Track grocery prices and discounts at Woolworths (Australia) and Sainsbury's (UK). Search any product term for price, previous price, discount %, unit price, pack size, barcode and stock. Filter to on-sale only - filtered rows are not charged. No login, proxy or browser.

- **URL**: https://apify.com/abdullahbilal-y/grocery-price-scraper.md
- **Developed by:** [Abdullah Bilal](https://apify.com/abdullahbilal-y) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

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

## Supermarket Price Scraper

### What does Supermarket Price Scraper do?

This Actor collects **live grocery prices and discounts from supermarket websites**. Give it product search terms — `milk`, `olive oil`, `nappies` — and it returns every matching product with its **current price, previous price, discount percentage, unit price, pack size, barcode, stock status, image and product URL**.

It currently covers **[Woolworths](https://www.woolworths.com.au) (Australia)** and **[Sainsbury's](https://www.sainsburys.co.uk) (United Kingdom)**, reading each chain's own public storefront data. There is **no login and no headless browser** — just structured JSON over plain HTTP, which keeps runs fast and cheap. Requests route through Apify Proxy, which is enabled by default and needs no configuration from you.

Running on Apify gives you a REST API, scheduled runs, webhooks, and exports to JSON, CSV, Excel and Google Sheets — so a daily price history is a scheduling setting rather than a project.

### Why use Supermarket Price Scraper?

- **Price monitoring** — track what competitors charge for the same products, every day.
- **Inflation and cost-of-living research** — build a real basket-of-goods time series instead of relying on published indices.
- **FMCG and brand analytics** — see where your products sit against own-brand alternatives, and when they get discounted.
- **Deal and cashback sites** — pull current markdowns with a single filtered run.
- **Unit-price comparison** — every row carries the per-litre or per-kilo price, which is what actually makes products comparable.

### How to use Supermarket Price Scraper

1. Click **Try for free**.
2. Add your **product search terms**, one per entry.
3. Pick your **retailers**, or leave the field as-is to search all of them.
4. Optional: tick **Only discounted products** to get just the markdowns.
5. Press **Start**, then download from the **Output** tab as JSON, CSV, Excel or HTML.

### Input

| Field | Description |
|---|---|
| `searchTerms` | Product terms to search. Each is run against every selected retailer. |
| `retailers` | `woolworths-au`, `sainsburys-uk`, or both. |
| `maxProductsPerTerm` | Cap per term per retailer. Pagination is automatic. |
| `onlyOnSale` | Return only products marked down from a previous price. |
| `minDiscountPct` | Drop anything discounted by less than this. |

```json
{
  "searchTerms": ["milk", "coffee", "olive oil"],
  "retailers": ["woolworths-au"],
  "onlyOnSale": true,
  "minDiscountPct": 20
}
```

### Output

One dataset item per product. Download as JSON, HTML, CSV, or Excel.

```json
{
  "retailer": "woolworths-au",
  "retailerName": "Woolworths (Australia)",
  "country": "AU",
  "currency": "AUD",
  "productId": "888140",
  "name": "Woolworths Full Cream Milk 3L",
  "brand": "Woolworths",
  "price": 4.95,
  "wasPrice": 5.5,
  "onSale": true,
  "discountPct": 10.0,
  "unitPrice": "$1.65 / 1L",
  "packageSize": "3L",
  "barcode": "9300633636999",
  "inStock": true,
  "url": "https://www.woolworths.com.au/shop/productdetails/888140",
  "searchTerm": "milk",
  "scrapedAt": "2026-09-20T14:00:00+00:00"
}
```

#### Data fields

| Field | Description |
|---|---|
| `retailer` / `retailerName` / `country` / `currency` | Which chain the row came from |
| `productId` | The retailer's own product code |
| `name` / `brand` / `packageSize` | Product identity |
| `price` / `wasPrice` | Current and previous price |
| `onSale` / `discountPct` | Whether it is marked down, and by how much |
| `unitPrice` | Price per litre, kilo or unit — the comparable figure |
| `barcode` | EAN / barcode where published |
| `inStock` | Current availability |
| `url` / `imageUrl` | Product page and image |
| `searchTerm` / `scrapedAt` | Which query produced the row, and when |

### How much does it cost to scrape supermarket prices?

Billing is **per product returned**. Because the Actor reads each retailer's own JSON over plain HTTP rather than driving a browser, compute per run is very low, and the Apify free tier covers trying it and running small jobs.

**Filtered rows are never charged.** If you set `onlyOnSale` or a minimum discount, the products that do not qualify are dropped before anything is written to the dataset.

### Tips

- **Batch your terms.** One run with 50 terms is cheaper and faster than 50 runs.
- **Schedule a daily run** on the same basket of terms to build a price history — that is where the real value is.
- **Use `unitPrice`, not `price`,** when comparing across pack sizes.
- **`onlyOnSale` plus `minDiscountPct`** is the cheapest way to watch for genuine markdowns.

### FAQ

**Which supermarkets are supported?**
Woolworths (AU) and Sainsbury's (UK) today. The Actor is built so new chains are straightforward to add — request one on the Issues tab and it will be considered.

**Why not Coles, Tesco or ASDA?**
Those chains actively block automated access to their storefronts. Rather than ship something that fails intermittently, only retailers that respond reliably are included.

**Do I need a proxy?**
It is already handled. Both retailers block Apify's server IPs directly, so runs go through Apify Proxy with the default datacenter groups. This is enabled out of the box - you do not need a residential proxy or any setup.

**Why is `wasPrice` sometimes empty?**
Not every retailer publishes a previous price, and a product that is not discounted has none. `onSale` will be `false` in that case.

### Legal and disclaimer

This Actor collects only **publicly available product information** — the prices and descriptions any shopper can see without logging in. It does not access customer accounts, orders or personal data.

You are responsible for how you use the data, including compliance with applicable law and each retailer's terms. Prices change constantly and may vary by store or delivery area; treat results as a point-in-time snapshot rather than a guaranteed quote.

### Support

Need another supermarket, another country, or an extra field? Open a ticket on the **Issues** tab — retailer requests genuinely shape what gets added next.

# Actor input Schema

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

What to look up, one term per entry — e.g. milk, bread, olive oil, nappies. Each term is searched at every selected retailer.

## `retailers` (type: `array`):

Which supermarkets to search. Leave empty to use all of them.

## `maxProductsPerTerm` (type: `integer`):

Upper limit on products returned for each search term at each retailer. Results are paginated automatically.

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

Return only products currently marked down from a previous price. Filtered products are removed before they reach your dataset, so you are not charged for them.

## `minDiscountPct` (type: `integer`):

Drop products discounted by less than this percentage. Also applied before billing.

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

Required, and already configured. Both retailers reject Apify's server IPs and most datacenter proxy IPs, so runs use residential proxies. Measured at under 1% of the cost of the results, and it is what takes reliability from 50% to 100%.

## Actor input object example

```json
{
  "searchTerms": [
    "milk",
    "bread"
  ],
  "retailers": [
    "woolworths-au",
    "sainsburys-uk"
  ],
  "maxProductsPerTerm": 100,
  "onlyOnSale": false,
  "minDiscountPct": 0,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `results` (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 = {
    "searchTerms": [
        "milk",
        "bread"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("abdullahbilal-y/grocery-price-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",
        "bread",
    ],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("abdullahbilal-y/grocery-price-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",
    "bread"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call abdullahbilal-y/grocery-price-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abdullahbilal-y/grocery-price-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/49wemirT3osD24iGm/builds/oceH9EkueWfA6fMle/openapi.json
