Postio Python SDK
Python SDK for Postio — the UK validation API for addresses, emails and phone numbers. Sync + async, type-safe via Pydantic v2. Backed by Royal Mail PAF and Ordnance Survey.
First time? Sign up free — first 100 lookups on us, no card needed.
Install
pip install postio
Requires Python 3.10+.
30-second example
from postio import PostioClient
client = PostioClient(api_key="pk_...") # or set POSTIO_API_KEY
result = client.address.search("downing street")
for hit in result.results:
print(hit.udprn, hit.suggestion)
print("request id:", result.meta.requestId)
Async
import asyncio
from postio import AsyncPostioClient
async def main():
async with AsyncPostioClient(api_key="pk_...") as client:
result = await client.address.postcode("SW1A 2AA")
for addr in result.results:
print(addr.address_line_1, addr.post_town)
asyncio.run(main())
API
| Method | Returns | Notes |
|---|---|---|
client.address.search(q, max_results=None) |
AddressSearchEnvelope |
Free-text typeahead lookup |
client.address.postcode(postcode, max_results=None) |
AddressPostcodeEnvelope |
Full addresses for a postcode |
client.address.udprn(udprn) |
AddressUdprnEnvelope |
Single address by UDPRN |
client.email.validate(address) |
EmailEnvelope |
Syntax + MX + SMTP + deliverability |
client.phone.validate(number) |
PhoneEnvelope |
E.164 format + carrier + reachability |
client.connect() |
ConnectSuccess |
Free health probe |
AsyncPostioClient exposes the same surface, awaitable.
Errors
Every non-2xx response raises a typed exception. PostioError is the base.
from postio import (
PostioClient,
PostioInvalidKey, # 401
PostioOutOfCredit, # 402
PostioForbidden, # 403
PostioNotFound, # 404
PostioValidationError, # 400 / 422
PostioRateLimit, # 429 — has .retry_after
PostioServerError, # 5xx — retried by default
PostioTimeout,
PostioConnectionError,
)
try:
client.address.postcode("not-a-postcode")
except PostioValidationError as err:
print(err.status, err.code, err.request_id, err.envelope)
Every error carries status, code, details, request_id, and the raw
envelope. The request_id is the support handle to quote when reporting
issues at https://postio.co.uk/contact.
Configuration
from postio import PostioClient, RetryConfig
client = PostioClient(
api_key="pk_...",
base_url="https://api.postio.co.uk/v1", # default
timeout=10.0, # seconds
retries=2, # or RetryConfig(...) or None to disable
headers={"x-tracking-id": "..."}, # extra headers, merged
)
Default retry policy: 2 retries, exponential backoff with full jitter
(0.5s → 8s cap), retries on 408, 409, 429, 5xx, and network/timeout errors.
Pass retries=None to disable.
Frameworks
The SDK is framework-agnostic but ships classifiers for Django, Flask, and FastAPI. Drop the client on your app state at startup:
FastAPI
from contextlib import asynccontextmanager
from fastapi import FastAPI
from postio import AsyncPostioClient
@asynccontextmanager
async def lifespan(app: FastAPI):
app.state.postio = AsyncPostioClient() # reads POSTIO_API_KEY
yield
await app.state.postio.close()
app = FastAPI(lifespan=lifespan)
Django — instantiate one PostioClient in apps.py's ready() and stash
it on a module-level singleton; close it in a shutdown signal handler.
Flask — PostioClient on app.extensions["postio"], close in
app.teardown_appcontext.
Links
License
MIT — see LICENSE.
Postio is a trading name of Onno Group Limited, registered in England & Wales (company no. 08622799). Registered office: Suite 22 Trym Lodge, 1 Henbury Road, Westbury-On-Trym, Bristol BS9 3HQ.
Metadata
Release files for postio 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| postio-0.1.2.tar.gz | 10.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| postio-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 22.5 kB
Release files / postio-0.1.2.tar.gz
| Download URL | postio-0.1.2.tar.gz |
|---|---|
| Size | 10.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3799f4833f56e0ba2900afcf60bc3883573cd4a08ef324300439f183ee80fd46
|
|
BLAKE2b-256 checksum How to use checksums |
36c99953692c3873d8dda95d436574c06536c31fd5f81a32fef07bd999cf613e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 12, 2026.
Transparency logRelease files / postio-0.1.2-py3-none-any.whl
| Download URL | postio-0.1.2-py3-none-any.whl |
|---|---|
| Size | 11.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0eb6b9fe8979dee808ff0c72f3ebb7774e15cbf94ebe4dcf366e7cc16845c825
|
|
BLAKE2b-256 checksum How to use checksums |
c5e8e18b61a12edb51ee2cce4a0748543b94e5c7467803c0d67de4a823f6eb09
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 12, 2026.
Transparency log