Alberta APIpurchasing.alberta.ca ↗
Search Alberta public procurement postings via API. Filter by status, solicitation type, category, and keyword. Returns titles, org, UNSPSC codes, and more.
What is the Alberta API?
The Alberta Purchasing Connection API exposes 1 endpoint — search_opportunities — that returns paginated procurement postings from purchasing.alberta.ca, with up to 15 fields per record including posting ID, title, contracting organization, solicitation type, status codes, delivery regions, UNSPSC commodity codes, amendment flags, and notice/opportunity classification. It covers the full live listing of open and historical government procurement notices posted by Alberta public bodies.
curl -X GET 'https://api.parse.bot/scraper/5a8cdb1f-61ba-4a94-8986-abac546c69c1/search_opportunities?status=OPEN&solicitation_type=RFP' \ -H 'X-API-Key: $PARSE_API_KEY'
Typed, relational, agent-ready
A generated client with real types, enums, and the links between objects — the structure a flat JSON response can't carry. Autocompletes in your editor and reads cleanly to coding agents.
- Fully typed · autocompletes
- Objects link to objects
- Typed errors & pagination
Typed Python client. Set up the SDK in your uv project, then pull this API’s typed client:
uv add parse-sdk uv run parse init uv run parse add --marketplace purchasing-alberta-ca-api
uv run parse add --marketplace pulls a pinned snapshot of this canonical API — it won’t change underneath you. To customize it, subscribe and swap to your own copy.
"""Walkthrough: Alberta Purchasing Connection — search open procurement opportunities."""
from parse_apis.purchasing_alberta_ca_api import (
PurchasingAlberta,
PostingStatus,
SolicitationType,
Category,
SortField,
SortDirection,
InputFormatInvalid,
)
client = PurchasingAlberta()
# Browse the newest open RFPs across all categories, capped at 5 results.
for opp in client.opportunities.search(
status=PostingStatus.OPEN,
solicitation_type=SolicitationType.RFP,
sort_by=SortField.POST_DATE,
sort_direction=SortDirection.DESC,
limit=5,
):
print(opp.title, "|", opp.reference_number, "|", opp.close_date_time)
# Keyword search narrowed to Services, take the first match.
try:
hit = client.opportunities.search(
query="engineering",
category=Category.SERVICES,
limit=1,
).first()
except InputFormatInvalid as e:
print("Bad filter value:", e.message)
hit = None
if hit is not None:
print(hit.full_title)
print("Status:", hit.status_code)
print("Posted:", hit.post_date_time, "Closes:", hit.close_date_time)
print("Regions:", ", ".join(hit.region_of_delivery))
print("Commodity codes:", ", ".join(hit.commodity_codes))
if hit.project_description:
print("Description:", hit.project_description[:200])
print("exercised: opportunities.search")
Searches procurement opportunities and notices listed on the Alberta Purchasing Connection and returns one page of matching postings, one record per posting, with the posting's identifiers, titles, contracting organization, status/type/category codes, delivery regions, UNSPSC commodity codes, posting and closing timestamps (site-local Alberta time, no offset), the description and submission text shown in the listing, and a link to the posting page. By default only postings whose status is Open and whose solicitation type is Request for Proposal are returned; both filters accept comma-separated code lists and are validated against the site's fixed vocabularies. Free-text keyword search matches the site's own text-phrase search. Results are sorted by posting date (newest first) unless sort_by/sort_direction say otherwise. Pagination is offset-based: limit (default 20, maximum 100) and offset (default 0) select the page; total_count is the site's total for the filter set and has_more tells whether a further page exists. A search with no matches returns an empty opportunities array with total_count 0. One upstream request per call.
| Param | Type | Description |
|---|---|---|
| limit | integer | Number of postings per page, 1 to 100; larger values are clamped to 100. |
| query | string | Free-text search phrase matched against title, description, contracting organization, solicitation type, region, and reference numbers. Omitted = no keyword filter. |
| offset | integer | Zero-based index of the first posting to return within the filtered result set. |
| status | string | Comma-separated posting status codes to include (e.g. OPEN,EVALUATION). Unrecognized codes are rejected. |
| sort_by | string | Field the results are ordered by. |
| category | string | Comma-separated category codes to include. Omitted = all categories. |
| sort_direction | string | Sort direction for sort_by. |
| solicitation_type | string | Comma-separated solicitation type codes to include (e.g. RFP,ITB). Unrecognized codes are rejected. |
{
"type": "object",
"fields": {
"limit": "page size applied",
"offset": "offset applied",
"has_more": "boolean, true when offset + returned count < total_count",
"total_count": "site's total number of postings matching the filters",
"opportunities": "array of posting records, one per opportunity or notice on this page",
"opportunities[].id": "site's opaque posting identifier (UUID string)",
"opportunities[].title": "short posting title",
"opportunities[].amended": "boolean, posting has been amended",
"opportunities[].keywords": "array of site-generated keyword strings, may be empty",
"opportunities[].is_notice": "boolean, true for notices rather than opportunities",
"opportunities[].detail_url": "public posting page URL on the site",
"opportunities[].full_title": "organization - solicitation type - title as displayed",
"opportunities[].is_reopened": "boolean",
"opportunities[].status_code": "posting status code (PostingStatus enum)",
"opportunities[].bid_security": "text; may be null or empty",
"opportunities[].category_code": "category code (Category enum)",
"opportunities[].post_date_time": "posting timestamp, ISO-8601 without offset, Alberta local time",
"opportunities[].close_date_time": "closing timestamp, ISO-8601 without offset, Alberta local time",
"opportunities[].commodity_codes": "array of UNSPSC commodity code strings",
"opportunities[].reference_number": "APC reference number, e.g. AB-2026-06524; also the posting page slug",
"opportunities[].region_of_delivery": "array of delivery region names",
"opportunities[].submission_details": "text; may be null or empty",
"opportunities[].agreement_type_code": "trade agreement type code",
"opportunities[].project_description": "description text; may be null or empty",
"opportunities[].solicitation_number": "contracting organization's internal solicitation number, may be null",
"opportunities[].external_origin_link": "URL of the originating tender portal when the posting is hosted elsewhere; null or empty otherwise",
"opportunities[].commodity_code_titles": "array of UNSPSC titles aligned by position with commodity_codes",
"opportunities[].opportunity_type_code": "procurement method code, e.g. OPEN-COMPETITIVE or LIMITED",
"opportunities[].solicitation_type_code": "solicitation type code (SolicitationType enum)",
"opportunities[].additional_requirements": "text; may be null or empty",
"opportunities[].contracting_organization_id": "UUID of the contracting organization",
"opportunities[].question_submission_details": "text; may be null or empty"
},
"sample": {
"data": {
"limit": 20,
"offset": 0,
"has_more": true,
"total_count": 233,
"opportunities": [
{
"id": "744a04eb-351d-45c5-8abc-4dc6d68acd0e",
"title": "Cost Estimating Consultant (WWTP Expansion)",
"amended": false,
"keywords": [
"cost estimating",
"cost consultant",
"cost assessment"
],
"is_notice": false,
"detail_url": "https://purchasing.alberta.ca/posting/AB-2026-06523",
"full_title": "City of Lethbridge - Request for Proposal - Cost Estimating Consultant (WWTP Expansion)",
"is_reopened": false,
"status_code": "OPEN",
"bid_security": "",
"category_code": "SRV",
"post_date_time": "2026-09-24T17:34:26",
"close_date_time": "2026-10-22T14:00:00",
"commodity_codes": [
"81100000"
],
"reference_number": "AB-2026-06523",
"region_of_delivery": [
"Alberta",
"Lethbridge"
],
"submission_details": "",
"agreement_type_code": "TA-01",
"project_description": "The procurement is to create a pre-qualified roster of three cost estimating consultants to provide independent cost advice for the WWTP Train 2 expansion and other priority municipal infrastructure work.",
"solicitation_number": "COL-26-165",
"external_origin_link": null,
"commodity_code_titles": [
"Professional engineering services"
],
"opportunity_type_code": "OPEN-COMPETITIVE",
"solicitation_type_code": "RFP",
"additional_requirements": null,
"contracting_organization": "City of Lethbridge",
"contracting_organization_id": "b22069dc-b680-40dc-ad6d-e9cec1e0ba4d",
"question_submission_details": ""
}
]
},
"status": "success"
}
}About the Alberta API
What the API Returns
The search_opportunities endpoint queries the Alberta Purchasing Connection and returns one page of matching procurement postings. Each record in the opportunities array carries the posting's UUID (id), short title, the contracting organization, status and solicitation type codes, category codes, delivery regions, UNSPSC commodity classifications, an amended boolean, an is_notice boolean distinguishing notices from active opportunities, and a keywords array generated by the site. Pagination is handled via offset and limit (1–100), and the response includes total_count and has_more so callers can walk through large result sets.
Filtering and Search
The endpoint accepts several independent filters that can be combined. status takes comma-separated status codes such as OPEN or EVALUATION; unrecognized codes are rejected with an error rather than silently ignored. solicitation_type filters by codes like RFP or ITB. category narrows results to specific procurement categories. The query parameter performs free-text matching against titles, descriptions, contracting organizations, solicitation types, and regions. sort_by and sort_direction control ordering of results.
Notices vs. Opportunities
Every record exposes is_notice, which distinguishes formal notices (such as contract award notices) from active solicitation opportunities. The amended field flags postings that have been updated after initial publication, which is relevant when tracking changes to active tenders. The keywords array may be empty for older or simpler postings.
Coverage and Freshness
The API reflects the Alberta Purchasing Connection's public listings, which are maintained by Alberta public sector bodies. Results represent what is currently indexed on the site, including open, closed, and awarded postings depending on the status filter applied.
The Alberta API is a managed, monitored endpoint for purchasing.alberta.ca — not a raw scraper you maintain. Every endpoint is automatically health-checked on a schedule, and when purchasing.alberta.ca changes and a check fails, the API is automatically queued for repair and re-verified. It is built to keep working as the site underneath it changes.
This isn't an official purchasing.alberta.ca API — it's an independent, maintained REST wrapper over public data. Where the source has no official API (or only a limited one), Parse gives you a stable contract over a source that never promised one, and keeps it current. Need a new endpoint or field? You can revise it yourself in plain English and the agent rebuilds it against the live site in minutes — contributing the change back to the shared API is free.
Will this API break when the source site changes?+
Is this an official API from the source site?+
Can I fix or extend this API myself if I need a new endpoint or field?+
What happens if I call an endpoint that has an issue?+
- Monitor all open RFP and ITB postings in a specific Alberta region by combining
solicitation_type=RFP,ITBwith a region-scopedquery. - Track amendments to active tenders by filtering on
status=OPENand checking theamendedfield in returned records. - Build a procurement alert system that polls for new postings using
sort_byon posting date and comparing against a storedoffset. - Classify government spending by mapping
categoryand UNSPSC commodity codes from search results to internal spend categories. - Distinguish contract award notices from active solicitations using the
is_noticeboolean to separate pipeline intelligence from live bid opportunities. - Aggregate posting volume by contracting organization to identify the most active Alberta public buyers over a period.
- Filter procurement postings to a specific commodity area using UNSPSC codes returned in opportunity records.
| Tier | Price | Credits/month | Rate limit |
|---|---|---|---|
| Free | $0/mo | 200 | 5 req/min |
| Hobby | $30/mo | 1,000 | 20 req/min |
| Developer | $100/mo | 5,000 | 100 req/min |
| Team | $300/mo | 20,000 | 300 req/min |
| Company | $1,000/mo | 100,000 | 500 req/min |
Each endpoint has a fixed posted price per successful call — most fall between 1 and 10 credits — shown on this API's page before you run it. Exceeding the rate limit returns a 429 response. Authenticate with the X-API-Key header.
Does purchasing.alberta.ca have an official developer API?+
What does the search_opportunities endpoint return for each posting?+
amended boolean, an is_notice boolean, and a keywords array. The is_notice field distinguishes contract award notices from active solicitation opportunities.How does pagination work, and can I retrieve all results?+
offset and limit (maximum 100 per page). The response includes total_count (total matching postings) and has_more (boolean). You can walk through the full result set by incrementing offset by limit until has_more is false.Does the API return the full posting document — description body, attachments, or closing date/time?+
Are there limitations on which status codes or solicitation types are accepted?+
status and solicitation_type accept only recognized codes (e.g. OPEN, EVALUATION for status; RFP, ITB for solicitation type). Passing an unrecognized code returns an error rather than an empty result set. Omitting either parameter returns postings across all statuses or types respectively.