From 71cdc5b3c6545a6abd372517adc36d0a2bfea8f0 Mon Sep 17 00:00:00 2001 From: mkbadeniyi Date: Thu, 8 Oct 2026 09:04:56 +0100 Subject: [PATCH 1/7] docs(python): show a lookup through to its result --- docs/llms-full.txt | 16 ++++++++++++---- python/README.md | 16 ++++++++++++---- python/tests/test_readme.py | 4 +++- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/docs/llms-full.txt b/docs/llms-full.txt index d40e916..7a854e9 100644 --- a/docs/llms-full.txt +++ b/docs/llms-full.txt @@ -276,20 +276,28 @@ A well-formed code is not necessarily assigned to a building. Only the API can c ```python from ng_postcode import Postcode, parse -from ng_postcode.api import lookup -from ng_postcode.client import Client +from ng_postcode.api import ApiError, lookup +from ng_postcode.client import Client, TransportError -code = parse("EK-01-A03-FK-01") +code = parse("FC-03-B06-AG-12") assert isinstance(code, Postcode) with Client(api_key="nipost_live_...") as client: - found = client.send(lookup(code, level=2)) + found = client.send(lookup(code, level=1)) + +match found: + case ApiError() | TransportError(): + print(found) # e.g. "invalid_api_key (401): ..." + case _: + print(found.valid, found.status) # True valid ``` `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. +Get a key from the [NIPOST developer dashboard](https://dashboard.postcode.gov.ng). Level 1 lookups confirm a code is assigned; levels 2 and up return address details and consume credits. + ## License MIT diff --git a/python/README.md b/python/README.md index aa4a2f2..95285fc 100644 --- a/python/README.md +++ b/python/README.md @@ -49,20 +49,28 @@ A well-formed code is not necessarily assigned to a building. Only the API can c ```python from ng_postcode import Postcode, parse -from ng_postcode.api import lookup -from ng_postcode.client import Client +from ng_postcode.api import ApiError, lookup +from ng_postcode.client import Client, TransportError -code = parse("EK-01-A03-FK-01") +code = parse("FC-03-B06-AG-12") assert isinstance(code, Postcode) with Client(api_key="nipost_live_...") as client: - found = client.send(lookup(code, level=2)) + found = client.send(lookup(code, level=1)) + +match found: + case ApiError() | TransportError(): + print(found) # e.g. "invalid_api_key (401): ..." + case _: + print(found.valid, found.status) # True valid ``` `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. +Get a key from the [NIPOST developer dashboard](https://dashboard.postcode.gov.ng). Level 1 lookups confirm a code is assigned; levels 2 and up return address details and consume credits. + ## License MIT diff --git a/python/tests/test_readme.py b/python/tests/test_readme.py index 72d8fe7..65e0a5d 100644 --- a/python/tests/test_readme.py +++ b/python/tests/test_readme.py @@ -13,7 +13,9 @@ def nipost(request: httpx.Request) -> httpx.Response: - return httpx.Response(200, json={"data": {"postcode": "EK-01-A03-FK-01", "valid": True}}) + return httpx.Response( + 200, json={"data": {"postcode": "FC-03-B06-AG-12", "valid": True, "status": "valid"}} + ) def test_the_readme_has_examples() -> None: From 037adf1c78b4dad6218a157228a63e3a3df3780d Mon Sep 17 00:00:00 2001 From: mkbadeniyi Date: Thu, 8 Oct 2026 09:05:24 +0100 Subject: [PATCH 2/7] docs(site): show a lookup through to its result --- docs/index.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/index.md b/docs/index.md index 1c41ffb..208ca31 100644 --- a/docs/index.md +++ b/docs/index.md @@ -59,11 +59,17 @@ A well-formed code is not necessarily assigned to a building. Only the NIPOST AP ```python from ng_postcode import Postcode -from ng_postcode.api import lookup -from ng_postcode.client import Client # pip install "ng-postcode[client]" +from ng_postcode.api import ApiError, lookup +from ng_postcode.client import Client, TransportError # pip install "ng-postcode[client]" with Client(api_key="nipost_live_...") as client: found = client.send(lookup(Postcode("FC03B06AG12"), level=1)) + +match found: + case ApiError() | TransportError(): + print(found) # e.g. "invalid_api_key (401): ..." + case _: + print(found.valid, found.status) # True valid ``` ```rust @@ -73,7 +79,7 @@ let client = Client::new(std::env::var("NG_POSTCODE_API_KEY")?); let found = client.send(&api::lookup("FC-03-B06-AG-12".parse()?, 1)?)?; ``` -The API layer also covers autocomplete, reverse geocoding and nearby search. +Level 1 confirms a code is assigned. Levels 2 and up return address details and consume credits. The API layer also covers autocomplete, reverse geocoding and nearby search. ## Use it from an AI assistant From 8649f72bde4eccdbb7599f6fcbe3c709f78141c4 Mon Sep 17 00:00:00 2001 From: mkbadeniyi Date: Thu, 8 Oct 2026 09:05:55 +0100 Subject: [PATCH 3/7] docs(readme): say which form to store, show, and exchange --- CRATE.md | 10 +++++----- README.md | 6 ++++++ docs/llms-full.txt | 26 ++++++++++++++++---------- python/README.md | 10 +++++----- 4 files changed, 32 insertions(+), 20 deletions(-) diff --git a/CRATE.md b/CRATE.md index 22000d0..0785eb1 100644 --- a/CRATE.md +++ b/CRATE.md @@ -11,11 +11,11 @@ Part of the [ng-postcode project](https://github.com/Adeniyikayodee/ng-postcode) 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` | +| Form | Example | Use it to | +| --- | --- | --- | +| Canonical | `EK-01-A03-FK-01` | Write a code and pass it between systems | +| Spaced | `EK 01 A03 FK 01` | Show a code to people | +| Compact | `EK01A03FK01` | Store and compare codes | 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])$` diff --git a/README.md b/README.md index 7237bdc..b61b132 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,12 @@ Reference: https://adeniyikayodee.github.io/ng-postcode/llms.txt | Area | `FK` | 2 letters | | Building unit | `01` | 2 digits, 01 to 99 | +| Form | Example | Use it to | +| --- | --- | --- | +| Canonical | `EK-01-A03-FK-01` | Write a code and pass it between systems | +| Spaced | `EK 01 A03 FK 01` | Show a code to people | +| Compact | `EK01A03FK01` | Store and compare codes | + Input may be hyphenated, spaced or compact, in either case. The compact form matches `^[A-Z]{2}(0[1-9]|[1-9][0-9])[A-Z0-9]{3}[A-Z]{2}(0[1-9]|[1-9][0-9])$`. A well-formed code is not necessarily assigned to a building; only the NIPOST API can confirm that. ## Passing a postcode between systems diff --git a/docs/llms-full.txt b/docs/llms-full.txt index 7a854e9..7fe9713 100644 --- a/docs/llms-full.txt +++ b/docs/llms-full.txt @@ -162,6 +162,12 @@ Reference: https://adeniyikayodee.github.io/ng-postcode/llms.txt | Area | `FK` | 2 letters | | Building unit | `01` | 2 digits, 01 to 99 | +| Form | Example | Use it to | +| --- | --- | --- | +| Canonical | `EK-01-A03-FK-01` | Write a code and pass it between systems | +| Spaced | `EK 01 A03 FK 01` | Show a code to people | +| Compact | `EK01A03FK01` | Store and compare codes | + Input may be hyphenated, spaced or compact, in either case. The compact form matches `^[A-Z]{2}(0[1-9]|[1-9][0-9])[A-Z0-9]{3}[A-Z]{2}(0[1-9]|[1-9][0-9])$`. A well-formed code is not necessarily assigned to a building; only the NIPOST API can confirm that. ## Passing a postcode between systems @@ -240,11 +246,11 @@ pip install "ng-postcode[client]" # adds the API client (httpx) 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` | +| Form | Example | Use it to | +| --- | --- | --- | +| Canonical | `EK-01-A03-FK-01` | Write a code and pass it between systems | +| Spaced | `EK 01 A03 FK 01` | Show a code to people | +| Compact | `EK01A03FK01` | Store and compare codes | 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])$` @@ -319,11 +325,11 @@ Part of the [ng-postcode project](https://github.com/Adeniyikayodee/ng-postcode) 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` | +| Form | Example | Use it to | +| --- | --- | --- | +| Canonical | `EK-01-A03-FK-01` | Write a code and pass it between systems | +| Spaced | `EK 01 A03 FK 01` | Show a code to people | +| Compact | `EK01A03FK01` | Store and compare codes | 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])$` diff --git a/python/README.md b/python/README.md index 95285fc..741f804 100644 --- a/python/README.md +++ b/python/README.md @@ -13,11 +13,11 @@ pip install "ng-postcode[client]" # adds the API client (httpx) 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` | +| Form | Example | Use it to | +| --- | --- | --- | +| Canonical | `EK-01-A03-FK-01` | Write a code and pass it between systems | +| Spaced | `EK 01 A03 FK 01` | Show a code to people | +| Compact | `EK01A03FK01` | Store and compare codes | 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])$` From ec4ffd034321978f94fee1a2dfe2cbbc7f048849 Mon Sep 17 00:00:00 2001 From: mkbadeniyi Date: Thu, 8 Oct 2026 09:06:17 +0100 Subject: [PATCH 4/7] docs(readme): open the quick start with offline parsing --- README.md | 36 +++++++++++++++++++----------------- docs/llms-full.txt | 36 +++++++++++++++++++----------------- 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index b61b132..73855fa 100644 --- a/README.md +++ b/README.md @@ -29,23 +29,7 @@ For online stores, [ng-postcode-woocommerce](https://github.com/Adeniyikayodee/n ## Quick start -**AI assistants** - -The MCP server works with any MCP client. It runs over stdio as `uvx ng-postcode-mcp`, with the API key in the environment. Most clients take this entry in their MCP settings: - -```json -{ - "mcpServers": { - "ng-postcode": { - "command": "uvx", - "args": ["ng-postcode-mcp"], - "env": { "NG_POSTCODE_API_KEY": "nipost_live_..." } - } - } -} -``` - -To run it with Node, use `"command": "npx"` and `"args": ["-y", "ng-postcode-mcp"]`; that edition has every tool except address resolution. Per-client steps for Cursor, VS Code, Codex, Claude and others are in the [MCP server README](mcp#install). Validation works without a key. The server is listed in the MCP Registry as `io.github.Adeniyikayodee/ng-postcode`. +Parsing and validation are offline and need no key. **Python** @@ -92,6 +76,24 @@ assert_eq!(code.to_string(), "EK-01-A03-FK-01"); assert_eq!(code.prefix(Segment::Area), "EK-01-A03-FK"); ``` +**AI assistants** + +The MCP server works with any MCP client. It runs over stdio as `uvx ng-postcode-mcp`, with the API key in the environment. Most clients take this entry in their MCP settings: + +```json +{ + "mcpServers": { + "ng-postcode": { + "command": "uvx", + "args": ["ng-postcode-mcp"], + "env": { "NG_POSTCODE_API_KEY": "nipost_live_..." } + } + } +} +``` + +To run it with Node, use `"command": "npx"` and `"args": ["-y", "ng-postcode-mcp"]`; that edition has every tool except address resolution. Per-client steps for Cursor, VS Code, Codex, Claude and others are in the [MCP server README](mcp#install). Validation works without a key; the other tools need one from the [NIPOST developer dashboard](https://dashboard.postcode.gov.ng). The server is listed in the MCP Registry as `io.github.Adeniyikayodee/ng-postcode`. + ## For coding agents Language models trained before October 2026 expect a six-digit Nigerian postcode. To point a coding agent at the current format, paste this into your project's `AGENTS.md`, `CLAUDE.md`, or editor rules: diff --git a/docs/llms-full.txt b/docs/llms-full.txt index 7fe9713..4b1d33f 100644 --- a/docs/llms-full.txt +++ b/docs/llms-full.txt @@ -77,23 +77,7 @@ For online stores, [ng-postcode-woocommerce](https://github.com/Adeniyikayodee/n ## Quick start -**AI assistants** - -The MCP server works with any MCP client. It runs over stdio as `uvx ng-postcode-mcp`, with the API key in the environment. Most clients take this entry in their MCP settings: - -```json -{ - "mcpServers": { - "ng-postcode": { - "command": "uvx", - "args": ["ng-postcode-mcp"], - "env": { "NG_POSTCODE_API_KEY": "nipost_live_..." } - } - } -} -``` - -To run it with Node, use `"command": "npx"` and `"args": ["-y", "ng-postcode-mcp"]`; that edition has every tool except address resolution. Per-client steps for Cursor, VS Code, Codex, Claude and others are in the [MCP server README](mcp#install). Validation works without a key. The server is listed in the MCP Registry as `io.github.Adeniyikayodee/ng-postcode`. +Parsing and validation are offline and need no key. **Python** @@ -140,6 +124,24 @@ assert_eq!(code.to_string(), "EK-01-A03-FK-01"); assert_eq!(code.prefix(Segment::Area), "EK-01-A03-FK"); ``` +**AI assistants** + +The MCP server works with any MCP client. It runs over stdio as `uvx ng-postcode-mcp`, with the API key in the environment. Most clients take this entry in their MCP settings: + +```json +{ + "mcpServers": { + "ng-postcode": { + "command": "uvx", + "args": ["ng-postcode-mcp"], + "env": { "NG_POSTCODE_API_KEY": "nipost_live_..." } + } + } +} +``` + +To run it with Node, use `"command": "npx"` and `"args": ["-y", "ng-postcode-mcp"]`; that edition has every tool except address resolution. Per-client steps for Cursor, VS Code, Codex, Claude and others are in the [MCP server README](mcp#install). Validation works without a key; the other tools need one from the [NIPOST developer dashboard](https://dashboard.postcode.gov.ng). The server is listed in the MCP Registry as `io.github.Adeniyikayodee/ng-postcode`. + ## For coding agents Language models trained before October 2026 expect a six-digit Nigerian postcode. To point a coding agent at the current format, paste this into your project's `AGENTS.md`, `CLAUDE.md`, or editor rules: From 3e1a2448878403cf8ada601697a51529480f9c42 Mon Sep 17 00:00:00 2001 From: mkbadeniyi Date: Thu, 8 Oct 2026 09:06:17 +0100 Subject: [PATCH 5/7] docs(readme): route readers from a task to a package --- README.md | 10 ++++++++++ docs/llms-full.txt | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/README.md b/README.md index 73855fa..7f794af 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,16 @@ Developer tools for Nigeria's National Digital Alphanumeric Postcode System (NDA A postcode has 11 characters in five segments, written `EK-01-A03-FK-01`: state, LGA, district, area and building unit. +## Start here + +| To | Use | +| --- | --- | +| Check, tidy, or format a code someone typed | The library for your language. It works offline, with no key. | +| Confirm a code is assigned to a building | The same library's API client, with a key from the [NIPOST developer dashboard](https://dashboard.postcode.gov.ng) | +| Give an AI assistant postcode tools | `ng-postcode-mcp` | +| Find the postcode for a described address or a location pin | `ng-address-resolver`, or the `resolve_address` tool of the Python MCP server (both pre-release) | +| Take postcodes at a WooCommerce checkout | [ng-postcode-woocommerce](https://github.com/Adeniyikayodee/ng-postcode-woocommerce) | + ## Packages | Package | What it does | Install | Source | diff --git a/docs/llms-full.txt b/docs/llms-full.txt index 4b1d33f..d23fe2a 100644 --- a/docs/llms-full.txt +++ b/docs/llms-full.txt @@ -59,6 +59,16 @@ Developer tools for Nigeria's National Digital Alphanumeric Postcode System (NDA A postcode has 11 characters in five segments, written `EK-01-A03-FK-01`: state, LGA, district, area and building unit. +## Start here + +| To | Use | +| --- | --- | +| Check, tidy, or format a code someone typed | The library for your language. It works offline, with no key. | +| Confirm a code is assigned to a building | The same library's API client, with a key from the [NIPOST developer dashboard](https://dashboard.postcode.gov.ng) | +| Give an AI assistant postcode tools | `ng-postcode-mcp` | +| Find the postcode for a described address or a location pin | `ng-address-resolver`, or the `resolve_address` tool of the Python MCP server (both pre-release) | +| Take postcodes at a WooCommerce checkout | [ng-postcode-woocommerce](https://github.com/Adeniyikayodee/ng-postcode-woocommerce) | + ## Packages | Package | What it does | Install | Source | From eef35c61d813178c7404b09afbadc25fb10bfc4d Mon Sep 17 00:00:00 2001 From: mkbadeniyi Date: Thu, 8 Oct 2026 09:06:17 +0100 Subject: [PATCH 6/7] docs(readme): explain the two names people get wrong --- README.md | 2 ++ docs/llms-full.txt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README.md b/README.md index 7f794af..cd50d9a 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,8 @@ A postcode has 11 characters in five segments, written `EK-01-A03-FK-01`: state, Each package has its own README with full usage. +Two names are easy to get wrong. On npm the library is `ng-postcode-js`, because `ng-postcode` there is another project. `ng-address-resolver` is imported as `ng_address`. + For online stores, [ng-postcode-woocommerce](https://github.com/Adeniyikayodee/ng-postcode-woocommerce) is a WooCommerce plugin built on the same rules and the same shared test cases: it checks and tidies the postcode at checkout, finds it from the customer's location, and makes shipping zones match on postcode prefixes. ## Quick start diff --git a/docs/llms-full.txt b/docs/llms-full.txt index d23fe2a..2ff7b0f 100644 --- a/docs/llms-full.txt +++ b/docs/llms-full.txt @@ -83,6 +83,8 @@ A postcode has 11 characters in five segments, written `EK-01-A03-FK-01`: state, Each package has its own README with full usage. +Two names are easy to get wrong. On npm the library is `ng-postcode-js`, because `ng-postcode` there is another project. `ng-address-resolver` is imported as `ng_address`. + For online stores, [ng-postcode-woocommerce](https://github.com/Adeniyikayodee/ng-postcode-woocommerce) is a WooCommerce plugin built on the same rules and the same shared test cases: it checks and tidies the postcode at checkout, finds it from the customer's location, and makes shipping zones match on postcode prefixes. ## Quick start From 0fbacf4d3339c07138ea16aa34eb3699b06519a2 Mon Sep 17 00:00:00 2001 From: mkbadeniyi Date: Thu, 8 Oct 2026 09:06:41 +0100 Subject: [PATCH 7/7] docs(site): give the live API differences their own page --- CONTRIBUTING.md | 6 +++--- README.md | 22 +------------------ docs/api-differences.md | 18 ++++++++++++++++ docs/llms-full.txt | 47 ++++++++++++++++++++++------------------- docs/llms.txt | 2 +- scripts/llms_full.py | 1 + 6 files changed, 49 insertions(+), 47 deletions(-) create mode 100644 docs/api-differences.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 14d6739..2fd91f0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -104,9 +104,9 @@ it, and makes only free calls unless you pass `--paid`. - Never commit a key, and never put one in an issue or a pull request. - Level 2 and above return a person's address. Do not commit captured responses that contain one. -- When the live API differs from its documentation, record it in the README - under "Where the live API differs from its docs", with the date observed, - and capture the body in `spec/responses.json` if it holds no personal data. +- When the live API differs from its documentation, record it in + `docs/api-differences.md`, with the date observed, and capture the body in + `spec/responses.json` if it holds no personal data. ## Branches and pull requests diff --git a/README.md b/README.md index cd50d9a..aa733d4 100644 --- a/README.md +++ b/README.md @@ -152,27 +152,7 @@ Input may be hyphenated, spaced or compact, in either case. The compact form mat - The NIPOST API needs a key for every endpoint, from the [developer dashboard](https://dashboard.postcode.gov.ng). Offline validation needs nothing. - The API layer is tested against responses captured from the live API with a level 1 key, kept in [`spec/responses.json`](spec/responses.json). Run [`scripts/live_check.py`](scripts/live_check.py) with your own key to repeat the comparison. Lookup levels 2 and up need a higher-access key and are tested only against NIPOST's documented examples. - The resolver and the `resolve_address` tool are pre-release. They work against the live API, but their accuracy on real addresses is unmeasured. Described addresses need a geocoder you run or pay for; text alone rarely identifies a building, so ask users for a location pin when the exact building matters. - -### Where the live API differs from its docs - -Observed on 3 October 2026: - -- Every endpoint needs a key, including search, assembly and level 1 lookup, which the docs describe as public. -- Lookup also returns `status` (`valid`, `not_found`, `invalid`) and `verified`. A malformed code is answered with HTTP 200 and `status: invalid`. -- Autocomplete suggestions carry only `code`, the value of the next segment. The documented `label` is not sent. -- Reverse geocoding also returns `depth`. -- Nearby search, which the docs leave unspecified, returns a list of `postcode`, `display` and `distance_m`, nearest first. -- Asking for a level the key lacks returns `403 level_not_granted`. -- An empty autocomplete query never gets a response, so the libraries refuse to send one. -- `EK-01-A03-FK-01`, the example used throughout NIPOST's docs, is reported as not assigned. - -Observed on 6 October 2026, with a level 1 test key: - -- An empty autocomplete query is answered, with no suggestions. The libraries still refuse to send one. -- `FC-03-B06-AG-12`, autocomplete, reverse geocoding and nearby search all return empty answers where [`spec/responses.json`](spec/responses.json) records data. `scripts/live_check.py` lists each difference. -- A lookup level the API cannot read, or `0`, is answered at level 1. -- A latitude outside -90 to 90 returns `500 internal`. -- A request the load balancer rejects, such as a 5,000-character autocomplete query, returns `403` with an HTML body. +- Where the live API differs from NIPOST's documentation is recorded, with the date of each observation, in [`docs/api-differences.md`](docs/api-differences.md). ## Development diff --git a/docs/api-differences.md b/docs/api-differences.md new file mode 100644 index 0000000..b41eab6 --- /dev/null +++ b/docs/api-differences.md @@ -0,0 +1,18 @@ +# Where the live API differs from its docs + +What `api.postcode.gov.ng` does where [NIPOST's documentation](https://docs.postcode.gov.ng) says otherwise or says nothing. Each row gives the date it was last observed. The 6 October observations were made with a level 1 test key. To repeat the comparison with your own key, run [`scripts/live_check.py`](https://github.com/Adeniyikayodee/ng-postcode/blob/main/scripts/live_check.py). + +| Topic | The live API | Observed | +| --- | --- | --- | +| Keys | Every endpoint needs a key, including search, assembly and level 1 lookup, which the docs describe as public. | 3 October 2026 | +| Lookup | Also returns `status` (`valid`, `not_found`, `invalid`) and `verified`. A malformed code is answered with HTTP 200 and `status: invalid`. | 3 October 2026 | +| Lookup level | Asking for a level the key lacks returns `403 level_not_granted`. | 3 October 2026 | +| Lookup level | A level the API cannot read, or `0`, is answered at level 1. | 6 October 2026 | +| Autocomplete | Suggestions carry only `code`, the value of the next segment. The documented `label` is not sent. | 3 October 2026 | +| Autocomplete | An empty query is answered with no suggestions. On 3 October it never got a response, so the libraries refuse to send one. | 6 October 2026 | +| Reverse geocoding | Also returns `depth`. | 3 October 2026 | +| Reverse geocoding | A latitude outside -90 to 90 returns `500 internal`. | 6 October 2026 | +| Nearby search | Returns a list of `postcode`, `display` and `distance_m`, nearest first. The docs leave it unspecified. | 3 October 2026 | +| Load balancer | A request it rejects, such as a 5,000-character autocomplete query, returns `403` with an HTML body. | 6 October 2026 | +| Example code | `EK-01-A03-FK-01`, the example used throughout NIPOST's docs, is reported as not assigned. | 3 October 2026 | +| Test keys | With a level 1 test key, `FC-03-B06-AG-12`, autocomplete, reverse geocoding and nearby search all return empty answers where [`spec/responses.json`](https://github.com/Adeniyikayodee/ng-postcode/blob/main/spec/responses.json) records data. | 6 October 2026 | diff --git a/docs/llms-full.txt b/docs/llms-full.txt index 2ff7b0f..0018baf 100644 --- a/docs/llms-full.txt +++ b/docs/llms-full.txt @@ -35,7 +35,7 @@ Since 1 October 2026 a Nigerian building's postcode is this 11-character code. A - [Java README](https://github.com/Adeniyikayodee/ng-postcode/blob/main/java/README.md) - [MCP server README](https://github.com/Adeniyikayodee/ng-postcode/blob/main/mcp/README.md): per-client setup and configuration - [Resolver README](https://github.com/Adeniyikayodee/ng-postcode/blob/main/agent/README.md) -- [Where the live API differs from its docs](https://github.com/Adeniyikayodee/ng-postcode#where-the-live-api-differs-from-its-docs) +- [Where the live API differs from its docs](https://github.com/Adeniyikayodee/ng-postcode/blob/main/docs/api-differences.md) ## Optional @@ -200,27 +200,7 @@ Input may be hyphenated, spaced or compact, in either case. The compact form mat - The NIPOST API needs a key for every endpoint, from the [developer dashboard](https://dashboard.postcode.gov.ng). Offline validation needs nothing. - The API layer is tested against responses captured from the live API with a level 1 key, kept in [`spec/responses.json`](spec/responses.json). Run [`scripts/live_check.py`](scripts/live_check.py) with your own key to repeat the comparison. Lookup levels 2 and up need a higher-access key and are tested only against NIPOST's documented examples. - The resolver and the `resolve_address` tool are pre-release. They work against the live API, but their accuracy on real addresses is unmeasured. Described addresses need a geocoder you run or pay for; text alone rarely identifies a building, so ask users for a location pin when the exact building matters. - -### Where the live API differs from its docs - -Observed on 3 October 2026: - -- Every endpoint needs a key, including search, assembly and level 1 lookup, which the docs describe as public. -- Lookup also returns `status` (`valid`, `not_found`, `invalid`) and `verified`. A malformed code is answered with HTTP 200 and `status: invalid`. -- Autocomplete suggestions carry only `code`, the value of the next segment. The documented `label` is not sent. -- Reverse geocoding also returns `depth`. -- Nearby search, which the docs leave unspecified, returns a list of `postcode`, `display` and `distance_m`, nearest first. -- Asking for a level the key lacks returns `403 level_not_granted`. -- An empty autocomplete query never gets a response, so the libraries refuse to send one. -- `EK-01-A03-FK-01`, the example used throughout NIPOST's docs, is reported as not assigned. - -Observed on 6 October 2026, with a level 1 test key: - -- An empty autocomplete query is answered, with no suggestions. The libraries still refuse to send one. -- `FC-03-B06-AG-12`, autocomplete, reverse geocoding and nearby search all return empty answers where [`spec/responses.json`](spec/responses.json) records data. `scripts/live_check.py` lists each difference. -- A lookup level the API cannot read, or `0`, is answered at level 1. -- A latitude outside -90 to 90 returns `500 internal`. -- A request the load balancer rejects, such as a 5,000-character autocomplete query, returns `403` with an HTML body. +- Where the live API differs from NIPOST's documentation is recorded, with the date of each observation, in [`docs/api-differences.md`](docs/api-differences.md). ## Development @@ -243,6 +223,29 @@ MIT --- + + +# Where the live API differs from its docs + +What `api.postcode.gov.ng` does where [NIPOST's documentation](https://docs.postcode.gov.ng) says otherwise or says nothing. Each row gives the date it was last observed. The 6 October observations were made with a level 1 test key. To repeat the comparison with your own key, run [`scripts/live_check.py`](https://github.com/Adeniyikayodee/ng-postcode/blob/main/scripts/live_check.py). + +| Topic | The live API | Observed | +| --- | --- | --- | +| Keys | Every endpoint needs a key, including search, assembly and level 1 lookup, which the docs describe as public. | 3 October 2026 | +| Lookup | Also returns `status` (`valid`, `not_found`, `invalid`) and `verified`. A malformed code is answered with HTTP 200 and `status: invalid`. | 3 October 2026 | +| Lookup level | Asking for a level the key lacks returns `403 level_not_granted`. | 3 October 2026 | +| Lookup level | A level the API cannot read, or `0`, is answered at level 1. | 6 October 2026 | +| Autocomplete | Suggestions carry only `code`, the value of the next segment. The documented `label` is not sent. | 3 October 2026 | +| Autocomplete | An empty query is answered with no suggestions. On 3 October it never got a response, so the libraries refuse to send one. | 6 October 2026 | +| Reverse geocoding | Also returns `depth`. | 3 October 2026 | +| Reverse geocoding | A latitude outside -90 to 90 returns `500 internal`. | 6 October 2026 | +| Nearby search | Returns a list of `postcode`, `display` and `distance_m`, nearest first. The docs leave it unspecified. | 3 October 2026 | +| Load balancer | A request it rejects, such as a 5,000-character autocomplete query, returns `403` with an HTML body. | 6 October 2026 | +| Example code | `EK-01-A03-FK-01`, the example used throughout NIPOST's docs, is reported as not assigned. | 3 October 2026 | +| Test keys | With a level 1 test key, `FC-03-B06-AG-12`, autocomplete, reverse geocoding and nearby search all return empty answers where [`spec/responses.json`](https://github.com/Adeniyikayodee/ng-postcode/blob/main/spec/responses.json) records data. | 6 October 2026 | + +--- + # ng-postcode diff --git a/docs/llms.txt b/docs/llms.txt index 397929a..9be2e39 100644 --- a/docs/llms.txt +++ b/docs/llms.txt @@ -33,7 +33,7 @@ Since 1 October 2026 a Nigerian building's postcode is this 11-character code. A - [Java README](https://github.com/Adeniyikayodee/ng-postcode/blob/main/java/README.md) - [MCP server README](https://github.com/Adeniyikayodee/ng-postcode/blob/main/mcp/README.md): per-client setup and configuration - [Resolver README](https://github.com/Adeniyikayodee/ng-postcode/blob/main/agent/README.md) -- [Where the live API differs from its docs](https://github.com/Adeniyikayodee/ng-postcode#where-the-live-api-differs-from-its-docs) +- [Where the live API differs from its docs](https://github.com/Adeniyikayodee/ng-postcode/blob/main/docs/api-differences.md) ## Optional diff --git a/scripts/llms_full.py b/scripts/llms_full.py index 09e8e4c..ad85c79 100644 --- a/scripts/llms_full.py +++ b/scripts/llms_full.py @@ -14,6 +14,7 @@ SOURCES = [ "docs/llms.txt", "README.md", + "docs/api-differences.md", "python/README.md", "CRATE.md", "js/packages/ng-postcode-js/README.md",