ausdata-sdk 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ausdata_sdk-0.2.0/.gitignore +30 -0
- ausdata_sdk-0.2.0/CHANGELOG.md +61 -0
- ausdata_sdk-0.2.0/CLAUDE.md +115 -0
- ausdata_sdk-0.2.0/LICENSE +21 -0
- ausdata_sdk-0.2.0/PKG-INFO +193 -0
- ausdata_sdk-0.2.0/README.md +140 -0
- ausdata_sdk-0.2.0/pyproject.toml +54 -0
- ausdata_sdk-0.2.0/src/ausdata/__init__.py +76 -0
- ausdata_sdk-0.2.0/src/ausdata/_internal/__init__.py +5 -0
- ausdata_sdk-0.2.0/src/ausdata/_internal/http.py +143 -0
- ausdata_sdk-0.2.0/src/ausdata/_internal/retry.py +63 -0
- ausdata_sdk-0.2.0/src/ausdata/_version.py +11 -0
- ausdata_sdk-0.2.0/src/ausdata/async_client.py +253 -0
- ausdata_sdk-0.2.0/src/ausdata/client.py +349 -0
- ausdata_sdk-0.2.0/src/ausdata/exceptions.py +101 -0
- ausdata_sdk-0.2.0/src/ausdata/models.py +288 -0
- ausdata_sdk-0.2.0/src/ausdata/py.typed +0 -0
- ausdata_sdk-0.2.0/tests/__init__.py +0 -0
- ausdata_sdk-0.2.0/tests/conftest.py +165 -0
- ausdata_sdk-0.2.0/tests/test_async_client.py +118 -0
- ausdata_sdk-0.2.0/tests/test_client.py +95 -0
- ausdata_sdk-0.2.0/tests/test_endpoints.py +297 -0
- ausdata_sdk-0.2.0/tests/test_exceptions.py +159 -0
- ausdata_sdk-0.2.0/tests/test_model_drift.py +189 -0
- ausdata_sdk-0.2.0/tests/test_retry.py +145 -0
- ausdata_sdk-0.2.0/tests/test_to_dataframe.py +141 -0
- ausdata_sdk-0.2.0/uv.lock +541 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Build artefacts
|
|
2
|
+
dist/
|
|
3
|
+
build/
|
|
4
|
+
*.egg-info/
|
|
5
|
+
*.egg
|
|
6
|
+
__pycache__/
|
|
7
|
+
*.pyc
|
|
8
|
+
*.pyo
|
|
9
|
+
|
|
10
|
+
# Virtual envs
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
env/
|
|
14
|
+
|
|
15
|
+
# Tooling caches
|
|
16
|
+
.pytest_cache/
|
|
17
|
+
.ruff_cache/
|
|
18
|
+
.mypy_cache/
|
|
19
|
+
.coverage
|
|
20
|
+
htmlcov/
|
|
21
|
+
|
|
22
|
+
# uv lock retained but exclude editor/system files
|
|
23
|
+
.DS_Store
|
|
24
|
+
.idea/
|
|
25
|
+
.vscode/
|
|
26
|
+
*.swp
|
|
27
|
+
|
|
28
|
+
# Local env files
|
|
29
|
+
.env
|
|
30
|
+
.env.local
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. Versioning follows
|
|
4
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
5
|
+
|
|
6
|
+
## [0.2.0] — 2026-05-17
|
|
7
|
+
|
|
8
|
+
### Distribution
|
|
9
|
+
|
|
10
|
+
- **PyPI distribution name changed:** `ausdata` → `ausdata-sdk` (the
|
|
11
|
+
short `ausdata` name was taken by another project on PyPI). The
|
|
12
|
+
**import path stays `from ausdata import Client`** — no code changes
|
|
13
|
+
required for existing users. Install command:
|
|
14
|
+
`pip install ausdata-sdk` (was `pip install ausdata`).
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- `client.whoami()` / `AsyncClient.whoami()` — caller introspection.
|
|
19
|
+
- `client.describe(source, dataset_id)` / async equivalent — schema lookup.
|
|
20
|
+
- `client.list_datasets(source)` / async equivalent — enumerate a source's
|
|
21
|
+
curated datasets.
|
|
22
|
+
- `client.get_data(source, dataset_id, **filters)` / async equivalent —
|
|
23
|
+
generic data fetch with kwargs for source-specific dimension filters.
|
|
24
|
+
- `WhoamiResponse` typed model.
|
|
25
|
+
- Drift-detection tests (`tests/test_model_drift.py`) — fail CI when the
|
|
26
|
+
SDK model fields diverge from the live API response shape.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- Default base URL is now `https://ausdata-api.fly.dev` (was the unconfigured
|
|
31
|
+
`https://api.ausdata.io`). Override via `AUSDATA_BASE_URL` env var or the
|
|
32
|
+
`base_url` constructor arg.
|
|
33
|
+
- `EconomicDashboard` model gained `consumer_spending_yoy_pct`. The old
|
|
34
|
+
`lending_housing_change_qoq_pct` field is kept as a deprecated alias and
|
|
35
|
+
will be removed in v0.2.
|
|
36
|
+
|
|
37
|
+
## [0.1.0] — 2026-05-16
|
|
38
|
+
|
|
39
|
+
Initial scaffold.
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
- Synchronous `Client` and asynchronous `AsyncClient` over httpx.
|
|
43
|
+
- API-key resolution: constructor arg, then `AUSDATA_API_KEY` env var.
|
|
44
|
+
- Typed response models mirroring the ausdata-api `ApiResponse` envelope
|
|
45
|
+
(`Meta`, `Links`, `SourceCitation`, plus per-endpoint payload shapes).
|
|
46
|
+
- Endpoint methods for the v1 MVP surface:
|
|
47
|
+
- `health()`
|
|
48
|
+
- `account.api_key()` / `account.rotate_key()`
|
|
49
|
+
- `search_datasets(q, source, limit)`
|
|
50
|
+
- `real_wages(start, end, seasonal_adjustment)`
|
|
51
|
+
- `economic_dashboard(period)`
|
|
52
|
+
- Convenience: `ApiResponse.to_dataframe()` (requires `pandas` extra),
|
|
53
|
+
`to_csv(path=None)`, `to_json()`.
|
|
54
|
+
- Typed exception hierarchy (`AusdataError`, `AuthenticationError`,
|
|
55
|
+
`PermissionError`, `RateLimitError`, `ValidationError`, `UpstreamError`,
|
|
56
|
+
`AusdataServerError`) — each carries the API's hint string.
|
|
57
|
+
- Automatic retry policy: 5xx and 429 retried with exponential backoff,
|
|
58
|
+
`Retry-After` header respected up to 60s, configurable via constructor.
|
|
59
|
+
- `py.typed` marker so downstream users get full IDE type completion.
|
|
60
|
+
- Test suite (40+ tests) covering successful parsing, auth, errors, retries,
|
|
61
|
+
pandas integration, and the async surface. All mocked — no live calls.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# ausdata-sdk — developer notes
|
|
2
|
+
|
|
3
|
+
The official Python client library for the hosted REST API at
|
|
4
|
+
[ausdata.io](https://ausdata.io). Layer 3's public face — thin HTTP wrapper,
|
|
5
|
+
not a data engine.
|
|
6
|
+
|
|
7
|
+
**One-line value prop:** `pip install ausdata-sdk` (imports as `from ausdata import Client`) and call typed methods against
|
|
8
|
+
the hosted API; no MCP runtime, no SDMX parsing, no upstream credentials.
|
|
9
|
+
|
|
10
|
+
## License
|
|
11
|
+
|
|
12
|
+
**MIT.** This package is intentionally MIT (not FSL like `ausdata-api`)
|
|
13
|
+
because the SDK is a trivial HTTP wrapper — the moat lives in the API
|
|
14
|
+
backend, not here. Permissive licensing maximises adoption + makes
|
|
15
|
+
enterprise legal teams happy.
|
|
16
|
+
|
|
17
|
+
The upstream FastAPI in `../ausdata-api/` is FSL-1.1-MIT; that's where
|
|
18
|
+
competitors would lift code and the licence does the work.
|
|
19
|
+
|
|
20
|
+
## Layout
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
src/ausdata/
|
|
24
|
+
├── __init__.py Re-exports Client, AsyncClient, models, exceptions
|
|
25
|
+
├── client.py Synchronous Client (httpx.Client)
|
|
26
|
+
├── async_client.py AsyncClient (httpx.AsyncClient)
|
|
27
|
+
├── models.py Pydantic ApiResponse envelope + per-endpoint payloads
|
|
28
|
+
├── exceptions.py AusdataError + 6 typed subclasses
|
|
29
|
+
├── py.typed PEP 561 marker — full IDE type completion downstream
|
|
30
|
+
└── _internal/
|
|
31
|
+
├── http.py API-key resolution, default headers, error mapping
|
|
32
|
+
└── retry.py RetryPolicy (5xx + 429 with Retry-After)
|
|
33
|
+
|
|
34
|
+
tests/
|
|
35
|
+
├── conftest.py Canned API response fixtures (all mocked via pytest-httpx)
|
|
36
|
+
├── test_client.py Sync client construction + headers + base_url
|
|
37
|
+
├── test_async_client.py
|
|
38
|
+
├── test_endpoints.py Success-path test per public method
|
|
39
|
+
├── test_exceptions.py 4xx/5xx → typed exception mapping
|
|
40
|
+
├── test_retry.py Backoff math + end-to-end retry behaviour
|
|
41
|
+
└── test_to_dataframe.py pandas/CSV/JSON conversions
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Cross-links
|
|
45
|
+
|
|
46
|
+
- **API spec** (private): `../API_DESIGN_V1.md` — every endpoint here mirrors
|
|
47
|
+
what the API exposes. If you add an endpoint method, update both files.
|
|
48
|
+
- **API server** (private): `../ausdata-api/` — when its response shape
|
|
49
|
+
changes, mirror the change in `models.py` here. Add new optional fields
|
|
50
|
+
rather than breaking changes whenever possible so old SDK versions stay
|
|
51
|
+
compatible with new servers.
|
|
52
|
+
- **Portfolio strategy** (private): `../STRATEGY.md` — the SDK is intentionally
|
|
53
|
+
separate from the 9 sister MCP packages on PyPI; don't blur the line.
|
|
54
|
+
|
|
55
|
+
## Development
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
cd ausdata-sdk
|
|
59
|
+
uv sync --extra dev
|
|
60
|
+
uv run pytest -q
|
|
61
|
+
uv run ruff check src tests
|
|
62
|
+
uv build
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
All tests use `pytest-httpx` — there are zero live API calls in the suite.
|
|
66
|
+
This is deliberate: SDK tests should run in CI without secrets and in <2s.
|
|
67
|
+
|
|
68
|
+
## Test patterns
|
|
69
|
+
|
|
70
|
+
- One success-path test per endpoint method (see `test_endpoints.py`)
|
|
71
|
+
- One typed-exception test per HTTP status the API uses (`test_exceptions.py`)
|
|
72
|
+
- Retry behaviour is tested both unit-style (`RetryPolicy` math) and
|
|
73
|
+
integration-style (full `Client.health()` calls with mocked 500s)
|
|
74
|
+
- pandas import failure is tested by patching `builtins.__import__` so the
|
|
75
|
+
test runs even if pandas is installed in the dev environment
|
|
76
|
+
|
|
77
|
+
## Anti-patterns — don't do these
|
|
78
|
+
|
|
79
|
+
- **Don't auto-publish to PyPI.** The user runs `uv publish` when ready.
|
|
80
|
+
CI here only lints + tests; no Trusted Publishing workflow yet.
|
|
81
|
+
- **Don't push to a public GitHub remote** until the user explicitly creates
|
|
82
|
+
it. Local commits only for now.
|
|
83
|
+
- **Don't add new runtime dependencies beyond `httpx` + `pydantic`** (plus
|
|
84
|
+
`pandas` as an optional extra). The SDK's value is being lightweight.
|
|
85
|
+
- **Don't import from any sister MCP package** (`abs_mcp`, `rba_mcp`, etc.).
|
|
86
|
+
The SDK talks to the hosted API only — that's the whole point of the
|
|
87
|
+
layered architecture.
|
|
88
|
+
- **Don't build a CLI here.** A future `ausdata-cli` package can wrap this
|
|
89
|
+
SDK; keep them separate.
|
|
90
|
+
- **Don't reimplement data parsing.** All shaping happens in `ausdata-api`'s
|
|
91
|
+
composers + the sister MCPs they call.
|
|
92
|
+
- **Don't echo tokens or API keys** in logs, errors, or test output.
|
|
93
|
+
|
|
94
|
+
## Adding a new endpoint method
|
|
95
|
+
|
|
96
|
+
1. Confirm the API supports it (`../ausdata-api/src/ausdata_api/routers/`).
|
|
97
|
+
2. Add the payload shape to `src/ausdata/models.py` if it isn't already
|
|
98
|
+
there. Prefer optional fields so older SDK versions don't break.
|
|
99
|
+
3. Add a method to both `Client` and `AsyncClient` (parameters mirror
|
|
100
|
+
the API query string, kwarg-only where it improves readability).
|
|
101
|
+
4. Add at least one success-path test (`test_endpoints.py`) and any
|
|
102
|
+
relevant error-path test (`test_exceptions.py`).
|
|
103
|
+
5. Update `README.md` (endpoint table) + `CHANGELOG.md` (new entry).
|
|
104
|
+
|
|
105
|
+
## Releasing (when the user is ready)
|
|
106
|
+
|
|
107
|
+
1. Bump `version` in `pyproject.toml` + `src/ausdata/__init__.py`.
|
|
108
|
+
2. Update `CHANGELOG.md`.
|
|
109
|
+
3. `uv run pytest -q` 10x — zero flakes.
|
|
110
|
+
4. `uv run ruff check src tests` — clean.
|
|
111
|
+
5. `uv build` — wheel + sdist build cleanly.
|
|
112
|
+
6. `uv publish` — **user runs this**, not Claude.
|
|
113
|
+
|
|
114
|
+
PyPI new-project rate limit: 5 per account per 24h. Plan first publish
|
|
115
|
+
accordingly; the user handles this.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Harry Vass
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ausdata-sdk
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Python SDK for the Australian public data API. Free tier available at ausdata.io.
|
|
5
|
+
Project-URL: Homepage, https://ausdata.io
|
|
6
|
+
Project-URL: Documentation, https://docs.ausdata.io
|
|
7
|
+
Project-URL: Repository, https://github.com/Bigred97/ausdata-sdk
|
|
8
|
+
Project-URL: Issues, https://github.com/Bigred97/ausdata-sdk/issues
|
|
9
|
+
Author: Harry Vass
|
|
10
|
+
License: MIT License
|
|
11
|
+
|
|
12
|
+
Copyright (c) 2026 Harry Vass
|
|
13
|
+
|
|
14
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
15
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
16
|
+
in the Software without restriction, including without limitation the rights
|
|
17
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
18
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
19
|
+
furnished to do so, subject to the following conditions:
|
|
20
|
+
|
|
21
|
+
The above copyright notice and this permission notice shall be included in all
|
|
22
|
+
copies or substantial portions of the Software.
|
|
23
|
+
|
|
24
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
25
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
26
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
27
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
28
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
29
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
30
|
+
SOFTWARE.
|
|
31
|
+
License-File: LICENSE
|
|
32
|
+
Keywords: api,australia,client,mcp,public-data,sdk
|
|
33
|
+
Classifier: Development Status :: 4 - Beta
|
|
34
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
38
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
39
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
40
|
+
Classifier: Typing :: Typed
|
|
41
|
+
Requires-Python: >=3.11
|
|
42
|
+
Requires-Dist: httpx>=0.27
|
|
43
|
+
Requires-Dist: pydantic>=2.7
|
|
44
|
+
Provides-Extra: dev
|
|
45
|
+
Requires-Dist: pandas>=2.0; extra == 'dev'
|
|
46
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
47
|
+
Requires-Dist: pytest-httpx>=0.30; extra == 'dev'
|
|
48
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
49
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
50
|
+
Provides-Extra: pandas
|
|
51
|
+
Requires-Dist: pandas>=2.0; extra == 'pandas'
|
|
52
|
+
Description-Content-Type: text/markdown
|
|
53
|
+
|
|
54
|
+
# ausdata — Python SDK for the Australian public data API
|
|
55
|
+
|
|
56
|
+
The official Python client for [ausdata.io](https://ausdata.io). One auth key,
|
|
57
|
+
one uniform response envelope, every major Australian government data source.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install ausdata-sdk
|
|
61
|
+
# or, with pandas convenience:
|
|
62
|
+
pip install "ausdata-sdk[pandas]"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Why?
|
|
66
|
+
|
|
67
|
+
The chart you spend 90 minutes building in Excel — one HTTP call.
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Before — wrangling SDMX XML by hand
|
|
71
|
+
curl 'https://api.data.abs.gov.au/data/WPI/...'
|
|
72
|
+
|
|
73
|
+
# After — typed, normalised, attribution-tagged
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from ausdata import Client
|
|
78
|
+
|
|
79
|
+
client = Client(api_key="ak_xxx")
|
|
80
|
+
result = client.real_wages(start="2019-Q1", end="2024-Q4")
|
|
81
|
+
|
|
82
|
+
print(result.data[0])
|
|
83
|
+
# RealWageRow(period='2024-Q4', wpi_annual_change_pct=3.2,
|
|
84
|
+
# cpi_annual_change_pct=2.4, real_wages_gap_pct=0.8,
|
|
85
|
+
# real_wages_direction='growing')
|
|
86
|
+
|
|
87
|
+
df = result.to_dataframe() # pandas (optional extra)
|
|
88
|
+
result.to_csv("real_wages.csv") # Datawrapper-friendly
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Async client
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
import asyncio
|
|
95
|
+
from ausdata import AsyncClient
|
|
96
|
+
|
|
97
|
+
async def main():
|
|
98
|
+
async with AsyncClient(api_key="ak_xxx") as client:
|
|
99
|
+
snapshot = await client.economic_dashboard()
|
|
100
|
+
print(snapshot.data.cash_rate_pct, snapshot.data.unemployment_rate_pct)
|
|
101
|
+
|
|
102
|
+
asyncio.run(main())
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Endpoints
|
|
106
|
+
|
|
107
|
+
| Method | API endpoint | What you get |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| `client.health()` | `GET /v1/health` | Service status (no quota cost) |
|
|
110
|
+
| `client.whoami()` | `GET /v1/whoami` | Tier + monthly usage (works for API keys or JWTs) |
|
|
111
|
+
| `client.account.api_key()` | `GET /v1/account/api-key` | Your key + tier + monthly usage (JWT-only) |
|
|
112
|
+
| `client.account.rotate_key()` | `POST /v1/account/api-key` | Rotate the key, get new plaintext (JWT-only) |
|
|
113
|
+
| `client.search_datasets(q=...)` | `GET /v1/search-datasets` | Search across all 9 AU sources |
|
|
114
|
+
| `client.list_datasets(source)` | `GET /v1/datasets/{source}` | Enumerate curated datasets for one source |
|
|
115
|
+
| `client.describe(source, id)` | `GET /v1/describe/{source}/{id}` | Schema: dimensions, valid filters, valid values |
|
|
116
|
+
| `client.get_data(source, id, **filters)` | `GET /v1/data/{source}/{id}` | Fetch any dataset with source-specific filters |
|
|
117
|
+
| `client.real_wages(start=...)` | `GET /v1/real-wages` | WPI YoY minus CPI YoY (composed) |
|
|
118
|
+
| `client.economic_dashboard()` | `GET /v1/economic-dashboard` | 5-source headline macro snapshot (composed) |
|
|
119
|
+
|
|
120
|
+
The full discover → introspect → fetch loop is:
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
r = client.search_datasets(q="unemployment rate") # find dataset
|
|
124
|
+
schema = client.describe("abs", "LF") # see valid filters
|
|
125
|
+
data = client.get_data("abs", "LF", # fetch with right filters
|
|
126
|
+
measure="unemployment_rate",
|
|
127
|
+
region="nsw", start="2024-01")
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Every data response is wrapped in the same `ApiResponse` envelope so you
|
|
131
|
+
always know where to find the payload (`.data`), the attribution (`.meta.sources`),
|
|
132
|
+
and the audit trail (`.meta.retrieved_at`, `.meta.stale`).
|
|
133
|
+
|
|
134
|
+
## Pricing
|
|
135
|
+
|
|
136
|
+
| Tier | Price | Calls/mo | Notes |
|
|
137
|
+
|---|---|---|---|
|
|
138
|
+
| Free | $0 | 100 | 1y history, attribution required |
|
|
139
|
+
| Analyst | $29 | 10,000 | 10y history, no attribution |
|
|
140
|
+
| Pro | $99 | 100,000 | Webhooks, priority support |
|
|
141
|
+
| Enterprise | custom | unlimited | SLA, white-label, custom queries |
|
|
142
|
+
|
|
143
|
+
Sign up + get a free key at [ausdata.io](https://ausdata.io).
|
|
144
|
+
|
|
145
|
+
## Configuration
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
from ausdata import Client
|
|
149
|
+
|
|
150
|
+
# API key resolution order:
|
|
151
|
+
# 1. explicit api_key= argument
|
|
152
|
+
# 2. AUSDATA_API_KEY environment variable
|
|
153
|
+
client = Client(
|
|
154
|
+
api_key="ak_xxx",
|
|
155
|
+
base_url="https://api.ausdata.io", # override for staging
|
|
156
|
+
timeout=30.0, # per-request timeout (seconds)
|
|
157
|
+
max_retries=3, # 5xx + 429 retry budget
|
|
158
|
+
retry_backoff_factor=2.0, # exponential delay multiplier
|
|
159
|
+
)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Errors
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from ausdata import Client
|
|
166
|
+
from ausdata.exceptions import (
|
|
167
|
+
AusdataError, # base
|
|
168
|
+
AuthenticationError, # 401
|
|
169
|
+
PermissionError, # 403, 402 (tier-blocked)
|
|
170
|
+
RateLimitError, # 429 with retry_after
|
|
171
|
+
ValidationError, # 400
|
|
172
|
+
UpstreamError, # 502/503
|
|
173
|
+
AusdataServerError, # 5xx fallback
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
try:
|
|
177
|
+
result = client.real_wages(start="not-a-quarter")
|
|
178
|
+
except ValidationError as e:
|
|
179
|
+
print(e.message, "Hint:", e.hint)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Every error carries the API's message plus the actionable `hint` when
|
|
183
|
+
available (e.g. `"Did you mean 2024-Q1?"`).
|
|
184
|
+
|
|
185
|
+
## Links
|
|
186
|
+
|
|
187
|
+
- **Docs**: [docs.ausdata.io](https://docs.ausdata.io)
|
|
188
|
+
- **API reference**: [api.ausdata.io/docs](https://api.ausdata.io/docs)
|
|
189
|
+
- **Issues**: [github.com/Bigred97/ausdata-sdk/issues](https://github.com/Bigred97/ausdata-sdk/issues)
|
|
190
|
+
|
|
191
|
+
## License
|
|
192
|
+
|
|
193
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# ausdata — Python SDK for the Australian public data API
|
|
2
|
+
|
|
3
|
+
The official Python client for [ausdata.io](https://ausdata.io). One auth key,
|
|
4
|
+
one uniform response envelope, every major Australian government data source.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
pip install ausdata-sdk
|
|
8
|
+
# or, with pandas convenience:
|
|
9
|
+
pip install "ausdata-sdk[pandas]"
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Why?
|
|
13
|
+
|
|
14
|
+
The chart you spend 90 minutes building in Excel — one HTTP call.
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Before — wrangling SDMX XML by hand
|
|
18
|
+
curl 'https://api.data.abs.gov.au/data/WPI/...'
|
|
19
|
+
|
|
20
|
+
# After — typed, normalised, attribution-tagged
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from ausdata import Client
|
|
25
|
+
|
|
26
|
+
client = Client(api_key="ak_xxx")
|
|
27
|
+
result = client.real_wages(start="2019-Q1", end="2024-Q4")
|
|
28
|
+
|
|
29
|
+
print(result.data[0])
|
|
30
|
+
# RealWageRow(period='2024-Q4', wpi_annual_change_pct=3.2,
|
|
31
|
+
# cpi_annual_change_pct=2.4, real_wages_gap_pct=0.8,
|
|
32
|
+
# real_wages_direction='growing')
|
|
33
|
+
|
|
34
|
+
df = result.to_dataframe() # pandas (optional extra)
|
|
35
|
+
result.to_csv("real_wages.csv") # Datawrapper-friendly
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Async client
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
import asyncio
|
|
42
|
+
from ausdata import AsyncClient
|
|
43
|
+
|
|
44
|
+
async def main():
|
|
45
|
+
async with AsyncClient(api_key="ak_xxx") as client:
|
|
46
|
+
snapshot = await client.economic_dashboard()
|
|
47
|
+
print(snapshot.data.cash_rate_pct, snapshot.data.unemployment_rate_pct)
|
|
48
|
+
|
|
49
|
+
asyncio.run(main())
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Endpoints
|
|
53
|
+
|
|
54
|
+
| Method | API endpoint | What you get |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| `client.health()` | `GET /v1/health` | Service status (no quota cost) |
|
|
57
|
+
| `client.whoami()` | `GET /v1/whoami` | Tier + monthly usage (works for API keys or JWTs) |
|
|
58
|
+
| `client.account.api_key()` | `GET /v1/account/api-key` | Your key + tier + monthly usage (JWT-only) |
|
|
59
|
+
| `client.account.rotate_key()` | `POST /v1/account/api-key` | Rotate the key, get new plaintext (JWT-only) |
|
|
60
|
+
| `client.search_datasets(q=...)` | `GET /v1/search-datasets` | Search across all 9 AU sources |
|
|
61
|
+
| `client.list_datasets(source)` | `GET /v1/datasets/{source}` | Enumerate curated datasets for one source |
|
|
62
|
+
| `client.describe(source, id)` | `GET /v1/describe/{source}/{id}` | Schema: dimensions, valid filters, valid values |
|
|
63
|
+
| `client.get_data(source, id, **filters)` | `GET /v1/data/{source}/{id}` | Fetch any dataset with source-specific filters |
|
|
64
|
+
| `client.real_wages(start=...)` | `GET /v1/real-wages` | WPI YoY minus CPI YoY (composed) |
|
|
65
|
+
| `client.economic_dashboard()` | `GET /v1/economic-dashboard` | 5-source headline macro snapshot (composed) |
|
|
66
|
+
|
|
67
|
+
The full discover → introspect → fetch loop is:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
r = client.search_datasets(q="unemployment rate") # find dataset
|
|
71
|
+
schema = client.describe("abs", "LF") # see valid filters
|
|
72
|
+
data = client.get_data("abs", "LF", # fetch with right filters
|
|
73
|
+
measure="unemployment_rate",
|
|
74
|
+
region="nsw", start="2024-01")
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Every data response is wrapped in the same `ApiResponse` envelope so you
|
|
78
|
+
always know where to find the payload (`.data`), the attribution (`.meta.sources`),
|
|
79
|
+
and the audit trail (`.meta.retrieved_at`, `.meta.stale`).
|
|
80
|
+
|
|
81
|
+
## Pricing
|
|
82
|
+
|
|
83
|
+
| Tier | Price | Calls/mo | Notes |
|
|
84
|
+
|---|---|---|---|
|
|
85
|
+
| Free | $0 | 100 | 1y history, attribution required |
|
|
86
|
+
| Analyst | $29 | 10,000 | 10y history, no attribution |
|
|
87
|
+
| Pro | $99 | 100,000 | Webhooks, priority support |
|
|
88
|
+
| Enterprise | custom | unlimited | SLA, white-label, custom queries |
|
|
89
|
+
|
|
90
|
+
Sign up + get a free key at [ausdata.io](https://ausdata.io).
|
|
91
|
+
|
|
92
|
+
## Configuration
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from ausdata import Client
|
|
96
|
+
|
|
97
|
+
# API key resolution order:
|
|
98
|
+
# 1. explicit api_key= argument
|
|
99
|
+
# 2. AUSDATA_API_KEY environment variable
|
|
100
|
+
client = Client(
|
|
101
|
+
api_key="ak_xxx",
|
|
102
|
+
base_url="https://api.ausdata.io", # override for staging
|
|
103
|
+
timeout=30.0, # per-request timeout (seconds)
|
|
104
|
+
max_retries=3, # 5xx + 429 retry budget
|
|
105
|
+
retry_backoff_factor=2.0, # exponential delay multiplier
|
|
106
|
+
)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Errors
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from ausdata import Client
|
|
113
|
+
from ausdata.exceptions import (
|
|
114
|
+
AusdataError, # base
|
|
115
|
+
AuthenticationError, # 401
|
|
116
|
+
PermissionError, # 403, 402 (tier-blocked)
|
|
117
|
+
RateLimitError, # 429 with retry_after
|
|
118
|
+
ValidationError, # 400
|
|
119
|
+
UpstreamError, # 502/503
|
|
120
|
+
AusdataServerError, # 5xx fallback
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
result = client.real_wages(start="not-a-quarter")
|
|
125
|
+
except ValidationError as e:
|
|
126
|
+
print(e.message, "Hint:", e.hint)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Every error carries the API's message plus the actionable `hint` when
|
|
130
|
+
available (e.g. `"Did you mean 2024-Q1?"`).
|
|
131
|
+
|
|
132
|
+
## Links
|
|
133
|
+
|
|
134
|
+
- **Docs**: [docs.ausdata.io](https://docs.ausdata.io)
|
|
135
|
+
- **API reference**: [api.ausdata.io/docs](https://api.ausdata.io/docs)
|
|
136
|
+
- **Issues**: [github.com/Bigred97/ausdata-sdk/issues](https://github.com/Bigred97/ausdata-sdk/issues)
|
|
137
|
+
|
|
138
|
+
## License
|
|
139
|
+
|
|
140
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "ausdata-sdk"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "Python SDK for the Australian public data API. Free tier available at ausdata.io."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = { file = "LICENSE" }
|
|
7
|
+
requires-python = ">=3.11"
|
|
8
|
+
authors = [{ name = "Harry Vass" }]
|
|
9
|
+
keywords = ["mcp", "australia", "public-data", "api", "client", "sdk"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 4 - Beta",
|
|
12
|
+
"License :: OSI Approved :: MIT License",
|
|
13
|
+
"Programming Language :: Python :: 3.11",
|
|
14
|
+
"Programming Language :: Python :: 3.12",
|
|
15
|
+
"Programming Language :: Python :: 3.13",
|
|
16
|
+
"Topic :: Internet :: WWW/HTTP",
|
|
17
|
+
"Topic :: Scientific/Engineering :: Information Analysis",
|
|
18
|
+
"Typing :: Typed",
|
|
19
|
+
]
|
|
20
|
+
dependencies = [
|
|
21
|
+
"httpx>=0.27",
|
|
22
|
+
"pydantic>=2.7",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.optional-dependencies]
|
|
26
|
+
pandas = ["pandas>=2.0"]
|
|
27
|
+
dev = [
|
|
28
|
+
"pytest>=8",
|
|
29
|
+
"pytest-asyncio>=0.23",
|
|
30
|
+
"pytest-httpx>=0.30",
|
|
31
|
+
"pandas>=2.0",
|
|
32
|
+
"ruff>=0.5",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Homepage = "https://ausdata.io"
|
|
37
|
+
Documentation = "https://docs.ausdata.io"
|
|
38
|
+
Repository = "https://github.com/Bigred97/ausdata-sdk"
|
|
39
|
+
Issues = "https://github.com/Bigred97/ausdata-sdk/issues"
|
|
40
|
+
|
|
41
|
+
[build-system]
|
|
42
|
+
requires = ["hatchling"]
|
|
43
|
+
build-backend = "hatchling.build"
|
|
44
|
+
|
|
45
|
+
[tool.hatch.build.targets.wheel]
|
|
46
|
+
packages = ["src/ausdata"]
|
|
47
|
+
|
|
48
|
+
[tool.pytest.ini_options]
|
|
49
|
+
asyncio_mode = "auto"
|
|
50
|
+
testpaths = ["tests"]
|
|
51
|
+
pythonpath = ["src"]
|
|
52
|
+
|
|
53
|
+
[tool.ruff.lint.per-file-ignores]
|
|
54
|
+
"tests/*" = ["E741", "F841", "F401"]
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Python SDK for the Australian public data API at https://ausdata.io.
|
|
2
|
+
|
|
3
|
+
Quick start::
|
|
4
|
+
|
|
5
|
+
from ausdata import Client
|
|
6
|
+
|
|
7
|
+
client = Client(api_key="ak_xxx") # or set AUSDATA_API_KEY
|
|
8
|
+
result = client.real_wages(start="2019-Q1", end="2024-Q4")
|
|
9
|
+
df = result.to_dataframe()
|
|
10
|
+
|
|
11
|
+
Async equivalent::
|
|
12
|
+
|
|
13
|
+
import asyncio
|
|
14
|
+
from ausdata import AsyncClient
|
|
15
|
+
|
|
16
|
+
async def main():
|
|
17
|
+
async with AsyncClient(api_key="ak_xxx") as client:
|
|
18
|
+
return await client.real_wages(start="2019-Q1")
|
|
19
|
+
|
|
20
|
+
asyncio.run(main())
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
from ._version import __version__
|
|
26
|
+
from .async_client import AsyncClient
|
|
27
|
+
from .client import Client
|
|
28
|
+
from .exceptions import (
|
|
29
|
+
AusdataError,
|
|
30
|
+
AusdataServerError,
|
|
31
|
+
AuthenticationError,
|
|
32
|
+
PermissionError,
|
|
33
|
+
RateLimitError,
|
|
34
|
+
UpstreamError,
|
|
35
|
+
ValidationError,
|
|
36
|
+
)
|
|
37
|
+
from .models import (
|
|
38
|
+
AccountResponse,
|
|
39
|
+
ApiResponse,
|
|
40
|
+
DatasetSummary,
|
|
41
|
+
EconomicDashboard,
|
|
42
|
+
HealthResponse,
|
|
43
|
+
Links,
|
|
44
|
+
Meta,
|
|
45
|
+
RealWageRow,
|
|
46
|
+
SearchResponse,
|
|
47
|
+
SourceCitation,
|
|
48
|
+
WhoamiResponse,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
__all__ = [
|
|
52
|
+
"__version__",
|
|
53
|
+
# clients
|
|
54
|
+
"AsyncClient",
|
|
55
|
+
"Client",
|
|
56
|
+
# exceptions
|
|
57
|
+
"AusdataError",
|
|
58
|
+
"AusdataServerError",
|
|
59
|
+
"AuthenticationError",
|
|
60
|
+
"PermissionError",
|
|
61
|
+
"RateLimitError",
|
|
62
|
+
"UpstreamError",
|
|
63
|
+
"ValidationError",
|
|
64
|
+
# models
|
|
65
|
+
"AccountResponse",
|
|
66
|
+
"ApiResponse",
|
|
67
|
+
"DatasetSummary",
|
|
68
|
+
"EconomicDashboard",
|
|
69
|
+
"HealthResponse",
|
|
70
|
+
"Links",
|
|
71
|
+
"Meta",
|
|
72
|
+
"RealWageRow",
|
|
73
|
+
"SearchResponse",
|
|
74
|
+
"SourceCitation",
|
|
75
|
+
"WhoamiResponse",
|
|
76
|
+
]
|