# Canadian Retail Price Tracker: Giant Tiger, Brick +70 Stores (`yugenox/canada-retail-price-tracker`) Actor

Prices, discounts, stock and barcodes from 74 Canadian retailers (Giant Tiger, The Brick, Leon's, Ardene, ALDO, SAIL, Mejuri…) or any Shopify store, always in CAD. Full catalogs, keyword search or collections, plus a changes feed: price drops, restocks, new and removed products.

- **URL**: https://apify.com/yugenox/canada-retail-price-tracker.md
- **Developed by:** [Yugenox Corp](https://apify.com/yugenox) (community)
- **Categories:** E-commerce, Automation, Integrations
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 product rows

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

## Canadian Retail Price Tracker

Track prices, discounts, stock and barcodes across **74 Canadian retailers** (Giant Tiger, The Brick, Leon's, Ardene, ALDO, SAIL, Mejuri, Vitamart and more) or **any Shopify store**. Prices are always in **Canadian dollars**. Get a store's full catalog, search by keyword or collection, or run it on a schedule and get **only what changed**: price drops, price increases, restocks, new products and removed products.

- 🇨🇦 **74 Canadian retailers ready to pick**, grouped by category. You can run a whole category, such as every footwear chain, in one go.
- 💲 **CAD prices, guaranteed.** Brands that sell in both countries return Canadian pricing, and any row that isn't in CAD is flagged.
- 🏷️ **Deal data.** Regular price, sale price, % off and savings on every product and every size/colour.
- 🔔 **Change feed.** Only the price drops, restocks and new arrivals since your last run. It's built for daily schedules and deal alerts.
- 🔢 **Barcodes (UPC/EAN) and SKUs** wherever the retailer publishes them, for price matching and arbitrage.
- 📦 **Full catalogs**, even for stores with 25,000+ products such as Giant Tiger.
- 🔍 **Precise search.** "boots" returns boots, not boot trays or hats.
- 🔗 **Any Shopify store.** Paste a domain, a collection link, a product link or a store search link.

No login and no API key are needed.

### Stores included

| Category | Retailers |
|---|---|
| Discount & general merchandise | Giant Tiger, Indigo, Mastermind Toys, Scholar's Choice |
| Furniture, home & sleep | The Brick, Leon's, Bouclair, Polysleep |
| Apparel | Ardene, Reitmans, Penningtons / Addition Elle, Suzy Shier, RW\&CO, Laura, La Senza, Oak + Fort, Knix, Sheertex, Encircled, Kit and Ace, Frank And Oak, Hatley, Stanfield's, Bench, Peace Collective, Province of Canada, Naked & Famous Denim, DUER |
| Footwear | ALDO, Call It Spring, Globo, Browns Shoes, Pajar, Kamik, Baffin, Cougar Shoes, Manitobah, Chaussures POP, Vessi |
| Outerwear, bags & jewellery | Moose Knuckles, Mackage, Rudsak, Herschel Supply, Mejuri, Reigning Champ, Tilley |
| Outdoor, sport & active | tentree, SAIL, Lolë, Ciele Athletics, SAXX, Pro Hockey Life, Bauer Hockey |
| Health, beauty & supplements | Vitamart, Supplement Source, Canadian Protein, Natural Factors, Jamieson Vitamins, Herbaland, Canvita, Greeniche, Saje Natural Wellness, Skinfix, Bushbalm |
| Food, coffee & tea | DAVIDsTEA, Kicking Horse Coffee, Detour Coffee, Balzac's Coffee, Pilot Coffee Roasters, Laura Secord |
| Pets | Global Pet Foods, Canada Pooch |
| Farm, hardware & tech | Peavey Mart, DroneDynamics |

Any other Shopify store (Canadian or not) works through **Other Shopify stores**, as long as it doesn't show a bot check to automated visitors. Stores behind a bot check (a Cloudflare "Just a moment" page, for example Aritzia, Decathlon or Staples) are not supported: this actor does not get around bot protection, so such a store is reported as "blocked" and skipped, and nothing is charged for it.

### How to use it

**Current prices (Products mode)**

1. Pick retailers under **Canadian retailers** or **Whole categories**, and/or paste store links under **Other Shopify stores**.
2. Optional: add **Search terms** (e.g. `winter jacket`) or **Collections** (e.g. `sale`, `clearance`). Leave both empty for the whole catalog.
3. Optional filters: **Only discounted items**, **Minimum discount (%)**, **Only in-stock items**, **Minimum/Maximum price**.
4. Choose **One row per**: *Product* (sizes/colours nested) or *Variant / SKU* (one row per size/colour with its own barcode, price and stock).

**Price-drop and restock alerts (Changes mode)**

1. Set **What to get** to *Changes since the last run*.
2. Pick your stores (and search terms or collections if you only want to watch part of a catalog).
3. The first run saves a baseline and returns no rows. Schedule the task (e.g. daily). Each later run returns only what changed: `new`, `price_drop`, `price_up`, `back_in_stock`, `out_of_stock`, `removed`, plus `new_variant` (a new size or colour of a product you already track) if you select it. Each row includes the previous price, the change in $ and %, and the current discount.
4. Use **Change types to report** to get, for example, only price drops. Combine it with **Minimum discount (%)** for a pure deal feed.

Runs with the same stores, search terms and collections share one history. Give a setup its own **Tracker name** to keep separate histories.

### Input example

```json
{
  "stores": ["gianttiger", "the-brick", "leons"],
  "searchTerms": ["air fryer", "mattress"],
  "onlyDiscounted": true,
  "maxItemsPerStore": 500,
  "outputLevel": "product"
}
```

Changes feed for two whole footwear chains, price drops of 30%+ only:

```json
{
  "stores": ["aldo", "globo"],
  "mode": "changes",
  "changeTypes": ["price_drop", "back_in_stock"],
  "minDiscountPct": 30
}
```

### Output

One row per product (or per variant). A real product row (shortened):

```json
{
  "store": "tentree",
  "storeName": "tentree",
  "storeDomain": "www.tentree.ca",
  "storeCity": "Vancouver",
  "storeProvince": "British Columbia",
  "productId": "8363172430010",
  "url": "https://www.tentree.ca/products/four-square-tank-undyed-green",
  "title": "Four Square Tank",
  "vendor": "tentree",
  "productType": "Womens",
  "tags": ["dirty-deals", "final-sale", "FW25-SS26"],
  "createdAt": "2026-03-26T20:53:51Z",
  "description": "A classic muscle tank for the win…",
  "imageUrl": "https://cdn.shopify.com/s/files/…/Tank-TCW6755-6604.jpg",
  "price": 32,
  "priceMax": 32,
  "compareAtPrice": 40,
  "discountPct": 20,
  "currency": "CAD",
  "available": true,
  "variantCount": 5,
  "availableVariantCount": 2,
  "options": { "Color": ["UNDYED GREEN"], "Size": ["XS", "S", "M", "L", "XL"] },
  "variants": [
    { "variantId": "45736526020794", "title": "UNDYED GREEN / S", "sku": "TCW6755-6604-S", "barcode": "661814852232",
      "price": 32, "compareAtPrice": 40, "discountPct": 20, "available": true, "weightGrams": 200 }
  ],
  "source": { "type": "collection", "value": "sale" },
  "scrapedAt": "2026-09-24T03:22:51.652Z"
}
```

A change row adds:

```json
{
  "changeType": "price_drop",
  "price": 12.98,
  "previousPrice": 17.98,
  "priceDelta": -5,
  "priceChangePct": -27.8,
  "previousAvailable": true,
  "firstSeenAt": "2026-09-20T03:10:42.000Z",
  "previousCheckAt": "2026-09-23T03:14:26.000Z",
  "changedVariants": [{ "variantId": "…", "sku": "…", "price": 12.98, "previousPrice": 17.98, "available": true }]
}
```

| Field | Meaning |
|---|---|
| `price` / `priceMax` | Lowest / highest current price across sizes and colours |
| `compareAtPrice` | Regular price when the item is on sale (empty when it isn't discounted) |
| `discountPct`, `savings` | % off and dollars off the regular price |
| `available` | Can be bought online right now |
| `sku`, `barcode` | Retailer SKU and UPC/EAN (barcode only when the retailer publishes it) |
| `currency`, `currencyMismatch` | Price currency. `currencyMismatch: true` appears only if a store can't price in the requested country's currency |
| `source` | What found the product: `catalog`, `search` (with the term), `collection` or `product` |
| `searchTruncated` | The store's search stopped at its 1,000-result limit, and this match was found through the extra catalog pass |

A per-store run summary (status, counts, notes) is saved as `SUMMARY` in the run's key-value store.

### Use cases

- **Deal and clearance alerts.** Schedule Changes mode on Giant Tiger, The Brick or whole categories and push price drops to Slack, email or a Google Sheet.
- **Competitor price monitoring.** Compare the same product types across Canadian chains, always in CAD.
- **Retail arbitrage and resale.** Use barcodes and SKUs with discount filters to find sourcing candidates.
- **Market and assortment research.** Get full catalogs with brands, types, tags, launch dates and stock depth (in-stock variants per product).
- **Price history.** Run on a schedule and append the results to your own database.

### FAQ

**Do I need an account, an API key or a login?** No. No login is needed: the actor reads the public product data every Shopify store publishes for its own website (the same prices and stock any visitor sees), and needs nothing from you except the stores and, if you like, search terms.

**How fast is it?** About 250 products per request. A 100-product search takes around 10 seconds. Giant Tiger's full catalog (about 27,000 products) took under two minutes in testing.

**Why are some Giant Tiger items missing a regular price?** Some retailers don't publish a "compare-at" price even on clearance, and mark sales with badges instead. Their tags still show it (e.g. `badge_en:Online Clearance`).

**Does it include items that are out of stock?** Yes, with `available: false`. Use **Only in-stock items** to skip them.

**Why did a store return 0 products for my search?** Strict matching requires every search word to appear in the product's title, type, brand or category tags. Plurals, accents and apostrophes are handled for you ("women's boots" also finds "Womens Boot" and "Women’s boots"), but some stores never write the words out. ALDO, for example, tags women's boots as `w-cas-boots`. When the store's own search finds products but none match strictly, the run's status message says so. Switch **Search matching** to *Broad* to get everything the store's own search returns.

**Why does the first Changes run return nothing?** It records the baseline. Changes are reported from the second run on. Set **Output everything on the first run** if you want the full list as `new` on day one.

**Can I track a whole huge catalog in Changes mode?** Mostly, yes. Giant Tiger (about 27,000 products) and The Brick (about 35,000) work at the default 2 GB of memory. Leon's and The Brick list each item once per store region, which adds up to hundreds of thousands of variants, and each extra GB of memory tracks about 460,000 more. Indigo lists millions of items (mostly books), so tracking its whole catalog is refused. Track Indigo with **Search terms** or **Collections** instead.

**What if a run is cut short?** Results collected so far are saved and the run still finishes successfully. In Changes mode, a store that wasn't fully checked keeps its previous history, so nothing is reported as removed and no change is lost. It is checked again next time.

**A store I added says "blocked", "not-shopify" or "disabled".** The run summary explains why. "blocked" means the store shows a bot check to automated visitors or refused every request; stores behind a bot check are not supported, because this actor does not get around bot protection. "not-shopify" means the site isn't a Shopify store. "disabled" means the store has switched off its public product data.

**Are prices really Canadian?** Prices are requested for Canada (change **Price country** for other markets). If a store can't sell in CAD, each row says so with `currencyMismatch: true`. Currencies are never silently mixed.

**Which proxy should I use?** Keep the default (Apify Proxy, datacenter). It comes with every Apify plan, including the free one, and almost every store answers it. If a store keeps refusing or rate-limiting datacenter IPs, the run retries it on Canadian residential proxies when your account has them, and carries on without them when it doesn't. If the proxy you picked isn't available on your account, the run uses datacenter instead and says so in its status message.

**What if my timeout is very short?** The run stops shortly before the timeout (20% of the run time, at most a minute) and saves what it has. If nothing came back in time, the status message says so: give searches at least 120 seconds, and whole catalogs or Changes runs several minutes.

**Is it legal to scrape these stores?** This Actor only collects publicly available data: product listings, prices, discounts, stock status and barcodes that each store publishes on its own website for any visitor, without an account. Collecting publicly available data is generally legal, but you're responsible for how you use it. The results contain no personal data: no customer, reviewer or employee information is collected. You must follow each store's terms and laws such as PIPEDA, GDPR and CCPA. If you're unsure, check with a lawyer. More on this: [Is web scraping legal?](https://blog.apify.com/is-web-scraping-legal/)

**Does it access any private data?** No. Everything comes from what each store shows to any visitor without logging in. It never uses a login, never touches private or restricted accounts, never reaches password-protected areas, and does not get around bot checks.

# Actor input Schema

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

Pick one or more of 74 Canadian retailers. Prices come back in CAD. For any other Shopify store, use "Other Shopify stores" below.

## `storeCategories` (type: `array`):

Add every preset retailer in these categories (e.g. all footwear chains) — handy for market-wide price comparisons.

## `customStoreUrls` (type: `array`):

Any Shopify store that does not show a bot check: a domain (shop.example.ca), a collection URL (…/collections/sale), a product URL (…/products/<handle>) or a store search URL (…/search?q=boots). A collection, product or search URL limits that store to just that page.

## `mode` (type: `string`):

Products: the current listing with prices, discounts and stock. Changes: only what changed since your previous run of the same setup — new products, price drops and rises, back in stock, out of stock and removed. Schedule it (e.g. daily) for a deal-alert feed. The first changes run for a store saves a baseline.

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

Only products matching these words, searched in every selected store. Leave empty for the whole catalog.

## `searchMatch` (type: `string`):

Strict: every word must appear in the product title, type, brand or category tags (precise). Broad: whatever the store's own search box returns (more results, many loosely related).

## `collectionHandles` (type: `array`):

Only products in these collections, e.g. "sale" or "clearance" (the part after /collections/ in the store URL). Applied to every selected store.

## `maxItemsPerStore` (type: `integer`):

Per store (and per search term / collection). 0 = everything. In Changes mode the whole catalog (or search/collection) is always checked; this setting is ignored.

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

Stop the whole run after this many results. Leave empty for no limit.

## `sortBy` (type: `string`):

Which products you get first when a limit applies. Search results are ranked by relevance unless you pick a price order. Applies when "Max products per store" is 5,000 or less and no filters are set.

## `outputLevel` (type: `string`):

Product: one row per product with all sizes/colours nested. Variant: one row per SKU (size/colour), with its own price, barcode and stock.

## `onlyDiscounted` (type: `boolean`):

Keep only items whose price is below the regular (compare-at) price.

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

Keep only items at least this much off the regular price, e.g. 40.

## `onlyInStock` (type: `boolean`):

Skip items that cannot be bought online right now.

## `minPrice` (type: `integer`):

In the store currency (CAD for Canadian stores).

## `maxPrice` (type: `integer`):

In the store currency (CAD for Canadian stores).

## `changeTypes` (type: `array`):

Changes mode only. Leave empty for everything except "New sizes / colours of known products", which you can add explicitly (it is noisy on stores that list every region as a variant, like Leon's and The Brick).

## `stateKey` (type: `string`):

Optional. Runs with the same name, stores and search terms / collections share one history. Use different names to keep separate trackers (e.g. "daily" and "weekly").

## `emitBaseline` (type: `boolean`):

On the first Changes run for a store, output every product as "new" instead of silently saving the baseline.

## `country` (type: `string`):

Prices are requested for this country (2-letter code). CA returns CAD prices even from stores that also sell in the US.

## `includeDescription` (type: `boolean`):

Turn off for smaller, faster datasets.

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

Requests in flight across the whole run.

## `escalateBlockedStores` (type: `boolean`):

If a store keeps refusing or rate-limiting datacenter IPs, retry it on Canadian residential proxies (only when your Apify account has them). Stores that show a bot check (a Cloudflare "Just a moment" page) are not supported and are reported as blocked.

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

Apify datacenter proxy is the default, comes with every Apify plan and works for almost every store; each request uses a new IP. If the proxy you pick is not available on your account, the run uses datacenter instead and says so.

## Actor input object example

```json
{
  "stores": [
    "gianttiger"
  ],
  "mode": "products",
  "searchTerms": [
    "winter jacket"
  ],
  "searchMatch": "strict",
  "maxItemsPerStore": 100,
  "sortBy": "newest",
  "outputLevel": "product",
  "onlyDiscounted": false,
  "onlyInStock": false,
  "emitBaseline": false,
  "country": "CA",
  "includeDescription": true,
  "maxConcurrency": 16,
  "escalateBlockedStores": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

Products, variants or change rows.

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

Per-store status, counts and notes.

## `report` (type: `string`):

RUN_REPORT: how the run ended, counts and proxy use.

# 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 = {
    "stores": [
        "gianttiger"
    ],
    "searchTerms": [
        "winter jacket"
    ],
    "maxItemsPerStore": 100,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("yugenox/canada-retail-price-tracker").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 = {
    "stores": ["gianttiger"],
    "searchTerms": ["winter jacket"],
    "maxItemsPerStore": 100,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("yugenox/canada-retail-price-tracker").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 '{
  "stores": [
    "gianttiger"
  ],
  "searchTerms": [
    "winter jacket"
  ],
  "maxItemsPerStore": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call yugenox/canada-retail-price-tracker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,yugenox/canada-retail-price-tracker"
        }
    }
}
```

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/9HhckhaEN07mJtX75/builds/a125tyS7KSaOXXfUG/openapi.json
