Canada Government Tenders - Aggregated & Normalized
Pricing
from $4.00 / 1,000 results
Canada Government Tenders - Aggregated & Normalized
Every open Canadian government tender - federal, provincial, and municipal - pulled live and normalized into one schema, with bid documents, amendment tracking, and procurement method. Delta mode turns it into a bid-alert feed.
Pricing
from $4.00 / 1,000 results
Rating
0.0
(0)
Developer
Goodland Consulting
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
1
Monthly active users
7 days ago
Last modified
Share
Canada Government Tenders — Aggregated & Normalized
The fastest way to track Canadian government tenders as they're published — federal, provincial, and municipal, pulled live on every run and normalized into one clean schema. No 40MB bilingual CSVs, no juggling seven different portal layouts.
Every run hits the source portals directly, so what you get is exactly what's published right now — not a cached snapshot from an intermediary database.
Do you love this Actor? Please leave a review! Found a bug, or need a source we don't cover yet? Please create an issue!
Need higher volume, a login-gated source (e.g. your own MERX account), or a delivery pipeline into your CRM/Slack? See "Need something custom?" below and get in touch — otherwise, read on.
What you get
- One schema, every portal. Federal, provincial, and municipal tenders come out with the same fields (
title,category,regions,closingDate,contactEmail, ...) no matter which source they came from. - Built-in filtering. Keyword, province, and procurement-category filters run before data ever reaches your dataset — see Filter behaviour by source for exactly what each source supports.
- Delta mode for alerts. Run it on a schedule and get only tenders you haven't seen before — turn this into an instant bid-alert pipeline via Apify's Slack, email, or webhook integrations.
- Resilient by design. Every source fails soft — a broken portal is logged and skipped, never fatal to the run, so one bad day on one site doesn't kill your data.
- Indigenous procurement flagging. Federal notices are checked against Canada's Indigenous set-aside program (PSIB/TIPS) and flagged automatically.
Sources covered
| Source | Coverage |
|---|---|
| CanadaBuys | All federal tenders — the official Government of Canada open data feed |
| SEAO | Quebec public sector, via Données Québec open data |
| Alberta Purchasing Connection | Alberta ministries and public entities |
| CivicInfo BC | BC local government — municipalities, regional districts, school districts |
| bids&tenders | Hundreds of Canadian municipalities, school boards, universities, and agencies — pick your organizations (see below) |
| SaskTenders | Saskatchewan ministries, crown corporations, municipalities, school divisions, health authorities |
| Nova Scotia | NS public sector including the MASH sector (municipalities, academic, school boards, health) |
Technical details
Freshness. CanadaBuys new-tender notices refresh every 2 hours (06:15–22:15 ET); its full open-tenders file refreshes daily (~07:00–08:30 ET). SEAO publishes weekly. Every other source is fetched live, straight off the portal, on each run.
Memory. Alberta, bids&tenders, Nova Scotia, and SaskTenders are more resource-intensive to fetch — give runs that include any of these sources at least 2048 MB. CanadaBuys, SEAO, and CivicInfo BC run fine on the default memory.
A source returned nothing — is it broken? Possibly something changed on the source's end. Sources fail soft (logged and skipped, never fatal to the run) — if a source looks stuck at 0 for a while, open an issue and we'll take a look.
Run order and maxItems. Sources are fetched in the order you list them in sources. Once the run has pushed maxItems tenders, remaining sources in the list are skipped entirely for that run — put your highest-priority source first if you're running a tight cap.
Expired notices. Dropped by default based on closingDate at run time (includeExpired: true to keep them). SEAO's weekly dumps in particular carry a lot of already-closed notices.
Filter behaviour by source
Not every source carries every field, so filters apply differently depending on where a tender came from:
- Keywords — matched case-insensitively against title + description on every source.
- Provinces — supported on every source. Sources that only ever cover one province (Alberta, CivicInfo BC, SaskTenders, Nova Scotia) are simply included or excluded based on whether that province is in your filter.
- Categories (
CNST/GD/SRV/SRVTGD) — only CanadaBuys and SEAO carry a real category code and are filtered by it. On other sources thecategoriesfilter has no effect — tenders come through regardless of category, andcategoryisnull(or a best-effort value on bids&tenders) in the output. newSinceHours— supported on every source. SEAO's data is published weekly, so short windows round up to the nearest week.
Choosing bids&tenders organizations
The bidsandtenders source works against any organization hosted at {org}.bidsandtenders.ca. Set bidsAndTendersOrgs to the subdomains you want; leave it empty for a diverse national starter set (Saskatoon, Metro Vancouver, TDSB, Yukon, Halifax, Red Deer).
| Province | Example orgs |
|---|---|
| Ontario | tdsb, durham, guelph, kitchener, waterloo, barrie, london, burlington |
| Alberta | reddeer, ae-ab |
| Saskatchewan | saskatoon, ae-sk |
| British Columbia | metrovancouver, bidcentral |
| Nova Scotia | halifax |
| Manitoba | mwsb (Manitoba Water Services Board) |
| Yukon | yukon |
| National co-ops | canoe (Canoe Procurement Group of Canada), bids |
To check whether an organization is on the platform, open https://{org}.bidsandtenders.ca/Module/Tenders/en — if it loads a listing, the subdomain works.
Indigenous procurement
Ottawa mandates a minimum 5% of federal contract value go to Indigenous business (the Procurement Strategy for Indigenous Business / Transformative Indigenous Procurement Strategy). This actor detects those set-asides automatically on CanadaBuys notices and flags them indigenousSetAside: true (false on other CanadaBuys notices; the field is omitted entirely on every other source, since they have no way to determine it). Set indigenousOnly: true to keep only flagged notices:
{"sources": ["canadabuys"],"indigenousOnly": true}
Example input
Construction tenders in the Prairies, published in the last day:
{"sources": ["canadabuys"],"categories": ["CNST"],"provinces": ["MB", "SK", "AB"],"newSinceHours": 24}
Turn it into a bid-alert pipeline
Set deltaMode: true and run this actor on a schedule (hourly, daily — your call). Each run only returns tenders it hasn't emitted before, so you can wire it straight into Apify's Slack, email, or webhook integrations for instant new-tender alerts:
{"sources": ["canadabuys", "seao", "bidsandtenders"],"keywords": ["paving", "excavation", "snow removal"],"deltaMode": true}
Input parameters
sources — array, default ["canadabuys"]. Which portals to pull from: canadabuys, seao, apc, civicinfobc, bidsandtenders, sasktenders, novascotia.
keywords — array, default []. Case-insensitive match against title and description, on every source. Empty matches everything.
provinces — array, default []. Only tenders deliverable in these provinces; Canada-wide tenders always match regardless of this filter. See Filter behaviour by source for how matching differs by source.
categories — array, default []. CNST (construction), GD (goods), SRV (services), SRVTGD (services related to goods). Only actually narrows CanadaBuys and SEAO — see the caveat above before relying on it for other sources.
newSinceHours — integer, default 0. Only tenders published in the last N hours; 0 returns all open tenders. Behaviour varies slightly by source (see above) — none of that changes the output shape, just how the window is applied.
deltaMode — boolean, default false. Skip tenders already emitted by a previous run of this actor (tracked by tender id, persisted between runs). Pair with an Apify schedule for a standing bid-alert feed.
maxItems — integer, default 1000. Hard cap on tenders pushed to the dataset in a single run, across all sources combined. Sources are processed in list order and stop being fetched once this is hit.
bidsAndTendersOrgs — array, default [] (national starter set). Org subdomains for the bidsandtenders source — see Choosing bids&tenders organizations.
includeExpired — boolean, default false. Keep notices whose closing date has already passed.
includeRaw — boolean, default false. Attach the original source record to each item as raw. Useful when a field looks off and you want to see exactly what the source portal sent.
indigenousOnly — boolean, default false. Keep only CanadaBuys notices detected as Indigenous set-asides. Pair with "sources": ["canadabuys"] — other sources produce nothing under this filter.
enrich — boolean, default true. Emit the deep record: bid documents, amendment tracking, procurement method, selection criteria, trade agreements, bilingual title and description, and the full contact block. Set it to false for a leaner, cheaper record — the tender enriched event isn't charged when it's off.
Pricing
Pay-per-event:
| Event | Price | Charged |
|---|---|---|
| Actor start | $0.15 | Once per run |
| Result | $0.004 | Per normalized tender returned |
| Tender enriched | $0.006 | On top of Result, for each record carrying the deep record (bid documents, amendment tracking, procurement method, selection criteria, trade agreements, bilingual text, full contact block). Not charged when enrich: false |
An enriched tender costs $0.01 all-in; a lean one (enrich: false) costs $0.004. A daily-scheduled delta feed of the federal source runs about $11/month — you only pay for tenders that are new or have changed since your last run.
Output schema
Every key is present on every record from every source. A field a source can't supply is null (or []) — never omitted. Ragged records break typed consumers, and in a CSV export an omitted key produces a column where "missing" and "false" look identical.
| Field | Type | Description |
|---|---|---|
id | text | Source-prefixed unique id, e.g. canadabuys:ws1234 |
source | text | canadabuys | seao | apc | civicinfobc | bidsandtenders | sasktenders | novascotia |
referenceNumber | text | null | The source's own notice id |
solicitationNumber | text | null | The number humans quote — distinct from referenceNumber. Your join key against email, CRM, and the portal itself |
title / titleFr | text | Tender title, English and French |
description / descriptionFr | text | Tender description, English and French (see source notes below) |
categories | text[] | CNST / GD / SRV / SRVTGD. Always an array — [] where the source has no category |
noticeType | text | null | e.g. RFP, RFI, ITT, ACAN |
procurementMethod | text | null | e.g. Competitive - Open bidding, sole-source. The bid/no-bid decision |
selectionCriteria | text | null | Lowest price vs best value |
tradeAgreements | text[] | CETA / CFTA / CPTPP / WTO-GPA — determines who is eligible to bid |
procurementEntity | text | null | The contracting organization |
endUserEntity | text | null | The end user of the contract, where distinct from the procuring entity |
contactName / contactEmail / contactPhone | text | null | Full contact block |
regions | text[] | Provinces where the work is delivered |
publicationDate | timestamp | null | ISO 8601 |
closingDate | timestamp | null | ISO 8601 |
expectedContractStartDate / expectedContractEndDate | timestamp | null | For capacity planning |
status | text | null | Normalized to Open across sources for active notices |
amendmentNumber | text | null | "000" = never amended. See "Amendments" below |
amendmentDate | timestamp | null | When the notice was last amended |
gsin / gsinDescription | text | null | Canadian commodity classification |
unspsc | text[] | null | UNSPSC commodity codes |
unspscDescription | text | null | Human-readable label for those codes |
documents | text[] | Links to the bid documents and addenda — the thing a bidder actually opens. Available on CanadaBuys and SEAO; [] elsewhere (see below) |
url | text | null | Link to the notice. Every CanadaBuys item resolves to a working URL — native notices link to a search pre-filled with the solicitation number |
indigenousSetAside | bool | null | true/false on CanadaBuys notices (detected PSIB/TIPS set-aside). null means the source can't determine it — which is not the same as "no" |
enriched | bool | Whether the deep record was populated (see enrich) |
scrapedAt | timestamp | When we saw this record. Needed for incremental sync |
raw | json | Original source record, only present when includeRaw: true |
SEAO's open data doesn't include full notice body text, so description for SEAO items is synthesized from structured fields (notice type, buyer, commodity labels, delivery region) and url links to the full notice on seao.gouv.qc.ca instead.
Field coverage varies by source. On CanadaBuys, measured against the live feed: solicitationNumber, amendmentNumber, procurementMethod, contactName and the French fields are ~100% populated; tradeAgreements 100%; selectionCriteria 81%; amendmentDate 71%; expectedContractStartDate 55%; contactPhone 42%; documents 41%; gsin 5%.
Bid documents
documents carries links to the actual bid documents and addenda on CanadaBuys and SEAO. It is [] on the other sources, and honestly so: bids&tenders keeps its bid documents behind a vendor login ("a free preview of the bid documents is available" — the rest needs an account). We don't authenticate to portals, so we don't have those files, and we'd rather say that than quietly ship an empty column and let you find out.
Amendments
Tenders get amended — closing dates move, scope changes, addenda land. 57% of open federal notices carry at least one amendment.
In deltaMode, a tender is re-emitted when it is new or when it has changed since we last sent it to you. Change is tracked by content fingerprint (the source's amendment counter where one exists, otherwise the title, closing date, and document set), so an amended notice reaches your alert pipeline instead of being silently swallowed as "already seen." An unchanged notice is never re-emitted.
Deploy a pre-filtered copy
Every filter can also be set as an actor environment variable (Actor Settings → Environment variables). Run input always overrides the environment, so you can deploy a narrowed copy of this actor — e.g. "Manitoba Construction Tenders" — that needs no input at all:
| Variable | Format | Example |
|---|---|---|
TENDERS_SOURCES | comma-separated | canadabuys,seao |
TENDERS_KEYWORDS | comma-separated | paving,excavation,snow removal |
TENDERS_PROVINCES | comma-separated codes | MB,SK,AB |
TENDERS_CATEGORIES | comma-separated | CNST,SRV |
TENDERS_NEW_SINCE_HOURS | integer | 24 |
TENDERS_MAX_ITEMS | integer | 500 |
TENDERS_DELTA_MODE | true/false | true |
TENDERS_INCLUDE_RAW | true/false | false |
TENDERS_BNT_ORGS | comma-separated subdomains | saskatoon,tdsb,halifax,mwsb |
TENDERS_INCLUDE_EXPIRED | true/false | false |
TENDERS_INDIGENOUS_ONLY | true/false | true |
FAQ
Wait, is this actually scraping live, or a pre-built database? Live. Every run fetches directly from each source portal you select, in real time. There's no intermediary database or caching layer; what you get is what's on the portal right now.
Where does the data come from? Federal data comes from the Government of Canada's official open data files under the Open Government Licence. Provincial and municipal sources are each portal's own public, unauthenticated data — no login-protected portal is accessed.
Can I get a province or portal that isn't listed? We're regularly expanding coverage. If there's a province or portal you need, get in touch — see "Need something custom?" below.
Can I access a login-gated source like MERX? Not through this public actor — MERX (used by Manitoba's government and Ontario's broader public sector, among others) is login-walled and its terms of service prohibit automated access. If you hold your own MERX credentials and want a private pipeline built against them, that's a custom engagement — see below.
How do I get a scheduled export as a file I can pull automatically?
-
Save your configured input as an Apify task.
-
Add a schedule to that task (e.g. hourly).
-
Pull the latest successful run's dataset from:
https://api.apify.com/v2/actor-tasks/TASK_ID/runs/last/dataset/items?token=YOUR_API_TOKEN&format=json&status=SUCCEEDEDReplace
TASK_IDwith your task's id andYOUR_API_TOKENwith your token from Settings → API & Integrations. Swapformat=jsonforcsv,xml,xlsx, or others — see the dataset items API docs.
Need something custom?
Custom work is available: additional provincial or MERX-account-based feeds, closing-date reminders, CRM/Slack delivery, or tender-to-capability matching against your service catalog. Contact us via Apify to discuss.
Legal note
Federal data comes from the Government of Canada's open data portal under the Open Government Licence. This actor does not access login-protected portals.