Narwhal API reads a Statistics Canada series by vector id, such as statcan:v41690973. A Statistics Canada table id such as 10-10-0106-01 finds the table in the catalogue search. Read the vector number for your chosen series from the table's page at statcan.gc.ca, then pass it to the series route. There is no route that lists a table's vectors yet. The catalogue search returns table entries, so keep the table number and the series vector number separate as you work.
Do it with the API
- Set your API key Put your Narwhal API key in
NARWHAL_API_KEYand install the Python requests library. Each Python example reads the key from that environment variable and sends it in the Authorization header. Run the examples in the same shell where you set the variable. You can create an account if you need a key.export NARWHAL_API_KEY="YOUR_API_KEY" python -m pip install requests - Search for the table Search with
qandsource=statcan. Use the table number when you have it, or search by a subject such as consumer price. Print the title, frequency, first period and last period to check the result. The catalogue search is open on every plan and makes no call to Statistics Canada. A table search result has the templatestatcan:v{vector}; you still need the series vector number.import os import requests response = requests.get( "https://api.narwhalapi.com/v1/economics/catalogue", headers={"Authorization": f"Bearer {os.environ['NARWHAL_API_KEY']}"}, params={"q": "10-10-0106-01", "source": "statcan", "limit": 25}, timeout=10, ) response.raise_for_status() for table in response.json()["results"]: print( table["title"], table["frequency"], table.get("first_period"), table.get("last_period"), sep=" | ", ) - Copy the series vector number Read the table number from the result's title. For example, the consumer price search includes a monthly result whose title contains
Statistics Canada table 10-10-0106-01. Open the page of the table you chose at statcan.gc.ca and copy the vector number for the series you want. A vector looks likevfollowed by digits, such asv41690973. Add thestatcan:prefix for the API request. The table number identifies the table; the vector identifies the series you will read. - Check the series definition Send the full series id in the
idquery parameter. Check the returned title, frequency andunit_labelbefore you use its values. Forstatcan:v41690973, the title isCanada;All-items, the frequency is monthly and the unit label is2002=100. The response also carries attribution and a licence URL. Keep those details with any data you retain or display.import os import requests response = requests.get( "https://api.narwhalapi.com/v1/economics/catalogue/series", headers={"Authorization": f"Bearer {os.environ['NARWHAL_API_KEY']}"}, params={"id": "statcan:v41690973"}, timeout=10, ) response.raise_for_status() series = response.json() for field in ("id", "title", "frequency", "unit_label", "attribution", "licence_url"): print(f"{field}: {series[field]}") - Read several latest values together For several vectors, send their full ids as one comma-separated
idsparameter to the latest route. A batch accepts up to 50 distinct ids and is charged in one step, with one call per series returned. The example starts withv41690973, the Canada all-items consumer price index, which comes from table 18-10-0004-01. Add the other vector numbers you copied to the list, then inspect both the returned observations and themissinglist.import os import requests vectors = ["v41690973"] # Add other vector numbers copied from table pages. ids = list(dict.fromkeys(f"statcan:{vector}" for vector in vectors)) if not 1 <= len(ids) <= 50: raise ValueError("Supply between 1 and 50 distinct series ids") response = requests.get( "https://api.narwhalapi.com/v1/economics/catalogue/latest", headers={"Authorization": f"Bearer {os.environ['NARWHAL_API_KEY']}"}, params={"ids": ",".join(ids)}, timeout=10, ) response.raise_for_status() data = response.json() for observation in data["observations"]: print(observation["id"], observation["period"], observation["value"]) print("Missing:", data["missing"]) - Read history and calculate the monthly change Use the observations route for a period range. Start and end are inclusive and must use the same period format; monthly series use
YYYY-MM. This complete example requests July through September 2026 and calculates September's change from August. Values arrive as decimal strings, so the calculation uses Decimal. The printed result is rounded to two decimal places.import os from decimal import Decimal import requests response = requests.get( "https://api.narwhalapi.com/v1/economics/catalogue/series/observations", headers={"Authorization": f"Bearer {os.environ['NARWHAL_API_KEY']}"}, params={ "id": "statcan:v41690973", "start": "2026-07", "end": "2026-09", "limit": 100, }, timeout=10, ) response.raise_for_status() series = response.json() values = {} for observation in series["observations"]: values[observation["period"]] = Decimal(observation["value"]) print( observation["period"], observation["value"], observation.get("released_on", ""), observation.get("status", ""), ) change = (values["2026-09"] / values["2026-08"] - Decimal("1")) * Decimal("100") print(f"September 2026 month-on-month change: {change:.2f}%")
Calculate a monthly change from the CPI vector
The captured response for statcan:v41690973 contains these Canada all-items CPI observations. Each row has an observation period, a decimal value and a release date. Keep the release date separate from the month the value describes.
| Period | CPI, 2002=100 | Released |
|---|---|---|
| 2026-07 | 169.9 | 17 August 2026 |
| 2026-08 | 169.8 | 14 September 2026 |
| 2026-09 | 170.0 | 19 October 2026 |
September's month-on-month change is (170.0 / 169.8 - 1) * 100, or approximately 0.1178%. Rounded to two decimal places, the result is 0.12%. The calculation compares consecutive monthly index levels. The returned seasonal_adjustment is not_stated, so preserve that label alongside the result.
Statistics Canada responses carry the Open Government Licence - Canada attribution and licence URL. The series has no revision history or vintages. History access follows your plan: Free has the last 12 months, Basic the last 5 years, and Pro full history. See the series API reference for pagination and plans for access limits.
Which Statistics Canada series are not available?
Daily, weekly, multi-year, fiscal-year and fiscal-quarter vectors return 404 not_found. Narwhal supports monthly, quarterly and annual Statistics Canada vectors. Check the frequency in the catalogue result before you copy a vector number from the table page.
A malformed vector id returns 400 invalid_series_id. Its detail says that a Statistics Canada series id is v<vector number>. Pass the complete id, such as statcan:v41690973, through the id parameter. See API errors for error handling.
How do I read the status of a value?
Read the optional status field on each observation. It is present when a value has a status such as provisional or estimated. Preserve it with the period and value so a calculation retains that context.
Keep released_on when present, and use fetched_at for the retrieval timestamp. A response has stale: true only when Narwhal serves its last kept copy because the source was unreachable. Observation status and response freshness describe separate properties.
Published .
Next steps
- API referenceSeries API reference
- Statistics Canada data API
- How-toCache responses and handle 429 errors
Create a free key and call the API from your own code. Get a free API key
