Skip to main content

Python wrapper for bpost External Mailing Address Proofing API with pydantic models and httpx sync/async clients.

Project description

bpost-address-validator

This is a lightweight Python wrapper for bpost’s External Mailing Address Proofing API endpoint POST /<env>/externalMailingAddressProofingRest/validateAddresses.

Environments have different base URLs and path prefixes. Presets are provided for convenience:

  • prod: https://api.mailops.bpost.cloud/roa-info/...
  • test (NP): https://api.mailops-np.bpost.cloud/roa-info-st2/...
  • uat (NP): https://api.mailops-np.bpost.cloud/roa-info-ac/...

It provides:

  • Synchronous and asynchronous clients powered by httpx
  • Pydantic v2 models for request/response envelopes (flexible inner structure; extra fields allowed)
  • A simple, typed interface and explicit error handling via ApiError

Features

  • Sync client: BpostClient
  • Async client: AsyncBpostClient
  • Automatic x-api-key header handling
  • Default base URL pointing to bpost NP environment (you should usually provide a preset explicitly)
  • Uniform error type ApiError for transport and non-200 responses

Requirements

  • Python 3.12+

Installation

pip install bpost-address-validator

From source (editable):

pip install -e .

Quick start (sync)

from bpost_address_validator import (
    BpostClient,
    ValidateAddressesRequest,
    ValidateAddressesRequestContent,
    AddressToValidateList,
    AddressToValidate,
    ValidateAddressOptions,
    PostalAddress,
    DeliveryPointLocation,
    StructuredDeliveryPointLocation,
    PostalCodeMunicipality,
    StructuredPostalCodeMunicipality,
)

req = ValidateAddressesRequest(
    validate_addresses_request=ValidateAddressesRequestContent(
        address_to_validate_list=AddressToValidateList(
            address_to_validate=[
                AddressToValidate(
                    id="1",
                    dispatching_country_iso_code="BE",
                    delivering_country_iso_code="BE",
                    # Provide either address_block_lines or a structured postal_address
                    postal_address=PostalAddress(
                        delivery_point_location=DeliveryPointLocation(
                            structured_delivery_point_location=StructuredDeliveryPointLocation(
                                street_name="Muntstraat",
                                street_number="1",
                            )
                        ),
                        postal_code_municipality=PostalCodeMunicipality(
                            structured_postal_code_municipality=StructuredPostalCodeMunicipality(
                                postal_code="1000",
                                municipality_name="Bruxelles",
                            )
                        ),
                    ),
                )
            ]
        ),
        validate_address_options=ValidateAddressOptions(
            include_submitted_address=True,
            include_suggestions=True,
            include_formatting=True,
        ),
    )
)

# Use a preset matching your target environment: "prod", "test", or "uat"
with BpostClient(api_key="<YOUR_API_KEY>", preset="test") as client:
    resp = client.validate_addresses(req)
    print(resp.model_dump())

Quick start (async)

import asyncio
from bpost_address_validator import (
    AsyncBpostClient,
    ValidateAddressesRequest,
    ValidateAddressesRequestContent,
    AddressToValidateList,
    AddressToValidate,
)

async def main():
    req = ValidateAddressesRequest(
        validate_addresses_request=ValidateAddressesRequestContent(
            address_to_validate_list=AddressToValidateList(
                address_to_validate=[
                    AddressToValidate(
                        id="1",
                        dispatching_country_iso_code="BE",
                        delivering_country_iso_code="BE",
                    )
                ]
            )
        )
    )
    async with AsyncBpostClient(api_key="<YOUR_API_KEY>", preset="test") as client:
        resp = await client.validate_addresses(req)
        print(resp.model_dump())

asyncio.run(main())

Environment configuration

You can configure the target environment in three ways:

  1. Use a preset (recommended):
# prod
BpostClient(api_key="...", preset="prod")

# test (NP, st2)
BpostClient(api_key="...", preset="test")

# uat (NP, ac)
BpostClient(api_key="...", preset="uat")
  1. Manually set base URL and path prefix:
# Equivalent to preset="prod"
BpostClient(
    api_key="...",
    base_url="https://api.mailops.bpost.cloud",
    environment="roa-info",
)

# Equivalent to preset="test"
BpostClient(
    api_key="...",
    base_url="https://api.mailops-np.bpost.cloud",
    environment="roa-info-st2",
)

# Equivalent to preset="uat"
BpostClient(
    api_key="...",
    base_url="https://api.mailops-np.bpost.cloud",
    environment="roa-info-ac",
)
  1. Advanced: Provide your own httpx client with custom base URL and headers. In that case, pass client= to the constructor and omit api_key or headers accordingly.

Using the typed address models

from bpost_address_validator import (
    BpostClient,
    ValidateAddressesRequest,
    ValidateAddressesRequestContent,
    AddressToValidateList,
    AddressToValidate,
    ValidateAddressOptions,
    AddressBlockLines,
    UnstructuredAddressLineItem,
    PostalAddress,
    DeliveryPointLocation,
    StructuredDeliveryPointLocation,
    PostalCodeMunicipality,
    StructuredPostalCodeMunicipality,
)

req = ValidateAddressesRequest(
    validate_addresses_request=ValidateAddressesRequestContent(
        address_to_validate_list=AddressToValidateList(
            address_to_validate=[
                AddressToValidate(
                    id="1",
                    dispatching_country_iso_code="BE",
                    delivering_country_iso_code="BE",
                    # Option A: unstructured address lines
                    address_block_lines=AddressBlockLines(
                        unstructured_address_line=[
                            UnstructuredAddressLineItem(body="Muntstraat 1", locale="nl")
                        ]
                    ),
                    # Option B: structured postal address (can be used instead of address_block_lines)
                    postal_address=PostalAddress(
                        delivery_point_location=DeliveryPointLocation(
                            structured_delivery_point_location=StructuredDeliveryPointLocation(
                                street_name="Muntstraat", street_number="1"
                            )
                        ),
                        postal_code_municipality=PostalCodeMunicipality(
                            structured_postal_code_municipality=StructuredPostalCodeMunicipality(
                                postal_code="1000", municipality_name="Bruxelles"
                            )
                        ),
                    ),
                )
            ]
        ),
        validate_address_options=ValidateAddressOptions(include_formatting=True),
    )
)

with BpostClient(api_key="<YOUR_API_KEY>") as client:
    resp = client.validate_addresses(req)
    # Access typed response envelope
    print(resp.validate_addresses_response is not None)

Passing a raw dict payload

from bpost_address_validator import BpostClient

payload = {
    "ValidateAddressesRequest": {
        "AddressToValidateList": {
            "AddressToValidate": [
                {
                    "@id": "1",
                    "DispatchingCountryISOCode": "BE",
                    "DeliveringCountryISOCode": "BE",
                    "PostalAddress": {
                        "DeliveryPointLocation": {
                            "StructuredDeliveryPointLocation": {
                                "StreetName": "Muntstraat",
                                "StreetNumber": "1",
                            }
                        },
                        "PostalCodeMunicipality": {
                            "StructuredPostalCodeMunicipality": {
                                "PostalCode": "1000",
                                "MunicipalityName": "Bruxelles",
                            }
                        },
                    },
                }
            ]
        },
        "ValidateAddressOptions": {"IncludeFormatting": True},
    }
}

with BpostClient(api_key="<YOUR_API_KEY>") as client:
    resp = client.validate_addresses(payload)
    print(resp.validate_addresses_response is not None)

API overview

  • Clients

    • BpostClient(api_key: str, base_url: str = DEFAULT, timeout: float | None = 30.0)
      • validate_addresses(payload) -> ValidateAddressesResponse
    • AsyncBpostClient(api_key: str, base_url: str = DEFAULT, timeout: float | None = 30.0)
      • await validate_addresses(payload) -> ValidateAddressesResponse
  • Models (selected)

    • ValidateAddressesRequest
    • ValidateAddressesRequestContent
    • AddressToValidateList
    • AddressToValidate
    • AddressBlockLines, UnstructuredAddressLineItem
    • PostalAddress, DeliveryPointLocation, StructuredDeliveryPointLocation
    • PostalCodeMunicipality, StructuredPostalCodeMunicipality
    • ValidateAddressOptions
    • ValidateAddressesResponse
  • Errors

    • ApiError — for transport errors and non-200 responses. Inspect status_code and details for context.

Request/Response envelopes

  • Request body root: {"ValidateAddressesRequest": {...}}
  • Response body root: {"ValidateAddressesResponse": {...}}

Models allow extra fields to preserve forward-compatibility with upstream changes. See externalMailaddressProofingAPI-OpenAPIspec_v3.yaml for the full schema reference.

Environments and base URL

  • Default base URL (NP): https://api.mailops-np.bpost.cloud
  • Endpoint path: /roa-info-st/externalMailingAddressProofingRest/validateAddresses You can override the base URL in the client constructor.

Error handling

  • Transport issues raise ApiError("HTTP transport error").
  • Non-200 responses raise ApiError with status_code and parsed details (JSON when available).

Example:

from bpost_address_validator import BpostClient, ApiError

try:
    with BpostClient(api_key="bad-key") as client:
        client.validate_addresses({"ValidateAddressesRequest": {}})
except ApiError as e:
    print(e.status_code, e.details)

Authentication

Provide your API key via the api_key parameter. It is sent as x-api-key automatically.

Typing strategy

  • Pydantic v2 models are used for the outer envelopes and key nested structures.
  • Public attributes are Pythonic snake_case; JSON aliases match the API (e.g., dispatching_country_iso_code -> DispatchingCountryISOCode).
  • Typed models are provided for address_block_lines and postal_address structures (including delivery point location and postal code/municipality).
  • Extra keys are allowed across models to avoid breakage if bpost adds new fields.
  • For attributes like "@id", the models expose proper field aliases (e.g., Field(alias="@id")).

Development

  • Python 3.12+
  • Runtime deps: httpx>=0.27.0, pydantic>=2.7.0
  • No tests yet — PRs welcome (tests and deeper typed models).

License

MIT — see LICENSE if provided, otherwise follow repository policy.

Disclaimer

This project is not affiliated with or endorsed by bpost. Use at your own risk and comply with bpost’s terms.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bpost_address_validator-0.1.5.tar.gz (4.7 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

bpost_address_validator-0.1.5-py3-none-any.whl (10.4 kB view details)

Uploaded Python 3

File details

Details for the file bpost_address_validator-0.1.5.tar.gz.

File metadata

  • Download URL: bpost_address_validator-0.1.5.tar.gz
  • Upload date:
  • Size: 4.7 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for bpost_address_validator-0.1.5.tar.gz
Algorithm Hash digest
SHA256 a5c8c33db9b3575af1c18e3a6165f89d8ecc10eaac6909df430905d8390829b6
MD5 ad7a2658c64fbda0841d4307de78c4bf
BLAKE2b-256 9df16c397bb31eb540c0680bd76f64a1366e71221575c8eb18ea88fb452ba4df

See more details on using hashes here.

Provenance

The following attestation bundles were made for bpost_address_validator-0.1.5.tar.gz:

Publisher: publish.yml on fromej-dev/bpost-address-validator

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file bpost_address_validator-0.1.5-py3-none-any.whl.

File metadata

File hashes

Hashes for bpost_address_validator-0.1.5-py3-none-any.whl
Algorithm Hash digest
SHA256 927bdeb3531d30c0aa4d0154ca2f8c8aa448168dd6bd418278324c9f49313038
MD5 089f9cc27b815a8ee88ffac9ec58bdbd
BLAKE2b-256 b694fa73152620f9ae2045ba0836b649aa377460d2657a3a96354d958ea0563f

See more details on using hashes here.

Provenance

The following attestation bundles were made for bpost_address_validator-0.1.5-py3-none-any.whl:

Publisher: publish.yml on fromej-dev/bpost-address-validator

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page