{"openapi":"3.1.0","info":{"title":"NIPOST Postcode API mock (unofficial)","version":"0.1.1","description":"**Unofficial.** A mock of the [NIPOST Postcode API](https://docs.postcode.gov.ng), not affiliated with NIPOST or the Federal Ministry of Communications, Innovation and Digital Economy.\n\nSame paths, parameters and response shapes as the real API, built from its public docs and OpenAPI spec. No sign-up: any key, or none, gets every lookup level. Apart from NIPOST's published test postcodes, the data is mock data, labelled `MOCK`, and every body carries `\"mock\": true`.\n\n**Reserved keys.** Every endpoint has an `X-API-Key` dropdown. Leave it empty for full access at every level, or pick a key to exercise error handling:\n\n| Key | Behaviour |\n|---|---|\n| `mock_level_1` | Lookups capped at level 1; asking for more returns the fields up to L1 |\n| `mock_level_2` | Lookups capped at level 2; asking for more returns the fields up to L2 |\n| `mock_level_3` | Lookups capped at level 3; asking for more returns the fields up to L3 |\n| `mock_level_4` | Lookups capped at level 4; asking for more returns the fields up to L4 |\n| `mock_level_5` | Lookups capped at level 5; asking for more returns the fields up to L5 |\n| `mock_no_credits` | 402 `insufficient_credits` on Lookup L2+ |\n| `mock_no_scope` | 403 `insufficient_scope` on Lookup L2+ |\n| `mock_rate_limited` | 429 `rate_limited` on every call |\n| `mock_invalid` | 401 `invalid_api_key` |\n| `mock_no_key` | 401 `auth_required`, as the real gateway answers today with no key |\n\nSource and npm package: [github.com/poliha/ng-postcode](https://github.com/poliha/ng-postcode). To switch to the real API, use `https://api.postcode.gov.ng` with your own key.","license":{"name":"MIT","url":"https://github.com/poliha/ng-postcode/blob/main/LICENSE"}},"servers":[{"url":"/","description":"This mock"}],"tags":[{"name":"Lookup"},{"name":"Search"},{"name":"Assembly","description":"Build and parse postcodes. The same rules work offline with the `ng-postcode` package."}],"paths":{"/v1/lookup":{"get":{"tags":["Lookup"],"summary":"Graded postcode lookup (levels 1–5, cumulative)","description":"L1 is validity only. L2 adds the administrative and recent house address, L3 building use, L4 other building info, L5 a point. Fields beyond L2 are mock data, and the L4/L5 shapes are guessed because NIPOST has not published them.","parameters":[{"name":"code","in":"query","required":true,"schema":{"type":"string"},"example":"LA-11-W06-TC-10","description":"Any style: `LA-11-W06-TC-10`, `LA 11 W06 TC 10`, `la11w06tc10`"},{"name":"level","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":5,"default":1},"example":3},{"$ref":"#/components/parameters/ApiKey"}],"responses":{"200":{"description":"Graded attributes. A malformed code returns `valid: false`.","content":{"application/json":{"schema":{"type":"object","required":["data","mock"],"properties":{"data":{"$ref":"#/components/schemas/LookupResponse"},"mock":{"type":"boolean","const":true,"description":"Marks a mock response. The real API does not send it."}}}}},"headers":{"X-RateLimit-Limit":{"schema":{"type":"integer","example":600}},"X-RateLimit-Remaining":{"schema":{"type":"integer"}},"X-Mock":{"schema":{"type":"string","const":"true"}}}},"400":{"description":"Missing code, or level outside 1–5","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`mock_invalid` → `invalid_api_key`; `mock_no_key` → `auth_required`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"`mock_no_credits` on L2+","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`mock_no_scope` on L2+","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`mock_rate_limited` → `rate_limited`, with `Retry-After: 60`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/search/autocomplete":{"get":{"tags":["Search"],"summary":"Segment-aware autocomplete","description":"Suggests the segment being typed, from NIPOST's published test postcodes.","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string"},"example":"EK 01","description":"Partial postcode, with or without separators"},{"$ref":"#/components/parameters/ApiKey"}],"responses":{"200":{"description":"Suggestions for the active segment","content":{"application/json":{"schema":{"type":"object","required":["data","mock"],"properties":{"data":{"$ref":"#/components/schemas/AutocompleteResponse"},"mock":{"type":"boolean","const":true,"description":"Marks a mock response. The real API does not send it."}}}}},"headers":{"X-RateLimit-Limit":{"schema":{"type":"integer","example":600}},"X-RateLimit-Remaining":{"schema":{"type":"integer"}},"X-Mock":{"schema":{"type":"string","const":"true"}}}},"400":{"description":"Missing q, or input that cannot start a postcode","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`mock_invalid` → `invalid_api_key`; `mock_no_key` → `auth_required`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`mock_rate_limited` → `rate_limited`, with `Retry-After: 60`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/search/reverse":{"get":{"tags":["Search"],"summary":"Reverse geocode","description":"Returns a random well-formed postcode whose state is the nearest of the known states to the coordinate. Nothing is found outside Nigeria.","parameters":[{"name":"lng","in":"query","required":true,"schema":{"type":"number","minimum":-180,"maximum":180},"example":3.3792},{"name":"lat","in":"query","required":true,"schema":{"type":"number","minimum":-90,"maximum":90},"example":6.5244},{"name":"max_distance_m","in":"query","required":false,"schema":{"type":"number","minimum":0,"maximum":250,"default":25},"description":"Clamped to 250"},{"$ref":"#/components/parameters/ApiKey"}],"responses":{"200":{"description":"Resolved postcode, or `found: false`","content":{"application/json":{"schema":{"type":"object","required":["data","mock"],"properties":{"data":{"$ref":"#/components/schemas/ReverseResponse"},"mock":{"type":"boolean","const":true,"description":"Marks a mock response. The real API does not send it."}}}}},"headers":{"X-RateLimit-Limit":{"schema":{"type":"integer","example":600}},"X-RateLimit-Remaining":{"schema":{"type":"integer"}},"X-Mock":{"schema":{"type":"string","const":"true"}}}},"400":{"description":"Missing or out-of-range coordinates","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`mock_invalid` → `invalid_api_key`; `mock_no_key` → `auth_required`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`mock_rate_limited` → `rate_limited`, with `Retry-After: 60`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/search/nearby":{"get":{"tags":["Search"],"summary":"Location search","description":"NIPOST publishes no response shape for this endpoint; `results` follows the hint in its widget docs.","parameters":[{"name":"lng","in":"query","required":true,"schema":{"type":"number","minimum":-180,"maximum":180},"example":7.4951},{"name":"lat","in":"query","required":true,"schema":{"type":"number","minimum":-90,"maximum":90},"example":9.0579},{"name":"radius","in":"query","required":false,"schema":{"type":"number","exclusiveMinimum":0,"maximum":300,"default":300},"description":"Clamped to 300"},{"$ref":"#/components/parameters/ApiKey"}],"responses":{"200":{"description":"Units within the radius","content":{"application/json":{"schema":{"type":"object","required":["data","mock"],"properties":{"data":{"$ref":"#/components/schemas/NearbyResponse"},"mock":{"type":"boolean","const":true,"description":"Marks a mock response. The real API does not send it."}}}}},"headers":{"X-RateLimit-Limit":{"schema":{"type":"integer","example":600}},"X-RateLimit-Remaining":{"schema":{"type":"integer"}},"X-Mock":{"schema":{"type":"string","const":"true"}}}},"400":{"description":"Missing or out-of-range coordinates or radius","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`mock_invalid` → `invalid_api_key`; `mock_no_key` → `auth_required`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`mock_rate_limited` → `rate_limited`, with `Retry-After: 60`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/assembly/assemble":{"post":{"tags":["Assembly"],"summary":"Assemble segments into a canonical postcode","parameters":[{"$ref":"#/components/parameters/ApiKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Segments"},"example":{"state":"ek","lga":"1","district":"a03","area":"fk","unit":"1"}}}},"responses":{"200":{"description":"Zero-filled and formatted","content":{"application/json":{"schema":{"type":"object","required":["data","mock"],"properties":{"data":{"$ref":"#/components/schemas/Formatted"},"mock":{"type":"boolean","const":true,"description":"Marks a mock response. The real API does not send it."}}}}},"headers":{"X-RateLimit-Limit":{"schema":{"type":"integer","example":600}},"X-RateLimit-Remaining":{"schema":{"type":"integer"}},"X-Mock":{"schema":{"type":"string","const":"true"}}}},"400":{"description":"A segment is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`mock_invalid` → `invalid_api_key`; `mock_no_key` → `auth_required`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`mock_rate_limited` → `rate_limited`, with `Retry-After: 60`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/assembly/disassemble":{"get":{"tags":["Assembly"],"summary":"Disassemble a postcode into segments","parameters":[{"name":"code","in":"query","required":true,"schema":{"type":"string"},"example":"EK01A03FK01"},{"$ref":"#/components/parameters/ApiKey"}],"responses":{"200":{"description":"Segments","content":{"application/json":{"schema":{"type":"object","required":["data","mock"],"properties":{"data":{"$ref":"#/components/schemas/Segments"},"mock":{"type":"boolean","const":true,"description":"Marks a mock response. The real API does not send it."}}}}},"headers":{"X-RateLimit-Limit":{"schema":{"type":"integer","example":600}},"X-RateLimit-Remaining":{"schema":{"type":"integer"}},"X-Mock":{"schema":{"type":"string","const":"true"}}}},"400":{"description":"Not a postcode","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`mock_invalid` → `invalid_api_key`; `mock_no_key` → `auth_required`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`mock_rate_limited` → `rate_limited`, with `Retry-After: 60`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"parameters":{"ApiKey":{"name":"X-API-Key","in":"header","required":false,"description":"Optional. Empty means full access; a reserved key triggers its behaviour. The real API takes your key in this same header.","schema":{"type":"string","enum":["mock_level_1","mock_level_2","mock_level_3","mock_level_4","mock_level_5","mock_no_credits","mock_no_scope","mock_rate_limited","mock_invalid","mock_no_key"]}}},"schemas":{"Segments":{"type":"object","required":["state","lga","district","area","unit"],"properties":{"state":{"type":"string","example":"EK"},"lga":{"type":"string","example":"01"},"district":{"type":"string","example":"A03"},"area":{"type":"string","example":"FK"},"unit":{"type":"string","example":"01"}}},"Formatted":{"type":"object","required":["postcode","display","compact"],"properties":{"postcode":{"type":"string","example":"EK-01-A03-FK-01"},"display":{"type":"string","example":"EK 01 A03 FK 01"},"compact":{"type":"string","example":"EK01A03FK01"}}},"LookupResponse":{"type":"object","required":["postcode","valid"],"properties":{"postcode":{"type":"string"},"valid":{"type":"boolean"},"administrative_address":{"type":"object","description":"L2+","required":["state_name","lga_name","locality_name","zone"],"properties":{"state_name":{"type":"string"},"lga_name":{"type":"string"},"locality_name":{"type":"string"},"zone":{"type":"string"}}},"recent_house_address":{"type":"object","description":"L2+","required":["recent"],"properties":{"recent":{"type":"string"}}},"building_use_status":{"type":"string","description":"L3+. Random per postcode."},"other_building_info":{"type":"object","description":"L4+. Guessed shape, marked `mock: true`."},"point_geometry":{"type":"object","description":"L5. GeoJSON Point near the centre of the postcode's state, marked `mock: true`. Guessed shape.","required":["type","coordinates"],"properties":{"type":{"type":"string","const":"Point"},"coordinates":{"type":"array","items":{"type":"number"},"minItems":2,"maxItems":2},"mock":{"type":"boolean"}}}}},"AutocompleteResponse":{"type":"object","required":["segment","suggestions"],"properties":{"segment":{"type":"string","enum":["state","lga","district","area","unit"]},"suggestions":{"type":"array","items":{"type":"object","required":["code","label"],"properties":{"code":{"type":"string"},"label":{"type":"string"}}}}}},"ReverseResponse":{"type":"object","required":["found","coordinate","radius_m"],"properties":{"found":{"type":"boolean"},"coordinate":{"type":"array","items":{"type":"number"},"minItems":2,"maxItems":2,"description":"[lng, lat] echoed back"},"unit":{"type":"object","required":["postcode","display","distance_m","confidence"],"properties":{"postcode":{"type":"string"},"display":{"type":"string"},"distance_m":{"type":"number"},"confidence":{"type":"string","enum":["high","medium","low"]},"state_name":{"type":"string","description":"L2+"},"lga_name":{"type":"string","description":"L2+"},"locality_name":{"type":"string","description":"L2+"},"address":{"type":"string","description":"L2+"}}},"area":{"type":"string"},"district":{"type":"string"},"state":{"type":"string"},"message":{"type":"string","description":"Set when found is false"},"radius_m":{"type":"number"}}},"NearbyResponse":{"type":"object","required":["results","radius_m"],"properties":{"results":{"type":"array","items":{"type":"object","required":["postcode","display","distance_m"],"properties":{"postcode":{"type":"string"},"display":{"type":"string"},"distance_m":{"type":"number"}}}},"radius_m":{"type":"number"}}},"Error":{"type":"object","required":["error","mock"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}},"mock":{"type":"boolean","const":true}}}}}}