Metadata-Version: 2.5
Name: ng-postcode
Version: 0.2.1
Summary: Parse, validate and format Nigeria's NIPOST digital postcode (NDAPS) offline, plus a client for the postcode.gov.ng API.
Project-URL: Repository, https://github.com/Adeniyikayodee/ng-postcode
Project-URL: Issues, https://github.com/Adeniyikayodee/ng-postcode/issues
Project-URL: API docs, https://docs.postcode.gov.ng
Author: Kayode Adeniyi
License-Expression: MIT
License-File: LICENSE
Keywords: address,address-validation,geocoding,ndaps,nigeria,nipost,postal-code,postcode
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: GIS
Classifier: Topic :: Software Development :: Libraries
Classifier: Typing :: Typed
Requires-Python: >=3.10
Provides-Extra: client
Requires-Dist: httpx>=0.27; extra == 'client'
Description-Content-Type: text/markdown

# ng-postcode

Python library for Nigeria's National Digital Alphanumeric Postcode System (NDAPS), the building-level postcode NIPOST launched in October 2026. Parse, validate and format postcodes offline, and call the [postcode.gov.ng](https://docs.postcode.gov.ng) API for lookup, autocomplete and reverse geocoding.

Also available for Rust: [`ng-postcode` on crates.io](https://crates.io/crates/ng-postcode).

```sh
pip install ng-postcode            # offline core, no dependencies
pip install "ng-postcode[client]"  # adds the API client (httpx)
```

## Format

An 11-character code in five segments: state, LGA, district, area, building unit.

| Style | Example |
| --- | --- |
| Canonical | `EK-01-A03-FK-01` |
| Display | `EK 01 A03 FK 01` |
| Compact | `EK01A03FK01` |

Compact form as a regular expression: `^[A-Z]{2}(0[1-9]|[1-9][0-9])[A-Z0-9]{3}[A-Z]{2}(0[1-9]|[1-9][0-9])$`

## Offline

Expected failures are returned as values, not raised, so the type checker makes you handle them.

```python
from ng_postcode import Postcode, Segment, parse

match parse("ek 01 a03 fk 01"):
    case Postcode() as code:
        print(code)  # EK-01-A03-FK-01
        print(code.compact)  # EK01A03FK01, store this
        print(code.spaced)  # EK 01 A03 FK 01
        print(code.prefix(Segment.AREA))  # EK-01-A03-FK
    case error:
        print(error)  # e.g. "invalid lga segment"
```

- `parse` accepts hyphenated, spaced or compact input in either case.
- `parse_lenient` first swaps look-alikes that cannot occur where they stand (`O`/`0`, `I`/`1`, `S`/`5`, `B`/`8`) and reports how many it changed.
- `from_segments` assembles a code from its parts and zero-fills the LGA and unit.
- `Postcode` is immutable, hashable and sorts by state, LGA, district, area, unit.

A well-formed code is not necessarily assigned to a building. Only the API can confirm that a postcode exists.

## API

```python
from ng_postcode import Postcode, parse
from ng_postcode.api import lookup
from ng_postcode.client import Client

code = parse("EK-01-A03-FK-01")
assert isinstance(code, Postcode)

with Client(api_key="nipost_live_...") as client:
    found = client.send(lookup(code, level=2))
```

`send` returns the typed response, an `ApiError` (for example `auth_required` or `insufficient_credits`) or a `TransportError`. `AsyncClient` has the same interface for asyncio.

`ng_postcode.api` covers lookup, autocomplete, reverse geocoding and nearby search. Each function returns a `Request` value and `decode` turns a status and body into a typed result, so it works with any HTTP client without the `client` extra.

## License

MIT
