CanQuery API documentation
Build your first Canadian public-data integration with working examples, resource capabilities, typed endpoint reference and practical guidance for limits, pagination and retries.
Your first API request
- Create and verify a Free account.
- Create an API key and save it in the CANQUERY_API_KEY environment variable on your server. Never expose it in a website or public repository.
- Read metadata before querying rows:
curl "https://canquery.com/api/v1/resources/ckan-toronto-open-data-resource-6d0229af-bc54-46de-9c2b-26759b01dd05" \
-H "Authorization: Bearer $CANQUERY_API_KEY"JSON successes normally contain data, pagination.nextCursor and meta. CSV exports, vector tiles, empty responses and /healthz have separate formats. Use an API key for new integrations. Anonymous compatibility is currently available.
A reporting workflow you can reuse
Discover a dataset, choose a resource, inspect its query_mode and preparation metadata, then filter the recorded fields. This example selects STREET_NAME equal to KING in Toronto’s active-permits resource.
curl --get "https://canquery.com/api/v1/resources/ckan-toronto-open-data-resource-6d0229af-bc54-46de-9c2b-26759b01dd05/query" \
-H "Authorization: Bearer $CANQUERY_API_KEY" \
--data-urlencode 'filters={"STREET_NAME":"KING"}' \
--data-urlencode 'limit=5'For a bounded CSV, use the same filters with /query.csv. Export starts at offset zero and includes at most 10,000 rows. Keep provenance, publisher licence and retrieval time with your report. The dataset contains active applications and permits, not a complete history.
Toronto active building permits: source and context · Guide: active versus cleared permits and street filters
Prepare a public file for a research extract
Use ECCC CABIN Yukon River resource 718489a5-d132-4ee9-ab6f-0e2644297b78. The selected CSV is not available through the federal DataStore. CanQuery decodes the UTF-16 download, imports and hosts a table, and supports bounded requests. ECCC also provides separate CABIN tools; this is not a claim that the publisher has no other interfaces.
Publisher catalogue and Open Government Licence–Canada · Original CSV · Publisher database tools
- GET resource metadata. If a current prepared table exists, continue to the query.
- For an eligible unprepared or stale resource, POST /prepare.
- For a 202 response, poll its job serially at least three seconds apart, honour Retry-After and stop at done or failed. Stop locally after ten minutes and retain the job ID; this does not fail or cancel shared work.
- Re-read metadata for the schema, freshness and preparation timestamp.
- GET /query with
filters={"Year/Année":2024,"Family/Famille":"Chironomidae"},sort=_id ascandlimit=10.
The example counts matching records, not individual organisms or water quality. You still interpret taxonomy, coverage and publisher-specific values. Preserve publisher modification, preparation and retrieval dates as separate events. Enable JavaScript for runnable curl, Python and Node.js recipes and the verified result excerpt.
| Workflow | Credits |
|---|---|
| Inspect a ready resource, then query rows | 2 |
| Inspect, admit a new preparation, poll, inspect the prepared schema, query rows | 103 |
| Later inspect and query while the copy remains ready | 2 |
Polling costs zero. Joining existing work reduces the complete preparation sequence to 3 credits. A direct repeat query costs 1; an optional CSV export adds 25. An aggregate instead of the cold sequence’s row query totals 112. Discovery and other additional successful requests use their own credits.
Check what a resource supports
- datastore: live upstream rows and equality filters; no aggregation.
- ingested: prepared local rows, recorded-column filters, aggregation and whole-file profiles.
- ingestable: eligible file; explicitly request /prepare, poll the returned job, then re-read metadata.
- file-only: use the original download. Map capability is separate.
Preparation can return 200 for a current copy or 202 with shared work. Limits and publisher availability apply. /ingest remains a legacy compatibility endpoint and does not refresh a ready copy.
Endpoint reference
Download OpenAPI 3.1: typed parameters, response schemas and examples. Enable JavaScript for searchable parameters and code examples.
Map example: Uxbridge ward boundaries and GIS downloads
GET /api/v1/datasets — Search datasets
Discover admitted catalogue records. Availability of metadata does not guarantee a working file, table or map. Catalogue reads never request preparation. Follow pagination.nextCursor until null. Repeated values of one filter match any of them; different filters must all match. With facets, each group is counted without its own filter; counts are null when unavailable.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
q | query | string | No | Catalogue search text, trimmed; at most 200 characters. |
org | query | array | No | Publisher name or identity; repeatable. |
format | query | array | No | Recorded resource format; repeatable. |
keyword | query | array | No | Recorded dataset keyword; repeatable. |
place | query | string | No | Canonical place ID or slug; includes configured inherited coverage. |
source | query | array | No | Catalogue source ID; repeatable. |
level | query | array | No | Level of government; provincial includes territorial and municipal includes regional. Repeatable. |
mappable | query | string | No | Only datasets with maps. Use true or 1; omit to disable. false is invalid. |
queryable | query | string | No | Only datasets with a live table or a ready prepared copy. Use true or 1; omit to disable. |
sort | query | string | No | relevance keeps search ranking; title order follows lang. |
lang | query | string | No | French when fr; otherwise English. |
facets | query | string | No | Add meta.total and meta.facets. Use true or 1; omit to disable. |
limit | query | integer | No | Page size; values above 100 are clamped to 100. |
cursor | query | integer | No | Nonnegative offset. Pass pagination.nextCursor unchanged; omit for the first page. |
GET /api/v1/datasets/{idOrName} — Get a dataset
Resolve a canonical dataset ID or published name/slug. Includes resources, capabilities and source-specific licence/provenance.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
idOrName | path | string | Yes | Canonical dataset ID or published name/slug. |
GET /api/v1/organizations — List publishers
Literal bilingual publisher search with optional source/place scope. Uses catalogue pagination.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
q | query | string | No | Literal bilingual publisher search. |
source | query | string | No | Catalogue source ID. |
place | query | string | No | Canonical place ID or slug; includes configured inherited coverage. |
limit | query | integer | No | Page size; values above 100 are clamped to 100. |
cursor | query | integer | No | Nonnegative offset. Pass pagination.nextCursor unchanged; omit for the first page. |
GET /api/v1/organizations/{name} — Get a publisher
Publisher identity, catalogue and capability counts, and place context.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
name | path | string | Yes | Publisher name or identity, at most 200 characters. |
GET /api/v1/places — List places
Search canonical geographical identities and their direct/inherited coverage. Uses catalogue pagination.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
q | query | string | No | Catalogue search text, trimmed; at most 200 characters. |
kind | query | string | No | Place type. |
parent | query | string | No | Canonical parent ID or slug. |
featured | query | string | No | Use true or 1 for featured places; omit to disable. |
limit | query | integer | No | Page size; values above 100 are clamped to 100. |
cursor | query | integer | No | Nonnegative offset. Pass pagination.nextCursor unchanged; omit for the first page. |
GET /api/v1/places/{idOrSlug} — Get a place
Resolve a canonical ID, slug or durable alias. Includes ancestry, children, viewport and coverage. API lookup does not redirect like an HTML alias.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
idOrSlug | path | string | Yes | Canonical place ID, slug or historical alias. |
GET /api/v1/sources — List catalogue sources
Persisted publisher portals, provenance hosts, catalogue counts and last successful sync. This endpoint is not paginated.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
place | query | string | No | Canonical place ID or slug; includes configured inherited coverage. |
GET /api/v1/resources/{id} — Inspect resource capabilities
Read query_mode, preparation, ingestion, map and provenance before querying. A map can exist on a file-only table resource. Metadata reads neither prepare files nor renew local-copy activity. Preparation time differs from publisher modification time; source changes can remain undiscovered when publisher version metadata is unchanged.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Resource ID from dataset or resource metadata. |
GET /api/v1/resources/recently-unlocked — List recently prepared resources
Recent ready local copies. The historical route name is retained. Reading the list does not prepare files.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
place | query | string | No | Canonical place ID or slug; includes configured inherited coverage. |
limit | query | integer | No | Page size; values above 20 are clamped to 20. |
GET /api/v1/resources/popular — List popular resources
Counts CanQuery query-log hits, not publisher downloads.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
place | query | string | No | Canonical place ID or slug; includes configured inherited coverage. |
days | query | integer | No | Lookback days; values above 30 are clamped. |
limit | query | integer | No | Page size; values above 20 are clamped to 20. |
GET /api/v1/resources/{id}/query — Query rows or aggregate
Ready local snapshots take precedence over live CKAN DataStore. Live DataStore supports equality filters only, with no aggregation. Local aggregates cover the complete filtered snapshot; total then counts groups, not rows. Local reads renew copy activity and successful queries record popularity. Local responses include meta.snapshot, retrieval time and deployment limits. Supply snapshot on subsequent pages to require identical stored rows and schema; 409 requires an explicit new extraction. Catalogue provenance and publisher modification timestamps are observed at retrieval, not frozen at preparation. source_metadata_version is not a downloaded-content checksum. A 409 never automatically enqueues preparation; inspect metadata and explicitly use /prepare when supported. Aggregations have an additional 30 requests/minute limit and consume expensive-request concurrency.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Resource ID from dataset or resource metadata. |
snapshot | query | string | No | Require this exact local snapshot. A replaced or unavailable copy returns 409 SNAPSHOT_UNAVAILABLE; no automatic fallback or preparation. This token does not promise retention or source-content freshness. |
q | query | string | No | Search within resource rows (not catalogue search); at most 200 characters. Local search uses case-insensitive ILIKE patterns. |
filters | query | string | No | URL-encoded JSON object, at most 2,000 characters and 20 column keys of 1–63 characters. Exact recorded field names; conditions combine with AND. Scalars mean equality. Objects use op and value: eq, lt, gt, lte, gte, contains. Live DataStore supports eq only. eq:null means IS NULL locally; contains preserves % and _ SQL wildcard meanings. |
sort | query | string | No | Exact field name, optionally followed by asc or desc. Local row sorting also accepts _id; aggregates accept key or value. An exact field name wins over a direction suffix. Live DataStore values are limited to 100 characters. |
group_by | query | string | No | Prepared tables only: exact recorded grouping field. Required together with agg. |
agg | query | string | No | Prepared tables only. count forbids agg_column; other functions require it. sum/avg require a numeric field. |
agg_column | query | string | No | Recorded measure field for sum, avg, min or max; forbidden with count. |
bucket | query | string | No | Optional temporal grouping; group_by must be DATE or TIMESTAMPTZ. |
limit | query | integer | No | Page size; values above 100 are clamped to 100. |
offset | query | integer | No | Row/group offset; independent of catalogue cursor. Avoid relying on a stable snapshot across page calls. |
GET /api/v1/resources/{id}/query.csv — Export a bounded CSV
Exports from offset zero, at most 10000 rows (deployment cap; hard maximum 10,000). Same query capabilities as /query, including local full-snapshot aggregation before limiting groups. There is no CSV pagination or full-file guarantee. Use the original download for complete publisher files. Formula-like spreadsheet cells are neutralized. A failure after headers closes the partial response: discard incomplete output and retry within limits. Keyed failed/interrupted exports are refunded. Ten exports/minute plus plan request/concurrency limits.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Resource ID from dataset or resource metadata. |
snapshot | query | string | No | Require this exact local snapshot. A replaced or unavailable copy returns 409 SNAPSHOT_UNAVAILABLE; no automatic fallback or preparation. This token does not promise retention or source-content freshness. |
q | query | string | No | Search within resource rows (not catalogue search); at most 200 characters. Local search uses case-insensitive ILIKE patterns. |
filters | query | string | No | URL-encoded JSON object, at most 2,000 characters and 20 column keys of 1–63 characters. Exact recorded field names; conditions combine with AND. Scalars mean equality. Objects use op and value: eq, lt, gt, lte, gte, contains. Live DataStore supports eq only. eq:null means IS NULL locally; contains preserves % and _ SQL wildcard meanings. |
sort | query | string | No | Exact field name, optionally followed by asc or desc. Local row sorting also accepts _id; aggregates accept key or value. An exact field name wins over a direction suffix. Live DataStore values are limited to 100 characters. |
group_by | query | string | No | Prepared tables only: exact recorded grouping field. Required together with agg. |
agg | query | string | No | Prepared tables only. count forbids agg_column; other functions require it. sum/avg require a numeric field. |
agg_column | query | string | No | Recorded measure field for sum, avg, min or max; forbidden with count. |
bucket | query | string | No | Optional temporal grouping; group_by must be DATE or TIMESTAMPTZ. |
GET /api/v1/resources/{id}/preview — Preview a file before preparation
For a CSV that CanQuery can prepare but has no ready copy and no live DataStore: reads the first 256 KiB of the publisher’s file (a ranged request; nothing is stored) and returns its header with inferred types and up to 50 rows as the file’s text. truncated means the sample ended before the file did. Nothing is prepared, renewed or recorded; the cached sample lasts ten minutes. A successful sample is priced as a metadata read; 409 PREVIEW_NOT_NEEDED (a prepared copy or live table exists), 422 PREVIEW_UNSUPPORTED (other formats or unreadable content) and 502 PREVIEW_UPSTREAM or PREVIEW_TIMEOUT (the publisher fails or stalls) cost nothing but still count toward request limits. Anonymous requests are limited to thirty a minute; keyed requests follow the plan request limit.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Resource ID from dataset or resource metadata. |
GET /api/v1/resources/{id}/profile — Profile a prepared table
Requires a ready local snapshot. Describes the whole file, not current filters. At most the first 60 recorded non-_id fields; numeric fields add min/max/avg and date fields min/max. Successful reads renew activity. Twenty requests/minute plus plan request/concurrency limits.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Resource ID from dataset or resource metadata. |
snapshot | query | string | No | Require this exact local snapshot. A replaced or unavailable copy returns 409 SNAPSHOT_UNAVAILABLE; no automatic fallback or preparation. This token does not promise retention or source-content freshness. |
POST /api/v1/resources/{id}/prepare — Prepare or refresh a file
No body. Returns 200 for a current copy or 202 for a new/shared job. Only a newly admitted job deducts preparation credits. A terminal failure without a result published by that job returns the actual debit to its original allowance period exactly once. Retries retain the charge; stopping polling does not fail the job. A failed refresh returns its own charge even if an older copy still serves. A successful empty table counts as success; later expiry or eviction does not return credits. This is an account-credit reversal, not a payment refund. Current copies, joins and polling cost zero but still count toward request limits. Keyed new jobs are capped at 20/account/hour; anonymous admission has a configured per-IP limit. A shared active-queue ceiling and format/size/row/column/storage caps also apply. A stale copy can continue serving during refresh. Paused admission returns 429 even for current copies. Anonymous requests from self-identified crawlers can read current copies and join shared jobs but cannot start new ones (429 AUTOMATED_CLIENT). Honour cooldown Retry-After; a new source version can bypass the old cooldown. No scheduled-refresh guarantee.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Resource ID from dataset or resource metadata. |
POST /api/v1/resources/{id}/ingest — Prepare a file (legacy)
No body. Compatibility endpoint; new integrations should use /prepare for freshness and refresh. It returns an existing ready copy without refreshing stale data. Five requests/hour, plus shared limits. New-job charging matches /prepare.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Resource ID from dataset or resource metadata. |
POST /api/v1/resources/{id}/activity — Renew local-copy activity
No body. Renews an existing ready copy without preparing a file or recording popularity. A 409 means the copy is unavailable or replaced; re-read metadata. Zero credits still counts toward request limits.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Resource ID from dataset or resource metadata. |
GET /api/v1/jobs/{id} — Poll a preparation job
Poll one request at a time with a bounded delay and honour Retry-After on errors. A done job is historical: re-read resource metadata before querying because a prepared copy may expire. Failure messages are sanitized; failure_reason and retry_at explain retry eligibility. Zero credits still counts toward request limits.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | Positive job ID returned by preparation. |
GET /api/v1/resources/{id}/map — Get a bounded map viewport
Enveloped WGS84 GeoJSON for admitted ArcGIS, local PostGIS or PMTiles maps. live=true identifies the viewport interface; it does not promise a publisher fetch on every call. Check returned/truncated; requests are capped at 1,000 features and 8 MiB. PMTiles viewport decoding also bounds tile count. Sixty viewport calls/minute plus plan request/concurrency limits.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Resource ID from dataset or resource metadata. |
bbox | query | string | Yes | Required WGS84 west,south,east,north: -180 ≤ west < east ≤ 180 and -90 ≤ south < north ≤ 90. |
zoom | query | integer | No | Integer zoom level. |
limit | query | integer | No | Feature ceiling; out-of-range values return 400. |
GET /api/v1/resources/{id}/map/tiles/{version}/{z}/{x}/{y}.pbf — Get a vector tile
PMTiles maps only. Copy the current tiles template/version and zoom range from resource metadata. Coordinates must fit the tile grid and archive zoom range. Anonymous successful tiles cache immutably for one year; keyed responses remain private, no-store. Up to 240 tiles/minute, also subject to the keyed plan request limit.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Resource ID from dataset or resource metadata. |
version | path | string | Yes | Current map version from resource metadata. |
z | path | integer | Yes | Zoom within this archive’s min_zoom/max_zoom (0–22). |
x | path | integer | Yes | Tile coordinate from 0 through 2^z − 1. |
y | path | integer | Yes | Tile coordinate from 0 through 2^z − 1. |
GET /api/v1/stats — Get catalogue statistics
Briefly cached counts; capabilities overlap. These are dated observations, not guaranteed coverage or publisher availability.
GET /api/v1/insights/top-downloads — Get the publisher download leaderboard
Publisher-reported download statistics with selected representatives. Distinct from CanQuery query popularity; meta.period identifies the reporting month.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
lang | query | string | No | French when fr; otherwise English. |
GET /api/v1/insights/featured — Get prepared chart previews
Up to 12 validated previews from already prepared Top 100 representatives. No preparation is admitted. Context gives aggregation, snapshot time, displayed/total groups and known missing metrics; this is neither a chart-rendering API nor a scheduled-refresh service.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
lang | query | string | No | French when fr; otherwise English. |
GET /api/v1/blog — List data guides
Published guide summaries; reads bundled content without querying the catalogue.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
lang | query | string | No | Guide language; must be en or fr. |
place | query | string | No | Guide place slug. |
dataset | query | string | No | Canonical dataset ID or published slug. |
GET /api/v1/blog/{lang}/{slug} — Get a data guide
Published guide edition including sanitized bodyHtml and translation links.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
lang | path | string | Yes | Guide edition language. |
slug | path | string | Yes | Published localized guide slug. |
GET /api/v1/repo — Get repository metadata
Cached public GitHub star count. data may be null when GitHub is unavailable.
GET /api/v1/ops — Inspect job and map health
Public and unmetered. HTTP 200 or 503 uses the same envelope according to data.ok. Preparation counters alone do not change ok; inspect queue age and worker state separately. Health does not guarantee every file is available.
GET /healthz — Check database and federal upstream
Site-root path /healthz (not /api/v1/healthz). Public, unmetered, unenveloped reachability check; not a worker or catalogue-wide availability check.
GET /api/v1/readyz — Check application and database availability
Site-root path. Public, unmetered and no-store. Checks database connectivity without requiring an upstream publisher or worker to be healthy.
GET /api/v1/ops/components/{component} — Inspect one operational component
Public, unmetered and no-store. Sanitized host observations older than 180 seconds fail closed with HTTP 503. Independent components separate availability, preparation, maps, sources, commercial backlogs, backups and storage. Existing /ops semantics remain unchanged.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
component | path | string | Yes | Operational component to inspect. |
GET /api/v1/openapi.json — Download the OpenAPI contract
Public, unmetered OpenAPI 3.1 document. Import into compatible developer tools. Examples are synthetic and do not establish current publisher data or availability.
Operate reliably
Catalogue lists use the returned nextCursor offset string until null. Row queries use limit/offset and data.total; separate requests do not guarantee one unchanged snapshot. Local filters use exact recorded columns and combine with AND. CSV exports are capped; discard partial output after a failed stream.
Account keys share credits, request rates and expensive-request concurrency. X-CanQuery-Credits-Remaining includes pending reservations. Successful cached requests count. Successful cached requests count. Failed ordinary requests and interrupted exports return their credits. A new preparation deducts 100 credits; a terminal failure without a result published by that job returns its actual debit once to the original allowance period. Retries retain the debit. A client timeout does not fail or cancel the shared job. A failed refresh returns its charge even if an older table remains usable. Valid empty tables count as success; later expiry or eviction does not return credits. This is an account-credit adjustment, not a cash refund. Joining jobs and polling cost zero credits but still count toward request limits.
Distinguish QUOTA_EXCEEDED from RATE_LIMIT, CONCURRENCY_LIMIT, PREPARATION_BUSY, PREPARATION_COOLDOWN and AUTOMATED_CLIENT. Honour Retry-After and bound retries; do not repeatedly retry exhausted allowances, authentication errors or unsupported files. Include X-Request-Id when contacting support.
Publisher modification time, preparation time and retrieval time differ. Copies may expire, upstream files may change, and there is no scheduled-refresh guarantee.