- T&T
- Web scraping
- Price data
- Apify
How to Scrape T&T Supermarket Prices
T&T is Canada’s largest Asian grocery chain, and its catalogue barely overlaps with the Loblaw and Pattison banners. That makes it the interesting one to scrape, and also the one with the most caveats.
Savvi4 min read
Start here
No banner to choose here, since there’s only one chain. Paste this into the Input tab of the T&T Scraper API, switch to JSON view, and hit Start.
{
"search_terms": ["eggs", "milk", "chicken breast"],
"postal_code": "V5X 0C4"
}Same run over HTTP:
curl -X POST \
"https://api.apify.com/v2/acts/sunny_eternity~tnt-grocery-scraper/run-sync-get-dataset-items?token=YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"search_terms": ["eggs", "milk"], "postal_code": "V5X 0C4"}'Swap search_terms for categories to walk a section instead, like ["produce/fruits", "dairy-eggs"].
Read these three first
I’d rather you know these before you build something on the data than find out afterwards.
- Prices don’t vary by store. T&T serves one catalogue chain-wide.
postal_codeandlocationIdlabel each row with the resolved store, but the product data is the same across the chain. The Loblaw and Pattison Actors do vary prices by store; this one can’t, because T&T doesn’t. - Sale data is absent.
was_priceis alwaysnullandis_on_salealwaysfalsein this version. The fields exist so rows stay schema-compatible with the other Actors, not because they carry anything. If your project needs promotions, that gap matters. comparable_unit_priceis null a lot. T&T sells much of its produce by weight with no package size in the title, so there’s nothing to normalize. You getnullinstead of a guess. This shows up far more here than on the other Actors.
What comes back
{ "store": "T&T Supermarket", "name": "Seedless Green Grape (~2.5lb)", "price": "4.99", "unit_price": "per lb", "location_name": "Marine Gateway", // nearest store from postal code "selling_type": "by_weight", // by_weight - sold per pound "was_price": null, // T&T promos not exposed yet "is_on_sale": false, … 8 more fields}
That grape row is the null case in the wild: sold by the pound, no size in the title, so package_size and comparable_unit_price both come back empty while price and unit_price are perfectly good.
From Python
pip install apify-client, then:
from apify_client import ApifyClient
client = ApifyClient("YOUR_API_TOKEN")
run = client.actor("sunny_eternity/tnt-grocery-scraper").call(run_input={
"search_terms": ["bok choy", "shiitake", "gochujang"],
"postal_code": "V5X 0C4",
})
rows = list(client.dataset(run["defaultDatasetId"]).iterate_items())
# Keep only rows you can actually compare on unit price.
comparable = [r for r in rows if r.get("comparable_unit_price") is not None]
print(len(comparable), "of", len(rows), "rows have a unit price")From JavaScript
npm i apify-client, then:
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_API_TOKEN' });
const run = await client.actor('sunny_eternity/tnt-grocery-scraper').call({
search_terms: ['bok choy', 'shiitake', 'gochujang'],
postal_code: 'V5X 0C4',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
const comparable = items.filter((i) => i.comparable_unit_price !== null);Stores and coverage
Stores resolve from T&T’s own live pickup-locations API, so a location that opened last month works with no update to the Actor. Pass locationId to pin one, like "MGFS" for Marine Gateway in Vancouver. Since the catalogue is chain-wide, that changes the label on each row rather than the prices.
| Provinces covered | Behaviour outside them |
|---|---|
| BC, Alberta, Ontario, Quebec | A postal code outside those four returns an error naming them, rather than quietly resolving a store on the far side of the country. |
To price T&T against the Loblaw and Pattison banners on shared staples in one run, use the comparison API, which includes tnt by default.
Frequently asked questions
Which provinces does T&T cover?
British Columbia, Alberta, Ontario and Quebec. A postal code outside those four returns an error naming them, rather than a store from the far side of the country.
Does the Actor return store-specific prices?
No. T&T serves one catalogue chain-wide, so postal_code and locationId label each row with the resolved store while the product data stays the same across the chain. The Loblaw and Pattison Actors do vary prices by store.
Are sale prices included?
This version leaves them out. was_price is always null and is_on_sale always false. The fields exist so rows stay schema-compatible with the other Actors in the suite.
Why is comparable_unit_price null on so many rows?
T&T sells a lot of produce by weight with no package size in the title. Where a size won’t parse, you get null instead of a guessed number. It shows up more here than on the other Actors.
Is there an official T&T API?
No. T&T publishes no public product or pricing API.
How do I pin a specific store?
Pass locationId instead of postal_code. MGFS is Marine Gateway in Vancouver. Since the catalogue is chain-wide, this changes the label on each row rather than the prices.
How do I track prices over time?
Schedule the Actor and diff price by product_id. No store dimension needed, since T&T runs one catalogue chain-wide, which makes this the simplest of the five to build a history on.
Is scraping T&T legal?
Public product and price information is generally legal to scrape. T&T’s terms of service and your local law are still your responsibility. The catalogue is chain-wide, so there’s no reason to run the same query once per store — one run covers the chain.
Try it on your own data
The T&T Scraper API runs on Apify. Paste an input, hit Start, and see what comes back before you write any code against it.
Get the T&T Scraper APIOther retailers
- Canadian Grocery Price Comparison API16 chains matched, scored and ranked in a single call.
- Loblaws, No Frills & Superstore API12 Loblaw banners with PC Optimum offers and multi-buy deals.
- Save-On-Foods & PriceSmart APIFour Pattison Food Group chains across BC and the prairies.
- Costco Scraper APIWarehouse prices, deals and item numbers from costco.ca and costco.com.
They all share the same field names. See them side by side on the grocery data API page, with a sample record for each.
Keep reading
- How to Compare Grocery Prices Across 16 Canadian Chains in One CallThis one isn’t a scraper, it’s a matcher. How the confidence scoring works, how to filter on it, and how to get basket totals per store.
- How to Scrape Loblaws, Superstore and No Frills PricesTwelve banners share one input and one output shape. Which banner covers your region, the promo fields only this chain exposes, and how to join across banners.