Home / Blog / ABS SDMX-XML in plain English (and the one trick to stop wrestling with it)
2026-05-21 · Harry Vass
ABS SDMX-XML in plain English (and the one trick to stop wrestling with it)
SDMX is the international standard for statistical data, and it's why ABS Stat API is a pain. Here's what every key means, and how to avoid writing the parser yourself.
If you've ever tried to use data.api.abs.gov.au, you've met SDMX-XML. The first time you saw ?dimensionAtObservation=MEASURE&dataKey=1.10101.M.AU.10 and got back a 2 MB XML document with 47 nested <generic:Obs> elements, you probably closed the tab.
This post explains SDMX in 800 words. By the end you'll know:
- Why the format looks like that
- How to read a
dataKey - How to query the ABS API without parsing the XML yourself
SDMX exists because statisticians vote
SDMX (Statistical Data and Metadata eXchange) is an international standard managed by the IMF, OECD, UN, World Bank, BIS, ECB, and Eurostat. It's designed to make economic statistics interchangeable across countries. The ABS adopted it for its public data API in 2018.
The cost of standardisation: every observation carries every dimension. The win: the same client code that reads ABS CPI reads ECB euro-zone inflation, OECD employment, IMF foreign reserves, BIS housing prices.
In practice: every Aussie developer learns SDMX the day they need an ABS number, and never uses it for another statistical office.
How to read a dataKey
The ABS data API uses dot-separated dimension keys:
GET https://data.api.abs.gov.au/data/CPI/1.10101.M.AU.10
Breaks down as:
CPI, the dataset code1, MEASURE (1 = index)10101, INDEX (10101 = All Groups)M, TIME PERIOD aggregation (M = monthly)AU, REGION (AU = Australia)10, TSEST (10 = original, 20 = seasonally adjusted, 30 = trend)
Every position is fixed. Skip a dimension by leaving the slot empty:
GET /data/CPI/1.10101..AU.10 # all time-period aggregations
Or use + for OR:
GET /data/CPI/1.10101.M.AU.10+20+30 # all three TSEST values
How do you know which positions mean what? GET /datastructure/ABS/CPI returns a 50KB XML document listing each dimension, its position, and the allowed codes. You read it, build a lookup table, and from now on you can construct dataKeys.
Why the response is XML
The data response is also SDMX-XML:
<message:GenericData xmlns:message="..." xmlns:generic="...">
<message:DataSet>
<generic:Series>
<generic:SeriesKey>
<generic:Value id="MEASURE" value="1"/>
<generic:Value id="INDEX" value="10101"/>
<generic:Value id="TIME_PERIOD_AGGREGATION" value="M"/>
...
</generic:SeriesKey>
<generic:Obs>
<generic:ObsDimension id="TIME_PERIOD" value="2026-04"/>
<generic:ObsValue value="138.1"/>
</generic:Obs>
...
Every observation carries every dimension as metadata. Verbose, but unambiguous.
You can request CSV instead by adding Accept: text/csv, but the CSV is still flattened SDMX, so you still need the data-structure lookup to know what each column means.
The one trick
Stop parsing SDMX yourself. The Python ecosystem has pandasdmx (works, but heavy), the R world has readabs and raustats, and ausdata.io does it in one call:
from ausdata import Ausdata
print(Ausdata().cpi(limit=4))
Output:
{
"data": [
{"period": "2026-04", "value": 138.1, "annual_change_pct": 4.0, ...},
{"period": "2026-03", "value": 137.8, "annual_change_pct": 4.1, ...},
...
],
"meta": {
"endpoint": "/v1/data/abs/CPI",
"sources": [{"name": "ABS", "url": "https://www.abs.gov.au/...", "attribution": "..."}]
}
}
The SDMX is parsed for you. Dimensions are turned into plain-English filters (region="australia" instead of AU, frequency="monthly" instead of M). Each observation carries the year-over-year change pre-computed.
When you should still use the raw ABS API
Reasons not to wrap it:
- You need a non-curated dataset (
ausdata.iocovers ~80% of the popular ones) - You're using R and
readabsalready handles your case (it's excellent) - You're at a research institution with ABS DataLab / TableBuilder access, those are richer
- You don't want a dependency on a third party (this is legitimate; the ABS API is free and the bundle is MIT)
For everything else, the gain is "8 lines of code instead of 80." Not magic, just convenience.
What's next
The current ABS Stat API has known reliability issues, the v2 (data.api.abs.gov.au) sometimes 5xxs on Friday evenings. ausdata.io adds a SQLite cache + stale-fallback layer that survives those outages by serving the last-known-good with stale=true, stale_reason="ABS upstream 503" in the meta. That's an SLA you can't get hitting the ABS directly.
Now go build something. The data is free and the cache exists.
---
Cover image: the ABS Data Explorer interface, included for context, not for parsing. Don't do that to yourself.