Looking for the overview — what this API returns, what it costs, and a call you can run without a key? See the Pick n Pay API page →
E-commerce & Marketplaces

Pick n Pay API & Scraper

Pick n Pay is one of South Africa's largest grocery chains, and every price on its online shop belongs to one store: the same 6 x 1 L UHT milk was R97.99 at Constantia (WC21), R104.99 at two other stores and R99.99 at a fourth on the same day, and 11 of 19 products shared by four stores carried different prices.

6 actionsLive JSON1,000 free credits$0.67–$1.50 / 1,000 creditsMCP-ready
Get a free keyOpen in playground

🤖 Using an AI assistant? Copy this link into ChatGPT / Claude / Cursor — it reads every endpoint and parameter instantly and tells you if this API fits your use case.

Every call therefore reads one store, chosen with store_id (without it the site's own default store is used and the answer says so). `search` returns products for a search term with the shelf price, was-price and saving, the Smart Shopper card price kept apart from the price, multi-buy and combo deals, price per kg for meat and produce sold by weight, stock status, rating and badges, plus the category, brand, certification and price facets. `category` lists a department or aisle, `promotions` lists everything on promotion at a store, `promotion` returns every product a combo or multi-buy covers, and `product` returns one product with every promotion and its dates, stock level, barcode, brand, description, allergens, certifications, the nutrition table and the single and case variants with their own prices. `stores` lists the 438 store codes of the online shop.

Reference

What a Pick n Pay field actually contains

Fields that read differently from how they look. Every row was measured against live responses on 2026-10-15.

FieldWhat it holds
store_id, store_defaultedThe store the prices belong to. "milk" counted 1,271 products at Constantia (WC21), 1,221 at GC11 and 946 at NC10; beef mince was R139.99/kg at WC21 and R119.99/kg at GG17. store_defaulted is true when you did not pass a store.
price, price_displayThe shelf price anyone pays at that store, in rand. For meat and produce sold by weight (product ids ending _KGM) it is the price per kg ("R139.99/kg") and price_unit is "kg".
member_priceThe Smart Shopper card price, never the shelf price: UHT milk 6 x 1 L shows price 97.99 and member_price 89.99. null when the product has no card price.
multi_buyA bulk deal apart from the single price: kind multi_buy ("2 For R34.00": quantity 2, price 34.00) or combo ("Combo For R49.99": a set of different items for one price; the promotion call lists the set). card_required says whether it needs Smart Shopper.
promotionsEvery promotion with its id, text, deal price, card requirement and start and end date. A search row carries the product's main promotion; product lists all of them (a loaf showed a combo in search and the combo plus a R17.99 Smart Shopper price on product).
original_price, savingsThe was-price and the saving when the product is on a price reduction. null, not 0, when there is no was-price.
weight_optionsFor goods sold by weight, the pack weights you can order (for example 900 g to 5 kg, with the weight range each one may come in).
stock_status, stock_levelin_stock, low_stock or out_of_stock at that store; product also gives the store's stock count (844 cases of one milk at WC21).
rating, review_countAs each surface prints it. The search row and the product record can disagree (5.0 from 11 reviews in search, 3.18 from 5 on the product the same minute), so each is returned as printed.
supplierThe company that supplies or owns the brand, as Pick n Pay names it: PICK N PAY for its own brand, COCA-COLA BEVERAGES SOUTH AFRICA, CLOVER, PEPSICO SNACK BRANDS and so on. Pick n Pay sells every product itself; the brand name is on the product record.
variantsThe pack variants of one product (single, case, pallet) with their own price and stock. Pallet units carry no price.

Pick n Pay does not publish a price per 100 g or 100 ml on its online shop, so none is returned; goods sold by weight carry their price per kg.

Live example

Real request and response JSON

Captured from the indexed primary action, search, on .

Captured request
{
  "method": "POST",
  "url": "https://api.reefapi.com/pnp/v1/search",
  "headers": {
    "x-api-key": "$REEF_KEY",
    "content-type": "application/json"
  },
  "body": {
    "query": "milk",
    "store_id": "WC21"
  }
}
Captured response
{
  "ok": true,
  "meta": {
    "api": "pnp",
    "endpoint": "search",
    "mode": "live",
    "latency_ms": 585.1,
    "record_count": 24,
    "bytes": 127748,
    "cache_hit": false,
    "total": 1270,
    "store_id": "WC21",
    "upstream_requests": 1,
    "charged_credits": 2,
    "version": "0.1.0",
    "request_id": "9f7c8dba5f164cd5",
    "queue_ms": 1.9,
    "fetched_at": "2026-10-15T17:02:57.865Z"
  },
  "data": {
    "query": "milk",
    "store_id": "WC21",
    "store_name": null,
    "store_defaulted": false,
    "total": 1270,
    "total_is_capped": false,
    "page": 1,
    "page_size": 24,
    "total_pages": 53,
    "sort": "relevance",
    "returned": 24,
    "sponsored_count": 0,
    "products": [
      {
        "rank": 1,
        "product_id": "000000000000349246_CS",
        "title": "PnP UHT Full Cream Milk 6 x 1L",
        "supplier": "PICK N PAY",
        "price": 97.99,
        "price_display": "R97.99",
        "price_type": "promotion",
        "price_unit": "each",
        "currency": "ZAR",
        "original_price": 99.99,
        "savings": 2,
        "online_only_price": false,
        "member_price": 89.99,
        "member_price_program": "Smart Shopper",
        "member_price_ends": "2026-10-18T21:59:59+0000",
        "multi_buy": null,
        "promotions": [
          {
            "promotion_id": "[trimmed-depth]",
            "type": "[trimmed-depth]",
            "card_required": "[trimmed-depth]",
            "text": "[trimmed-depth]",
            "description": "[trimmed-depth]",
            "kind": "[trimmed-depth]",
            "quantity": "[trimmed-depth]",
            "deal_price": "[trimmed-depth]",
            "starts": "[trimmed-depth]",
            "ends": "[trimmed-depth]",
            "validity_text": "[trimmed-depth]"
          }
        ],
        "sold_by": "unit",
        "weight_options": [],
        "average_weight": null,
        "stock_status": "in_stock",
        "in_stock": true,
        "purchasable": true,
        "max_order_quantity": 10,
        "rating": 5,
        "review_count": 5,
        "badges": [
          "SAVE",
          "SMART SHOPPER"
        ],
        "sponsored": false,
        "image": "https://cdn-prd-02.pnp.co.za/sys-master/images/hb4/he0/81649202331678/silo-product-image-v2-13Oct2026-060106-6001007164294-Straight_on-474524-606_400Wx400H",
        "url": "https://www.pnp.co.za/All-Products/Beverages/Long-Life-Milk/UHT-Milk/Full-Cream/pnp-uht-full-cream-milk-6-x-1l/p/000000000000349246_CS"
      },
      {
        "rank": 2,
        "product_id": "000000000000349246_EA",
        "title": "PnP UHT Full Cream Milk 1L",
        "supplier": "PICK N PAY",
        "price": 17.99,
        "price_display": "R17.99",
        "price_type": "selling",
        "price_unit": "each",
        "currency": "ZAR",
        "original_price": null,
        "savings": null,
        "online_only_price": false,
        "member_price": null,
        "member_price_program": null,
        "member_price_ends": null,
        "multi_buy": null,
        "promotions": [],
        "sold_by": "unit",
        "weight_options": [],
        "average_weight": null,
        "stock_status": "in_stock",
        "in_stock": true,
        "purchasable": true,
        "max_order_quantity": null,
        "rating": null,
        "review_count": 0,
        "badges": [],
        "sponsored": false,
        "image": "https://cdn-prd-02.pnp.co.za/sys-master/images/h8f/h2e/81798209929246/silo-product-image-v2-15Oct2026-091610-6001007041052-Straight_on-19006-64_400Wx400H",
        "url": "https://www.pnp.co.za/All-Products/Beverages/Long-Life-Milk/UHT-Milk/Full-Cream/pnp-uht-full-cream-milk-1l/p/000000000000349246_EA"
      },
      {
        "rank": 3,
        "product_id": "000000000000357781_EA",
        "title": "PnP Full Cream Fresh Milk 2L",
        "supplier": "PICK N PAY",
        "price": 32.99,
        "price_display": "R32.99",
        "price_type": "promotion",
        "price_unit": "each",
        "currency": "ZAR",
        "original_price": 34.99,
        "savings": 2,
        "online_only_price": false,
        "member_price": null,
        "member_price_program": null,
        "member_price_ends": null,
        "multi_buy": null,
        "promotions": [],
        "sold_by": "unit",
        "weight_options": [],
        "average_weight": null,
        "stock_status": "in_stock",
        "in_stock": true,
        "purchasable": true,
        "max_order_quantity": null,
        "rating": null,
        "review_count": 0,
        "badges": [
          "SAVE"
        ],
        "sponsored": false,
        "image": "https://cdn-prd-02.pnp.co.za/sys-master/images/ha6/hab/63177270558750/silo-product-image-v2-27Jun2026-060051-6001007162474-Straight_on-441005-30_400Wx400H",
        "url": "https://www.pnp.co.za/All-Products/Milk%2C-Dairy-%26-Eggs/Fresh-Milk-%26-Cream/Fresh-Milk/Full-Cream/pnp-full-cream-fresh-milk-2l/p/000000000000357781_EA"
      }
    ],
    "facets": {
      "categories": [
        {
          "name": "Milk, Dairy & Eggs",
          "count": 974,
          "category_id": "milk-dairy-and-eggs-423144840"
        },
        {
          "name": "Food Cupboard",
          "count": 200,
          "category_id": "food-cupboard-423144840"
        },
        {
          "name": "Beverages",
          "count": 168,
          "category_id": "beverages-423144840"
        }
      ],
      "certifications": [
        {
          "name": "Free Range",
          "count": 1
        },
        {
          "name": "Halaal",
          "count": 812
        },
        {
          "name": "Kosher",
          "count": 508
        }
      ],
      "on_promotion": [
        {
          "name": "On Promotion",
          "count": 333
        }
      ],
      "lifestyle": [
        {
          "name": "Dairy free",
          "count": 7
        },
        {
          "name": "Gluten Free",
          "count": 64
        },
        {
          "name": "Lactose free",
          "count": 13
        }
      ],
      "brands": [
        {
          "name": "ALMOND",
          "count": 7,
          "value": "ALMOND"
        },
        {
          "name": "AMAJOYA",
          "count": 4,
          "value": "AMAJOYA"
        },
        {
          "name": "AQUAFRESH",
          "count": 2,
          "value": "AQUAFRESH"
        }
      ],
      "unit_of_measure": [
        {
          "name": "Case",
          "count": 32
        },
        {
          "name": "Single",
          "count": 1174
        },
        {
          "name": "Pack",
          "count": 45
        }
      ],
      "price_ranges": [
        {
          "name": "R0-R49.99",
          "count": 725
        },
        {
          "name": "R50-R199.99",
          "count": 503
        },
        {
          "name": "R200-R499.99",
          "count": 39
        }
      ]
    },
    "breadcrumbs": []
  }
}
Actions

What the Pick n Pay API does

ActionDescriptionConcrete use caseKey params
searchSearch one Pick n Pay store's online shelf: shelf price, was-price, Smart Shopper card price (kept apart from the price), multi-buy / combo deal, price per kg for goods sold by weight, stock, rating and badges per product, plus the category / brand / certification / price facets. A row carries the product's primary promotion; `product` lists every promotion.Pricing teams call search to search one Pick n Pay store's online shelf.query, store_id, page, page_size, sort, ...
categoryEvery product in one category (department or aisle) at one store, paged, with the same rows, filters and sorts as search; the `categories` facet lists the sub-categories with their codes.Marketplace operators call category to get every product in one category (department or aisle) at one store, paged, with the same rows,….category_id, store_id, page, page_size, sort, ...
promotionsEverything on promotion at one store right now — sale prices, Smart Shopper card prices, multi-buys and combos — optionally inside one category or for one search term.Catalog enrichment teams call promotions to get everything on promotion at one store right now.store_id, query, category_id, page, page_size, ...
promotionOne promotion by the `promotion_id` a product row carries: every product it covers at one store (a combo's whole bundle, a multi-buy's qualifying items) with the promotion's text, deal price, card requirement and dates.Retail analysts call promotion to get one promotion by the `promotion_id` a product row carries.promotion_id, store_id, page, page_size
productOne product at one store by the `product_id` a search row returns: price, was-price, Smart Shopper price, multi-buy / combo, every promotion with dates, price per kg and allowed weights, stock level, barcode, brand, description, features, allergens, certifications, nutrition table, storage, pack variants (single / case) with their own price and stock, categories, images.Pricing teams call product to get one product at one store by the `product_id` a search row returns.product_id, store_id
storesThe Pick n Pay stores of the online shop with their store_id (for every other action): code, name, display name and a closed flag where the store's own name says it is closed. Narrow by `query`.Marketplace operators call stores to get the Pick n Pay stores of the online shop with their store_id (for every other action).query, include_closed, limit
Code samples

Call search from your stack

curl -X POST https://api.reefapi.com/pnp/v1/search \
  -H "x-api-key: $REEF_KEY" \
  -H "content-type: application/json" \
  -d '{"query":"milk","store_id":"WC21"}'
MCP one-liner
Ask your MCP-connected assistant: call reefapi.pnp.search with {"query":"milk","store_id":"WC21"}.
Use cases

Who uses this API and why

  • Compare Pick n Pay grocery prices store by store across South Africa.
  • Track Smart Shopper prices, multi-buys and combo deals at the stores you watch.
  • Monitor price per kg for meat and produce, and was-prices for real discounts.
  • Build a South African grocery product database with barcodes, allergens and nutrition tables.
  • Check stock levels of a product at a store before sending a shopper.
FAQ

Questions developers ask before integrating

Do I need a Pick n Pay or Smart Shopper account?

No. Everything is read logged out: the prices, Smart Shopper prices, deals and stock any visitor sees for that store. Personal Smart Shopper offers that need a login are not included.

Is the Smart Shopper price the price I get?

Only with the card. price is what anyone pays; member_price is the Smart Shopper card price. On 2026-10-15, 30 of 288 search rows carried a card price and 19 a multi-buy or combo.

Why does the price depend on the store?

Because Pick n Pay sets prices, deals, stock and even the range per store: 11 of 19 products shared by four stores had different prices on the same day. Pass a store_id from the stores call; without it the site's default store (Pick n Pay Constantia, WC21) is used and the answer says so.

Which stores are covered?

The 438 store codes of pnp.co.za, 386 of them not marked closed. Some stores have no online shop (wholesale and closed stores); a search there returns a clear not-found answer. The store list gives the code and name only; addresses and opening hours are not published there.

Which filters and sorts work?

Sort by relevance or price; filter by brand, category, on promotion, price range in rand and certification (halaal, kosher, free range). Each was checked against the unfiltered count: "milk" 1,271, Clover 125, Milk, Dairy & Eggs 975, on promotion 334, R20-R30 168, kosher 508.

Does the price filter use the Smart Shopper price?

No. min_price and max_price filter on the shelf price: for R20-R30, every returned row's price was inside the range.

How many products per page?

24 by default, up to 48. Past the last page the answer is an empty page with a note. A search for everything stops counting at 10,000 results.

How fresh is the data?

Every call reads the site live; nothing is cached. Promotions carry their own start and end dates.

What is the Pick n Pay API?

Pick n Pay API is a ReefAPI endpoint group for south african grocery prices per store from pick n pay: price in rand, smart shopper price, multi-buy and combo deals, stock and product facts. It returns live JSON through POST requests under /pnp/v1.

Is the Pick n Pay API free to try?

Yes. ReefAPI starts with 1,000 free credits, no card required. Pick n Pay calls use the same shared credit balance as every other ReefAPI engine.

Do I need a Pick n Pay login or account?

No login to Pick n Pay is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.

How fresh is the Pick n Pay data?

The page example is captured from a live search call, and production requests fetch live data through ReefAPI rather than a static sample.

How many credits does the Pick n Pay API use?

Pick n Pay actions currently cost 1-2 credits per successful call. Failed or blocked calls are free. All APIs draw from one credit pool.

Can I call Pick n Pay from an AI assistant or MCP client?

Yes. Connect ReefAPI once through MCP and your assistant can call pnp actions with the same key, credit pool and JSON envelope used by normal REST requests.

docs / pnp

Pick n Pay

South African grocery prices per store from Pick n Pay: price in rand, Smart Shopper price, multi-buy and combo deals, stock and product facts.

base /pnp/v16 endpoints
post/pnp/v1/category2 credits

Every product in one category (department or aisle) at one store, paged, with the same rows, filters and sorts as search; the `categories` facet lists the sub-categories with their codes.

ParameterAllowed / rangeDescription
category_idrequired—Category code as the `categories` facet or a product's `categories` list gives it (e.g. milk-dairy-and-eggs-423144840, cheese44805192).
store_idoptional—Pick n Pay store code from the `stores` action (e.g. WC21 Constantia, GC11, NC10). Prices, deals and the assortment are the store's own and differ between stores. Without it the site's own default store is used and the answer says so (store_defaulted: true).
page = 1optional1–500Page number, from 1. Past the last page the answer is an empty page.
page_size = 24optional1–48Products per page, 1-48 (default 24, the site's own page).
sort = relevanceoptionalrelevance · price_asc · price_descOrder of the results (the site's three sorts).
brandoptional—Only this brand, exactly as the `brands` facet of a search lists it (e.g. CLOVER, PNP). An unknown brand gives an empty result.
on_promotion = falseoptional—Only products with a current promotion (sale, Smart Shopper price, multi-buy, combo).
min_priceoptional0–Lowest shelf price in rand (the price anyone pays, not the Smart Shopper price; per kg for goods sold by weight).
max_priceoptional0–Highest shelf price in rand (same price as min_price).
certificationoptionalhalaal · kosher · free_rangeOnly products carrying this certification.
Try in playground →
post/pnp/v1/promotions2 credits

Everything on promotion at one store right now — sale prices, Smart Shopper card prices, multi-buys and combos — optionally inside one category or for one search term.

ParameterAllowed / rangeDescription
store_idoptional—Pick n Pay store code from the `stores` action (e.g. WC21 Constantia, GC11, NC10). Prices, deals and the assortment are the store's own and differ between stores. Without it the site's own default store is used and the answer says so (store_defaulted: true).
queryoptional—Only promotions whose product matches this search.
category_idoptional—Category code as the `categories` facet or a product's `categories` list gives it (e.g. milk-dairy-and-eggs-423144840, cheese44805192).
page = 1optional1–500Page number, from 1. Past the last page the answer is an empty page.
page_size = 24optional1–48Products per page, 1-48 (default 24, the site's own page).
sort = relevanceoptionalrelevance · price_asc · price_descOrder of the results (the site's three sorts).
brandoptional—Only this brand, exactly as the `brands` facet of a search lists it (e.g. CLOVER, PNP). An unknown brand gives an empty result.
Try in playground →
post/pnp/v1/promotion1 credit

One promotion by the `promotion_id` a product row carries: every product it covers at one store (a combo's whole bundle, a multi-buy's qualifying items) with the promotion's text, deal price, card requirement and dates.

ParameterAllowed / rangeDescription
promotion_idrequired—The `promotion_id` exactly as a product row's promotions[] gives it. Promotions run for days, so an id from a recent search is the one to use.
store_idoptional—Pick n Pay store code from the `stores` action (e.g. WC21 Constantia, GC11, NC10). Prices, deals and the assortment are the store's own and differ between stores. Without it the site's own default store is used and the answer says so (store_defaulted: true).
page = 1optional1–500Page number, from 1. Past the last page the answer is an empty page.
page_size = 24optional1–48Products per page, 1-48 (default 24, the site's own page).
Try in playground →
post/pnp/v1/product2 credits

One product at one store by the `product_id` a search row returns: price, was-price, Smart Shopper price, multi-buy / combo, every promotion with dates, price per kg and allowed weights, stock level, barcode, brand, description, features, allergens, certifications, nutrition table, storage, pack variants (single / case) with their own price and stock, categories, images.

ParameterAllowed / rangeDescription
product_idrequired—The `product_id` exactly as a search row returns it (e.g. 000000000000349246_EA, 000000000000251704_KGM), its short form 349246_EA, or a product URL ending /p/<code>.
store_idoptional—Pick n Pay store code from the `stores` action (e.g. WC21 Constantia, GC11, NC10). Prices, deals and the assortment are the store's own and differ between stores. Without it the site's own default store is used and the answer says so (store_defaulted: true).
Try in playground →
post/pnp/v1/stores2 credits

The Pick n Pay stores of the online shop with their store_id (for every other action): code, name, display name and a closed flag where the store's own name says it is closed. Narrow by `query`.

ParameterAllowed / rangeDescription
queryoptional—Stores whose code or name contains this text (case-insensitive).
include_closed = falseoptional—Also list stores whose own name marks them closed (default false).
limit = 500optional1–500At most this many stores.
Try in playground →
Built for volume
5M+ requests a day

Measured at 60 requests a second across the fleet, with no central bottleneck. Volume pricing is on request, and per-key limits are raised for high-volume accounts.

Missing a source?
We build it

Tell us a site we do not cover yet and it becomes an engine. A customer asked for bestprice.gr on a Sunday and it was in the catalog the next day.

Support
2 minute median reply

Median time from a question in the live chat to the first answer, measured across every answered conversation. Setup help included, no support tier to buy.

One key, one balance
Every API included

No per-site plans and no separate subscriptions. One key and one credit pool across the whole catalog, so adding a source costs nothing up front.

Planning something large? Tell us the volume and the sources and we will come back with what it costs and what we would have to build.