DEV Community

Vadim Zabin
Vadim Zabin

Posted on

How to Get Canadian Government Tenders by API (2026 Guide)

Canada's governments buy over $200 billion in goods and services every year. Every tender is public — but the data is scattered across a dozen portals. Here is how to get it programmatically in 2026: every official source, their APIs and formats, and a working setup for automated monitoring.

The problem: one country, a dozen portals

If you sell to the Canadian public sector — or build tools for companies that do — you quickly discover there is no single API for Canadian tenders:

  • Federal tenders live on CanadaBuys, the Government of Canada's system of record.
  • Québec runs its own system, SEAO, covering ministries, municipalities, schools and hospitals.
  • Nova Scotia publishes procurement data through its open-data portal.
  • Ontario, BC, Alberta and Saskatchewan run separate portals (Jaggaer, Ivalua and custom systems) with no public data feeds.
  • Commercial aggregators like MERX charge subscription fees for a unified view.

The good news: the three biggest open sources publish machine-readable data under open licences. Let's walk through them.

Source 1: CanadaBuys (federal) — open CSV, updated every 2 hours

CanadaBuys publishes official open data files under the Open Government Licence:

  • New tender notices — refreshed every 2 hours during business hours
  • Open tender notices — all tenders currently accepting bids, refreshed daily
  • Award notices and contract history — who won what

The files are bilingual CSVs with a documented data dictionary. A quick look with Python:

import pandas as pd

url = "https://canadabuys.canada.ca/opendata/pub/openTenderNotice-ouvertAvisAppelOffres.csv"
df = pd.read_csv(url)
print(df.shape)          # thousands of open tenders
print(df.columns[:8])    # bilingual column names like "title-titre-eng"
Enter fullscreen mode Exit fullscreen mode

Watch out for three things: bilingual column names (title-titre-eng, tenderClosingDate-appelOffresdateCloture), occasional schema drift, and multi-category rows (procurementCategory can be CNST,SRV).

Source 2: SEAO (Québec) — OCDS JSON on Données Québec

Québec publishes SEAO data as weekly JSON files in the Open Contracting Data Standard (CC-BY). Each weekly file contains tender notices and awarded contracts as OCDS "releases" — with buyer, UNSPSC classification, award values in CAD.

The catch: file URLs change weekly, so you must query the CKAN catalogue API first to find the latest resources, then download and parse each file. Titles are French-only.

Source 3: Nova Scotia — awarded tenders on Socrata

Nova Scotia publishes awarded public tenders — government plus municipalities, universities, school boards and health authorities — with the winning vendor and award amount:

https://data.novascotia.ca/resource/m6ps-8j6u.json?$limit=100&$order=awarded_date DESC
Enter fullscreen mode Exit fullscreen mode

This is market intelligence rather than live opportunities: who wins contracts in your category, and at what price.

What about Ontario, BC, Alberta?

As of mid-2026, none of them publish open data feeds for current tenders. Their portals are JavaScript applications that require browser-level scraping. If you only need federal + Québec + Nova Scotia, you can skip that complexity entirely.

The DIY reality check

Building this yourself means: three different formats (CSV, OCDS JSON, Socrata), bilingual normalization, weekly-changing URLs, schema drift, deduplication across sources, and a scheduler with failure alerts. It's a fun weekend project — and then an unpaid maintenance job forever, because government portals change things without notice.

The 5-minute alternative: a ready-made API

I maintain an Apify Actor that does all of the above and returns one normalized feed:

Canada Tenders Scraper — Federal, Quebec, Nova Scotia

One schema across all three sources: title (EN/FR), buyer, category, UNSPSC, closing date, contact, notice URL, award value where applicable. Example input:

{
    "sources": ["canadabuys", "seao"],
    "keywords": ["software", "cloud", "informatique"],
    "closingWithinDays": 30,
    "onlyNewSinceLastRun": true,
    "maxResults": 500
}
Enter fullscreen mode Exit fullscreen mode

The onlyNewSinceLastRun flag is the key feature for monitoring: schedule the Actor daily, and each run returns only tenders you haven't seen before. Connect it to email or Slack through Apify integrations and you have a tender-alert system for a few dollars a month — no subscription, you pay per record ($4 per 1,000).

Calling it from your own code:

from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("truenorthdata/canada-tenders-scraper").call(run_input={
    "keywords": ["construction", "roofing"],
    "closingWithinDays": 21,
    "maxResults": 200,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["titleEn"] or item["titleFr"], "|", item["closingDate"], "|", item["buyerOrg"])
Enter fullscreen mode Exit fullscreen mode

Everything runs on official open data — no proxies, no scraping grey zones, no logins.

FAQ

Is scraping Canadian government tenders legal? The sources above are official open-data programs published for reuse (Open Government Licence – Canada, CC-BY Québec). Yes.

How fresh is the data? Federal: every 2 hours for new tenders. Québec: weekly files. Nova Scotia: monthly award updates.

Can I get tenders for a specific province only? Yes — filter by region (e.g. "Ontario") on federal data, or select the Québec/Nova Scotia sources directly.

What's the cheapest way to try it? Apify's free tier includes $5 of credit — enough for ~1,000 tender records to see if the data fits your use case.


Questions or a source you'd like covered (Ontario? BC? municipal portals?) — open an issue on the Actor page and I'll take a look.

Top comments (0)