ProcureData aggregates 2,105,430 Canadian government procurement records across 9 sources into a single REST API: CanadaBuys federal contracts and tenders, TBS proactive disclosures, SEAO Quebec tenders and contracts, Nova Scotia awarded contracts, Alberta sole-source records, and PSPC standing offers. All sources are normalized to consistent field names, cursor-based pagination, and response times under 200 ms.
The raw sources are fragmented by design. CanadaBuys uses inconsistent department names and no stable record IDs. SEAO’s OCDS feed requires non-trivial parsing. Nova Scotia, Alberta, and other provinces each publish separately, in different formats. ProcureData handles all of that before your first API call. What follows is the developer reference for building LLM agents, reporting tools, or RAG pipelines on top of that data.
Every record maps to one of five entity types: contract, tender, award, disclosure, or standing_offer. The source breakdown:
| Source | Entity type | Records | Level | Coverage |
|---|---|---|---|---|
| CanadaBuys contracts | contract |
591,762 | federal | 2009–present |
| TBS proactive disclosures | disclosure |
499,868 | federal | 2004–present |
| SEAO Quebec tenders | tender |
368,149 | provincial | 2021–present |
| SEAO Quebec contracts | contract |
352,972 | provincial | 2021–present |
| CanadaBuys awards | award |
143,129 | federal | 2012–present |
| CanadaBuys tenders | tender |
99,182 | federal | 2009–present |
| Nova Scotia awarded contracts | contract |
30,788 | provincial | 2010–present |
| Alberta sole-source registry | contract |
13,596 | provincial | 1975–present |
| PSPC standing offers | standing_offer |
5,984 | federal | current snapshot |
Federal records use government_level=federal. Quebec, Nova Scotia, and Alberta records use government_level=provincial. The municipal level is reserved for future use. Alberta sole-source coverage begins in 1975, though pre-2000 records are sparse; most practical queries will use issued_after=2010-01-01.
All endpoints return JSON and are accessed via RapidAPI. Two headers are required on every request:
# Required headers for every request
X-RapidAPI-Key: YOUR_API_KEY
X-RapidAPI-Host: procuredata-canadian-government-procurement-api.p.rapidapi.com
Free tier: 100 requests/day. Pro tier: 1,000 requests/day at $29/month. Ultra tier: ~127,000 requests/day at $99/month.
The API surface is intentionally small. Ten endpoints cover all entity types, aggregation, and lifecycle views. The full OpenAPI 3.0.3 spec is at procuredata.ca/openapi.json and imports directly into Postman, Insomnia, or any LLM tool-use framework.
curl -G "https://procuredata-canadian-government-procurement-api.p.rapidapi.com/contract" \
--data-urlencode "department=National Defence" \
--data-urlencode "issued_after=2025-04-01" \
--data-urlencode "sort_by=value" \
--data-urlencode "limit=20" \
-H "X-RapidAPI-Key: YOUR_API_KEY" \
-H "X-RapidAPI-Host: procuredata-canadian-government-procurement-api.p.rapidapi.com"
curl -G "https://procuredata-canadian-government-procurement-api.p.rapidapi.com/tender" \
--data-urlencode "q=cybersecurity" \
--data-urlencode "government_level=federal" \
--data-urlencode "issued_after=2025-01-01" \
--data-urlencode "limit=10" \
-H "X-RapidAPI-Key: YOUR_API_KEY" \
-H "X-RapidAPI-Host: procuredata-canadian-government-procurement-api.p.rapidapi.com"
Every list response includes a next_cursor field. Pass it as the cursor parameter on the next request. Cursors are stable and safe for sequential tool calls.
import requests
BASE = "https://procuredata-canadian-government-procurement-api.p.rapidapi.com"
HEADERS = {
"X-RapidAPI-Key": "YOUR_API_KEY",
"X-RapidAPI-Host": "procuredata-canadian-government-procurement-api.p.rapidapi.com",
}
params = {"department": "Health Canada", "category": "SRV", "limit": 100}
records = []
while True:
r = requests.get(BASE + "/contract", headers=HEADERS, params=params)
data = r.json()
records.extend(data["results"])
if not data.get("next_cursor"):
break
params["cursor"] = data["next_cursor"]
print(f"{len(records)} contracts fetched")
The API is a natural fit for tool use in LLM agents. Below is an OpenAI-compatible function definition you can drop into any agent that needs to query Canadian procurement data. Anthropic tool use follows the same structure.
{
"type": "function",
"function": {
"name": "query_canadian_procurement",
"description": "Search Canadian government contracts, tenders, and awards from federal and provincial sources. Returns structured procurement records including department, vendor, value, category, and date.",
"parameters": {
"type": "object",
"properties": {
"entity_type": {
"type": "string",
"enum": ["contract", "tender", "award", "disclosure", "standing_offer"],
"description": "Type of procurement record to query"
},
"department": {
"type": "string",
"description": "Federal or provincial department name (partial match supported)"
},
"vendor": {
"type": "string",
"description": "Vendor or supplier name (partial match)"
},
"q": {
"type": "string",
"description": "Full-text search query across title and description fields"
},
"category": {
"type": "string",
"enum": ["SRV", "GD", "CNST", "SRVTGD"],
"description": "Procurement category: SRV=Services, GD=Goods, CNST=Construction, SRVTGD=Services+Goods"
},
"government_level": {
"type": "string",
"enum": ["federal", "provincial"],
"description": "Filter by level of government"
},
"value_min": {
"type": "number",
"description": "Minimum contract value in CAD"
},
"value_max": {
"type": "number",
"description": "Maximum contract value in CAD"
},
"issued_after": {
"type": "string",
"description": "Return records issued on or after this date (YYYY-MM-DD)"
},
"issued_before": {
"type": "string",
"description": "Return records issued on or before this date (YYYY-MM-DD)"
},
"sort_by": {
"type": "string",
"enum": ["value", "date"],
"description": "Sort results by contract value or date (default: date desc)"
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 500,
"description": "Number of records to return (default 100)"
},
"cursor": {
"type": "string",
"description": "Pagination cursor from next_cursor field of a previous response"
}
},
"required": ["entity_type"]
}
}
}
A user asks: “Which IT vendors received the most federal contracts from Health Canada in the last two years?” The agent decomposes this into two tool calls:
{
"entity_type": "contract",
"department": "Health Canada",
"category": "SRV",
"issued_after": "2024-04-22",
"sort_by": "value",
"limit": 100
}
GET /contract/stats?department=Health+Canada&category=SRV&group_by=vendor&period=1y
The stats endpoint returns approximate group counts and sums via database sampling. For large questions about total spend or category distribution, it is faster than paging through all records.
A typical contract record:
{
"record_id": "canada_contracts|EC107-200001|C",
"entity_type": "contract",
"source_id": "canada_contracts",
"event_date": "2025-03-15",
"department": "Health Canada",
"vendor": "Deloitte Inc",
"contract_value": 2400000.00,
"category": "SRV",
"government_level": "federal",
"data": {
"solicitation_number": "EC107-200001",
"procurement_method": "Competitive",
"contract_period_start": "2025-03-15",
"contract_period_end": "2026-03-14",
"description": "Management consulting services",
"vendor_province": "Ontario"
}
}
{
"record_id": "canada_tenders|W8486-226285",
"entity_type": "tender",
"source_id": "canada_tenders",
"event_date": "2025-11-03",
"department": "National Defence",
"data": {
"solicitation_number": "W8486-226285",
"notice_type": "Request for Proposal",
"closing_date": "2025-12-15",
"security_clearance": "secret",
"title": "Tactical Communications Equipment"
}
}
Security clearance field: federal tenders from CanadaBuys include a security_clearance field extracted from the solicitation PDF. Values: reliability, enhanced_reliability, secret, top_secret, none, or null when no document was available. Useful for filtering tenders accessible to uncleared vendors.
Offset pagination breaks when records are added between requests. If your agent pages through results over multiple turns or tool calls, use the cursor parameter. Pass next_cursor from the previous response. There is no need to track a page number or recalculate offsets.
If the user asks “how much did DND spend on IT in 2024,” do not page through all contracts. Call /contract/stats?department=National+Defence&category=SRV&sum_field=contract_value&period=1y. The stats endpoint uses database sampling and returns in under a second even on large filtered sets.
The /department/{name} endpoint returns the top vendors, total value, and category breakdown for a department without any pagination. One tool call answers most “who does X buy from” questions.
The raw CanadaBuys data has approximately 90 department name variants normalized to about 20 canonical names in the API. The department filter supports partial match, but using canonical names returns faster results and avoids ambiguous matches. Common canonical names:
# Canonical names return the fastest, most precise results
"National Defence"
"Public Services and Procurement Canada"
"Health Canada"
"Shared Services Canada"
"Royal Canadian Mounted Police"
"Canada Revenue Agency"
"Transport Canada"
"Environment and Climate Change Canada"
"Indigenous Services Canada"
"Fisheries and Oceans Canada"
# Or use the /departments endpoint to list all canonical names
GET /departments
The API returns HTTP 429 when you exceed your plan’s daily limit. RapidAPI sends a Retry-After header indicating when the limit resets (UTC midnight). For agents running overnight pipelines, stay under 80% of your daily quota to leave room for retries.
If you are embedding procurement records for retrieval-augmented generation, a few practical notes:
description field in the data object is the richest text for embedding. It is populated for most CanadaBuys records and all disclosure records.title + notice_type + department together. The title alone is often ambiguous.event_date as a metadata filter when users ask time-scoped questions. This avoids embedding stale records into answers about current spending.government_level in metadata before retrieval if the user’s question is explicitly federal or provincial.With records at 200–800 tokens each, no chunking logic is needed. Pipe records directly from the API into your embedding endpoint and index by record_id for deduplication on re-ingestion. For background on how CanadaBuys tenders are structured, see Federal Tenders on CanadaBuys: A Technical Overview.
next_cursor field. Pass cursor=<value> on subsequent requests. Cursors are stable and safe for agents making sequential calls across turns.
?government_level=federal for CanadaBuys and TBS data, or ?government_level=provincial for SEAO Quebec, Nova Scotia, and Alberta data. The field is indexed; responses are under 200 ms for typical queries.
/sources endpoint for the last updated timestamp per source.
contract, tender, award, disclosure, standing_offer. Department names are normalized from raw source data (approximately 90 variants to about 20 canonical forms; use the /departments endpoint for the full list). category values: SRV (Services), GD (Goods), CNST (Construction), SRVTGD (Services and Goods). security_clearance is extracted from solicitation PDFs via pypdf for CanadaBuys tenders only. Stats endpoints use TABLESAMPLE SYSTEM for approximate aggregation; exact counts can be obtained by paging through results.
Get your API key and start querying 2.1M procurement records today.
Get API Key on RapidAPI View OpenAPI Spec