# Pak'nSave NZ Scraper — Products, Prices, Promotions (`architekture/paknsave-nz-scraper`) Actor

Extract store-scoped Pak’nSave NZ products, NZD prices, unit prices and promotions. No supermarket login. Saved continuation and 20 free lifetime previews of up to 10 rows. Paid runs: $0.15 per successful collection + $4 per 1,000 products.

- **URL**: https://apify.com/architekture/paknsave-nz-scraper.md
- **Developed by:** [architek.earth](https://apify.com/architekture) (community)
- **Categories:** AI, E-commerce
- **Stats:** 3 total users, 2 monthly users, 80.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.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

## Pak'nSave NZ Product & Price Scraper

**Recorded 16 October 2026: 3/3 collection checks succeeded. A free preview returned 10 products in 36.5 seconds; a paid search and its continuation returned 55 unique products with no overlap.** These are release-validation runs, not a historical uptime claim. Evidence is recorded in DEPLOY.md and BUILD15_VALIDATION.md.

From Architekture, makers of the Woolworths AU, Coles AU, ALDI AU and Tesco UK scrapers.

**20 free lifetime preview attempts. Paid collections: $0.15 per run that delivers products + $4 per 1,000 products.** Billing is USD; product prices are NZD.

### Why choose this actor?

- Compare numeric `price` and `pricePerUnit` without parsing currency strings.
- Keep observations tied to the requested `storeId`, with Royal Oak as the documented default.
- Deduplicate by `id` across overlapping queries and saved continuations.
- Inspect `SUMMARY` to distinguish complete, limited, partial and failed collections.
- Preserve source `promotions` without misrepresenting multi-buy rewards as individual prices.

### Worth knowing up front

**Prices are store-scoped.** Supply a public Pak'nSave store UUID for repeatable comparisons. Blank input selects Royal Oak (`e1925ea7-01bc-4358-ae7c-c6502da5ab12`). We verified different assortments between stores; overlapping sampled prices were identical. Price differences between stores have not been demonstrated. Output `storeId` records the requested store; the response does not independently echo it.

Challenge protection requires **real-browser warm-up and NZ residential proxy** before product requests. Startup uses compute and proxy traffic even for a small search; the collection fee covers this work. No supermarket login is required. Startup time and source availability vary.

Keyword search and the **Pantry category** are supported. Other category URLs are rejected. Some searches expose a 1,000-result ceiling. A successful run does not guarantee the whole catalogue was returned: check `SUMMARY` when time, spending or item limits are reached.

This release does **not** return ingredients, nutrition, barcodes, image URLs, verified stock quantities, membership prices or separate was-prices. `availability` is conservatively `unknown`; channel flags are not stock guarantees. Detail enrichment is disabled. Missing source values are omitted, never invented.

### Try the example

The recorded milk preview returned ten rows in 36.5 seconds; future run times are not guaranteed.

```json
{
  "operation": "collect",
  "searchTerms": ["milk"],
  "storeId": "e1925ea7-01bc-4358-ae7c-c6502da5ab12",
  "preview": true,
  "maxItemsPerQuery": 10,
  "maxRunSeconds": 60
}
```

Each account gets **20 lifetime preview attempts**, each limited to one page and ten rows across the whole run. Failed or uncertain reservations can consume an attempt. Quota exhaustion or a quota-service outage stops the run; it never switches to paid mode. Select `preview: false` explicitly for paid collection.

### Operations / input parameters

| Parameter | Type / default | Used by | Description |
|---|---|---|---|
| `operation` | string / `collect` | All | Use collect. Capture is a developer diagnostic, not a buyer operation. |
| `searchTerms` | string\[] / milk | Collect | Up to 100 product keywords. |
| `categoryUrls` | string\[] / empty | Collect | Supported: https://www.paknsave.co.nz/shop/category/pantry. Set searchTerms to \[] for category-only collection. |
| `storeId` | string / blank | Collect | Public store UUID. Blank selects Royal Oak. |
| `maxItemsPerQuery` | integer / 100 | Collect | Per-query cap across the continuation chain. 0 removes the item cap, not time or spending limits. |
| `includeDetails` | boolean / false | Collect | Leave false. True is rejected; enrichment is unsupported. |
| `preview` | boolean / true | Collect | Free preview; false explicitly enables paid mode. |
| `continuePreviousRun` | boolean / false | Paid collect | Resume an unconsumed checkpoint. |
| `previousStoreId` | string / blank | Continuation | Prior run's Apify key-value store ID, not retailer store ID. Requires read access. |
| `previousQueueId` | string / blank | Continuation | Queue ID from prior CHECKPOINT_META. Requires read/write access for the single-use claim. |
| `maxRunSeconds` | integer / 240 | Collect | Collection deadline, 60–3,600 seconds. Set platform timeout longer to allow checkpoint saving. |
| `proxyConfiguration` | object / residential NZ | Collect | NZ residential proxy required; included in listed price. |
| `debugSaveResponses` | boolean / false | Collect | Save sanitised last response for investigation. Review before sharing. |
| `feedback` | string / blank | All | Logged for human review; never changes output. |

Leave developer-only `captureCostMode` at `none`.

### Stable fields for automations

These top-level names and meanings are the supported contract. Optional values can be absent. New fields may be added; these fields will not be renamed. Product amounts are numeric **NZD**.

| Field | Type | Meaning |
|---|---|---|
| `id` | string | Required source product identifier and deduplication key. |
| `name` | string | Required product name. |
| `url` | string | Required product URL. |
| `storeId` | string | Required requested store UUID. |
| `source` | object | Query provenance: source=search with searchTerm, or source=category with categoryUrl. |
| `availability` | string | Currently unknown; no stock inference. |
| `brand` | string | Brand, when supplied. |
| `packageSize` | string | Source pack-size label, when supplied. |
| `price` | number | Individual product price, converted from source cents. |
| `pricePerUnit` | number | Unit price, converted from source cents. |
| `pricePerUnitLabel` | string | Source unit label, e.g. 1L. |
| `category` | string | First source category path, when supplied. |
| `availabilityChannels` | string\[] | Source sales channels, when supplied; not stock quantities. |

`promotions` is optional source passthrough. Treat its inner structure as a bonus, not a stable automation contract. Observed `rewardValue` values are in cents; multi-buy quantities and conditions matter. Do not substitute promotion rewards for `price`.

### Output examples

A real product row from run `edLZpbdBTgDx3AXKY`, recorded 16 October 2026:

```json
{
  "id": "5007415-EA-000",
  "name": "Zero Lacto Blue Milk",
  "url": "https://www.paknsave.co.nz/shop/product/5007415_ea_000pns",
  "storeId": "e1925ea7-01bc-4358-ae7c-c6502da5ab12",
  "source": {"source": "search", "searchTerm": "milk"},
  "availability": "unknown",
  "brand": "Anchor",
  "packageSize": "1l",
  "price": 5.11,
  "pricePerUnit": 5.11,
  "pricePerUnitLabel": "1L",
  "availabilityChannels": ["IN_STORE", "ONLINE"],
  "category": "Fridge, Deli & Eggs > Milk > Fresh Milk"
}
```

This is a dated observation, not a current-price promise. Search and category collection share this schema; `source` identifies how the row was found.

### Advanced usage

#### Monitor one store

Keep store and queries fixed, retain dated datasets and compare by `id`. Start with daily collection rather than repeated minute-by-minute runs. An assortment change is not proof of a price change.

#### Collect Pantry

Set `searchTerms: []` and `categoryUrls: ["https://www.paknsave.co.nz/shop/category/pantry"]`. Use bounded item and spending limits. Other category URLs are unsupported.

#### Continue a larger collection

Use paid mode with `continuePreviousRun: true`. Select the prior run's default key-value store as `previousStoreId` and its `CHECKPOINT_META.queueId` as `previousQueueId`. Grant only the requested storage access. Keep the store and query inputs unchanged; raise `maxItemsPerQuery` if its chain cap caused the stop. Buffered rows are delivered before new pages. Results go into the **new run's dataset**.

The recorded continuation returned 49 saved-page rows plus five newly fetched rows, without duplicates or overlap with the original row. Checkpoints are single-use. Inspect `SUMMARY` and pending-delivery markers before retrying; ambiguous delivery requires reconciliation. Catalogue changes between runs prevent a universal snapshot or exactly-once guarantee.

#### Join grocery datasets

Use `id` within this actor's observations. Do not assume IDs match other retailers. Cross-retailer joins need independently checked identifiers or product matching; this release does not provide barcodes.

### Integrations

- **Dataset exports:** download JSON, CSV or Excel. JSON preserves nested source and promotions.
- **Sheets, Make, Zapier or n8n:** retrieve the completed dataset through Apify's integrations or API. Bind to stable fields and check SUMMARY before updating downstream records.
- **Webhooks:** trigger retrieval after run completion; a successful run may still be limited.
- **Apify Scheduler:** schedule a saved paid task with an explicit store and spending limit. Previews are a lifetime allowance, not a renewable scheduled plan.

These are integration routes, not claims that every third-party workflow has been tested with this actor.

### Pricing

**$0.15 per successful paid collection + $4 per 1,000 delivered products. Platform usage is included.** Charges are USD.

| Event | Price | What it covers |
|---|---:|---|
| `product` | $4.00 / 1,000 | One event per product saved to the dataset. |
| `collection-delivered` | $0.15 / run | Once after the first paid product is saved, including each continuation run. |
| Free preview | $0 | 20 lifetime attempts; one page and up to ten products per attempt. |

One paid product costs **$0.154**; ten cost **$0.19**; 100 cost **$0.55** in one run. Each productive continuation incurs another collection fee. Batching queries avoids separate startup fees; overlapping products are deduplicated within a run or continuation chain.

**Nothing is charged for failed requests.** Zero-output runs incur no product or collection events. Products saved before a later failure still incur their product charges and the single collection fee. A paid spending allowance below $0.154 is rejected before proxy setup; this is an allowance check, not an automatic minimum bill. Previews have no paid fallback.

### FAQ

**Do I need a Pak'nSave account?** No. Verified collection uses an anonymous application session. An Apify account is required.

**Are these nationwide prices?** No. Requests are store-scoped. We verified assortment differences, not different prices for overlapping samples. Keep a fixed store for comparisons.

**Does it return Clubcard, was-prices or member-only discounts?** No verified mapping exists for these fields. Available offers are preserved in promotions without inventing membership prices.

**Can I get ingredients, nutrition, barcodes or images?** Not in this release. includeDetails=true is rejected and no detail event is charged.

**Will I get every product?** No such guarantee. Source visibility, its result ceiling, time, spending limits and catalogue changes can limit collection. Check SUMMARY.

**What happens after 20 previews?** Preview mode stops. Choose paid mode explicitly to continue. Failed or uncertain reservations can count towards the allowance.

**Can I replay a checkpoint or resume a preview?** No. Continuation is paid-only and checkpoints are single-use. Use the latest run's storage records for the next continuation. Contact support about ambiguous pending delivery rather than replaying blindly.

**Why a collection fee?** Browser startup and residential traffic establish an anonymous session. The fee applies only when a paid run delivers its first product, not when a zero-output attempt fails.

### For AI agents & LLM apps

Use `architekture/paknsave-nz-scraper` (actor ID `E7L3iA9flfLiUJ7Iz`) for store-scoped Pak'nSave product and price workflows without a supermarket login. Start with preview=true for one page and up to ten rows within the 20-attempt lifetime allowance; paid runs cost $0.15 plus $0.004 per delivered product, including a new fee for each productive continuation. Read SUMMARY before treating output as complete: only Pantry category collection is supported, source caps apply, and membership prices, nutrition, barcodes and stock quantities are unavailable. Obtain a token at https://console.apify.com/settings/integrations and read `GET https://api.apify.com/v2/acts/E7L3iA9flfLiUJ7Iz/builds/default` with bearer authentication for inputSchema, actorDefinition.storages.dataset and readme; put human-readable requests in feedback, which never changes results.

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("E7L3iA9flfLiUJ7Iz").call(run_input={
    "operation": "collect",
    "searchTerms": ["milk"],
    "storeId": "e1925ea7-01bc-4358-ae7c-c6502da5ab12",
    "preview": True,
    "maxItemsPerQuery": 10,
    "maxRunSeconds": 60,
    "feedback": "Testing a store-price monitoring workflow"
})
summary = client.key_value_store(run["defaultKeyValueStoreId"]).get_record("SUMMARY")
print(summary["value"])
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(row)
```

### More Architekture scrapers

- **NZ groceries:** Pak'nSave NZ — **you are here**. Woolworths NZ and New World NZ are planned cluster companions; availability is not promised here.
- **Australian groceries:** [Woolworths AU](https://apify.com/architekture/woolworths-au-product-scraper), [Coles AU](https://apify.com/architekture/coles-au-product-scraper), [ALDI AU](https://apify.com/architekture/aldi-au-product-scraper).
- **UK groceries:** [Tesco UK](https://apify.com/architekture/tesco-uk-scraper), [Sainsbury's UK](https://apify.com/architekture/sainsburys-uk-scraper).

### Support

Open an Issue on this actor's page with the run ID, expected result and what happened. A human replies within a day. Do not include tokens or private credentials. Optional feedback input is also read by a human.

### Legal compliance

Public product data only. This actor is not affiliated with Pak'nSave or Foodstuffs. Users are responsible for applicable site terms, copyright and privacy obligations, including GDPR or CCPA where relevant.

### Changelog

- **2026-10-16:** Added actionable failed-run status messages for insufficient allowance, exhausted previews and rejected continuation. Failure summaries retain the stage, error type and partial row counts without exposing transport credentials.
- **2026-10-16:** Verified free preview, product/collection billing and continuation across a newly fetched page. Added zero-charge checks for insufficient allowance and consumed-checkpoint replay.
- **2026-10-16:** Verified anonymous product API, cent-to-NZD mapping, public store selection and Pantry filtering against saved real responses. Documented unsupported fields and source limits.
- **2026-10-16:** Prepared listing, priced examples and stable-field documentation from recorded runs; no production uptime or customer-demand claim.

# Actor input Schema

## `operation` (type: `string`):

Collect public product rows. Capture is a developer-only diagnostic.

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

Product keywords, e.g. milk or weet-bix. Up to100 terms.

## `categoryUrls` (type: `array`):

Verified category: https://www.paknsave.co.nz/shop/category/pantry. Other categories are rejected before collection.

## `storeId` (type: `string`):

Public store UUID. Blank collection input selects Royal Oak (e1925ea7-01bc-4358-ae7c-c6502da5ab12). Output records the requested store; the response does not echo it.

## `maxItemsPerQuery` (type: `integer`):

Per term/category cap across a continuation chain. 0 means no item cap; time and budget limits remain.

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

Not enabled: no additional detail fields verified. True stops before collection; no detail event exists.

## `preview` (type: `boolean`):

20 lifetime attempts per account; one page and up to10 rows across the entire run. No paid fallback.

## `continuePreviousRun` (type: `boolean`):

Paid only. Claims a saved checkpoint once. Increase query cap to consume previously buffered results.

## `previousStoreId` (type: `string`):

Previous run's default key-value store; contains CHECKPOINT and CHECKPOINT_META. Not a retailer store ID.

## `maxRunSeconds` (type: `integer`):

Total collection/warm-up deadline; leaves checkpoint where safe.

## `debugSaveResponses` (type: `boolean`):

Private evidence preparation. Saves last source JSON with key-based secret redaction and URL query stripping; manually review before sharing.

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

Required residential NZ proxy. One sticky endpoint per clearance; changes on challenge recurrence.

## `feedback` (type: `string`):

Tell the maintainers what you needed and did not get. Read by a human; does not change the output.

## `captureCostMode` (type: `string`):

Developer-only matched cost experiment. Warmup stops after anonymous API bootstrap; representative requests up to20 Pantry pages. No paid rows.

## `previousQueueId` (type: `string`):

Queue ID from the previous run CHECKPOINT_META record. Needed only for continuation; it prevents replay of the same checkpoint.

## Actor input object example

```json
{
  "operation": "collect",
  "searchTerms": [
    "milk"
  ],
  "categoryUrls": [],
  "maxItemsPerQuery": 100,
  "includeDetails": false,
  "preview": true,
  "continuePreviousRun": false,
  "maxRunSeconds": 240,
  "debugSaveResponses": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "NZ"
  },
  "captureCostMode": "none"
}
```

# Actor output Schema

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

No description

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

No description

## `checkpoint` (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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("architekture/paknsave-nz-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("architekture/paknsave-nz-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 '{}' |
apify call architekture/paknsave-nz-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,architekture/paknsave-nz-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/E7L3iA9flfLiUJ7Iz/builds/Q7TAxTgfpPjMTKLMB/openapi.json
