# Singapore Grocery Prices - FairPrice, Cold Storage, Sheng Siong (`locaihost/sg-supermarket-prices`) Actor

No login, no API key: supermarket product prices from FairPrice, Cold Storage and Sheng Siong in one run: price, promo price, promo text and end date, unit price per kg or litre, stock, brand, size and category. Track price changes between runs. $1 per 1,000 products.

- **URL**: https://apify.com/locaihost/sg-supermarket-prices.md
- **Developed by:** [locaihost data](https://apify.com/locaihost) (community)
- **Categories:** E-commerce, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.55 / 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.
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?

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

## Singapore Grocery Prices - FairPrice, Cold Storage, Sheng Siong

Product prices from Singapore's three big online supermarkets in one run: **FairPrice, Cold Storage and Sheng Siong**. Search by keyword, read whole categories, or check a list of product links. Every row has the regular price, the promo price and promo wording with its end date, a unit price per kg or litre, stock, brand, size and category, in the same columns for every store. Turn on price tracking and each row also says what the price was last time.

No login, no API key, no browser extension.

### Quick start

Paste this into the **JSON** input tab and click **Start**:

```json
{"searchTerms": ["milk"], "maxItemsPerQuery": 20}
```

You get the first 20 "milk" results from each of the three stores (60 rows) in a few seconds.

A daily price watch on a few items across all three stores:

```json
{"searchTerms": ["Marigold HL milk 1L", "Milo 1.5kg", "jasmine rice 5kg"], "maxItemsPerQuery": 10, "onlyChanges": true}
```

Save it as a task, add a daily schedule, and each run returns only products whose price or promotion moved, with the old price beside the new one.

### What you get

- **Three stores, one table.** FairPrice, Cold Storage and Sheng Siong rows share the same columns, so you can sort the same product across stores or stack weeks of runs in one sheet.
- **Real promotion data**: the regular price, the price now, the promo price for single-item price cuts, the promotion as the store words it ("Buy 2 for $3.50", "Any 2 @ $6.65", "10% off") and when it ends, where the store shows a date (FairPrice and Cold Storage do).
- **Unit price per kg, per litre or per piece**, worked out the same way for every store from the pack size ("6 x 200ml" is 1.2 L), so a 1.89 L bottle and a 6-pack compare fairly.
- **Price tracking** (`trackPrices`): each row adds `previousPrice`, `priceChange` and `previousSeenAt`. **Only changes** (`onlyChanges`) returns and charges only products that moved or are new.
- **Only new products** (`onlyNew`) for spotting launches in a category.
- **Barcodes** (EAN) on FairPrice rows, handy for matching the same product across stores.
- **Any link from the store's website**: category, brand and product pages, pasted in either box. Product links that are no longer on sale are listed in the status message and cost nothing.

### Sample row

```json
{
  "store": "fairprice",
  "storeName": "FairPrice",
  "productId": "13281144",
  "name": "[BCRS] Marigold HL Bottle Milk - Plain",
  "brand": "Marigold",
  "size": "1.89L",
  "currency": "SGD",
  "price": 6.97,
  "promoPrice": 6.55,
  "currentPrice": 6.55,
  "onPromotion": true,
  "promoText": "Buy 1 At $6.55",
  "promoEndDate": "2026-11-01T04:00:00+08:00",
  "unitPrice": 3.47,
  "unitPriceUnit": "L",
  "inStock": true,
  "previousPrice": 6.97,
  "priceChange": -0.42,
  "previousSeenAt": "2026-10-14T06:00:12.511Z",
  "categoryPath": "Dairy, Chilled & Eggs > Fresh Milk",
  "rating": null,
  "reviewCount": 0,
  "countryOfOrigin": "Singapore",
  "sku": "1622351",
  "barcode": "8888026240011",
  "imageUrl": "https://media.nedigital.sg/fairprice/fpol/media/images/product/XL/13281144_XL1_20260727T175327.jpg",
  "productUrl": "https://www.fairprice.com.sg/product/bcrs-marigold-hl-bottle-milk-plain-1-89l-13281144",
  "sourceType": "search",
  "source": "milk",
  "scrapedAt": "2026-10-15T06:00:09.204Z"
}
```

`previousPrice`, `priceChange` and `previousSeenAt` are filled when price tracking is on and the product was seen before; otherwise they are `null`.

### Price

**$1 per 1,000 products** on the Free and Starter plans ($0.75 on Scale, $0.55 on Business), plus $0.00005 per run start. You don't pay for Apify platform usage on top. A product is charged once per run even when two of your searches find it. Products skipped by `onlyChanges` or `onlyNew`, and product links that are gone, are free.

On Apify's free plan a run stops at 1,000 products. Any paid plan removes the cap.

### Input

| Field | What it does |
|---|---|
| `searchTerms` | Words to search in each store, e.g. `"milk"`, `"Milo"`, `"jasmine rice 5kg"` |
| `stores` | `fairprice`, `coldstorage`, `shengsiong`. Empty means all three. Applies to search terms; links always go to their own store |
| `categoryUrls` | Category or brand pages, e.g. `https://www.fairprice.com.sg/category/fresh-milk`, `https://coldstorage.com.sg/category/200300-beverages`, `https://shengsiong.com.sg/dairy-chilled-eggs` |
| `productUrls` | Product pages to check |
| `maxItemsPerQuery` | How many products to read from each search or category, per store (default 100; 0 = all the store lists) |
| `maxResults` | Stop after this many products in total (0 = no limit) |
| `trackPrices` | Remember prices between runs and report the change |
| `onlyChanges` | Return only products whose price or promotion changed, plus new ones |
| `onlyNew` | Return only products this setup never returned before |

Price memory belongs to a saved task. Without a task it belongs to the exact set of stores, search terms and links, so changing them starts a fresh history.

### Good to know

- **Prices are the online prices** each store shows a visitor who is not logged in, for home delivery. Member-only prices, vouchers and in-store prices can differ.
- **Promo prices:** `promoPrice` is set for single-item price cuts. Multi-buy deals ("Any 2 for $6.80") are in `promoText` with `promoPrice` left empty, because the price for one is unchanged.
- **Listing limits:** FairPrice shows at most 1,000 products for one search or category and Cold Storage at most 250 for one search. To cover a big aisle, use its sub-categories.
- **Fields that depend on the store:** FairPrice shows ratings, barcodes and country of origin in listings; Cold Storage and Sheng Siong show country of origin on product pages only (use product links). Sheng Siong does not show promotion end dates, and its search results carry no category (category links do).
- **Giant** no longer has an online shop and **RedMart** blocks logged-out requests, so neither is included.
- A run that hits its time limit stops cleanly, keeps what it read and says what to narrow.

### Use cases

- Daily price and promotion monitoring of your own products or a competitor's across the three chains.
- Grocery inflation and basket-cost research for Singapore.
- Deal and price-comparison apps, newsletters and alerts.
- Brand and category analysis: assortment, pack sizes, unit prices and promotion frequency.

### Lawful use

This Actor reads product and price information that the stores show publicly to any visitor, without logging in. It collects no personal data. Check that your use fits the stores' terms and the laws that apply to you, and don't republish product images you don't have rights to (rows link to them; they are not copied).

### Support

Found a wrong price, a missing field or a store that changed its site? Open an issue on the Issues tab and we'll look at it within a day.

Made by locaihost data.

### Did it help?

If this saved you time, a quick rating on the Store helps other people find it. Something not working? Open an issue on the Issues tab and we usually reply within a day.

# Actor input Schema

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

Words to search for in each store you pick below, e.g. "milk", "jasmine rice 5kg", "Milo". Each term is searched in every store.

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

Which supermarkets to search. Leave empty for all three. (Links below always go to the store they belong to.)

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

Category or brand pages copied from the store's website, e.g. https://www.fairprice.com.sg/category/fresh-milk, https://coldstorage.com.sg/category/200300-beverages or https://shengsiong.com.sg/dairy-chilled-eggs. Every product listed there is read, up to the limit below.

## `productUrls` (type: `array`):

Individual product pages to check, e.g. a list of the items you track. Works with fairprice.com.sg/product/..., coldstorage.com.sg/product/... and shengsiong.com.sg/product/... links. A product no longer on sale is listed in the run's status message and costs nothing.

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

How many products to read from each search term or category, per store, in the store's own order. 0 = everything the store lists (FairPrice shows up to 1,000 per listing, Cold Storage up to 250 per search).

## `maxResults` (type: `integer`):

Stop after this many products in total. 0 = no limit.

## `trackPrices` (type: `boolean`):

Remember each product's price from run to run, so every row also says the previous price, the change and when it was last seen. Remembered per saved task (or per set of search terms and links).

## `onlyChanges` (type: `boolean`):

Return (and pay for) only products whose price or promotion changed since the last run, plus products seen for the first time. Switches on price tracking. Ideal for a daily schedule.

## `onlyNew` (type: `boolean`):

Return only products this setup has never returned before, e.g. to spot new launches in a category.

## Actor input object example

```json
{
  "searchTerms": [
    "milk",
    "eggs",
    "jasmine rice",
    "Milo"
  ],
  "stores": [
    "fairprice",
    "coldstorage",
    "shengsiong"
  ],
  "categoryUrls": [
    "https://www.fairprice.com.sg/category/fresh-milk",
    "https://coldstorage.com.sg/category/200300-beverages",
    "https://shengsiong.com.sg/dairy-chilled-eggs"
  ],
  "productUrls": [
    "https://www.fairprice.com.sg/product/bcrs-marigold-hl-bottle-milk-plain-1-89l-13281144"
  ],
  "maxItemsPerQuery": 20,
  "maxResults": 0,
  "trackPrices": false,
  "onlyChanges": false,
  "onlyNew": false
}
```

# Actor output Schema

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

One row per product: price, promotion, unit price, stock and links.

## `priceChanges` (type: `string`):

Previous price and change for each product (when price tracking is on).

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

Rows per store, what each search found, anything skipped and the run's cost.

# 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"
    ],
    "stores": [
        "fairprice",
        "coldstorage",
        "shengsiong"
    ],
    "maxItemsPerQuery": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("locaihost/sg-supermarket-prices").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"],
    "stores": [
        "fairprice",
        "coldstorage",
        "shengsiong",
    ],
    "maxItemsPerQuery": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("locaihost/sg-supermarket-prices").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"
  ],
  "stores": [
    "fairprice",
    "coldstorage",
    "shengsiong"
  ],
  "maxItemsPerQuery": 20
}' |
apify call locaihost/sg-supermarket-prices --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,locaihost/sg-supermarket-prices"
        }
    }
}
```

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/AFHu9sZcaEilbzlAi/builds/mEoXKfZC6s7l2eWjL/openapi.json
