SHIPPED 2026-05-06 · SPRINT 1.8

Postcode polygon.
In one API call.

4.6M+ administrative boundaries across 63 countries. Walk-up hierarchy, walk-down sub-divisions, consolidated cities, three polygon precision tiers.

One call, not two. Most boundary APIs make you geocode the postcode first to get an opaque place_id, then call again to fetch the polygon. Ours bundles polygon + bbox + population + Wikidata ID + parent admin chain into a single nested response. Drop-in compatible with the standard GeoJSON-shaped geocoder format — both lat (flat) and location.lat (nested) on every result.

Free tier: 3,000 calls/day. No credit card.
6.7× faster at p50 One call instead of two Population & Wikidata inline

Four ways to ask the boundary question

All powered by the same Overture-backed boundary collection. All return rich nested metadata in a single response.

Postcode → boundary, in one call

Other APIs make you geocode the postcode first to get an opaque place ID, then make a second call to fetch the polygon. Ours: one GET, postcode in, polygon out, with population and Wikidata baked in.

GET /v1/divisions/by-postcode?code=90210&country=US&include=geometry

Walk up the admin chain

Given any division, return the full ancestor chain — neighborhood → city → county → state → country — with each level's name, population, parent ID, and (optionally) polygon. Cycle-safe, depth-capped.

GET /v1/divisions/ancestors/{id}

Walk down to sub-divisions

Given a parent, return its immediate children. Filter by subtype (county, locality, neighborhood). Polygons inline when you ask, simplified to the precision tier you choose.

GET /v1/divisions/children/{id}?subtype=county

Consolidated cities

NYC is one city to humans but five boroughs in the data. Pass any borough ID and get the canonical NYC record plus all members. Same pattern for Greater London, Greater Tokyo, Paris arrondissements.

GET /v1/divisions/consolidated/{id}

Disambiguate with rank.importance

Every division response carries a rank.importance score (0–1) sourced from Wikidata sitelink count. NYC, Tokyo and Paris ≈ 1.0; small rural divisions ~0.2. Use it to pick the right "Springfield" out of 30, or sort search results by global notability.

Backfilled across 1.02M of 4.6M divisions — every division with a Wikidata Q-ID has a value. Drop-in compatible with the Geoapify rank response shape.

// /v1/divisions/by-postcode?code=10001&country=US
{
  "name": "New York",
  "subtype": "locality",
  "rank": {
    "importance": 1.0
  },
  "wikidata": "Q60",
  ...
}

Try it on a live map

Type a postcode, watch the polygon render. No signup, no key — for the first 30 calls a minute. Same response shape your code would get.

Open the live demo →

Three lines of code

Same call, three languages. SDKs at version 1.4.0 on PyPI and npm.

curl
# postcode → polygon, one call
curl "https://csv2geo.com/api/v1/divisions/by-postcode?code=90210&country=US&include=geometry&precision=med" \
  -H "Authorization: Bearer geo_live_..."
Python
from csv2geo import Client
c = Client("geo_live_...")
r = c.divisions_by_postcode(
  "90210", "US",
  include="geometry",
  precision="med")
print(r["result"]["bbox"])
Node.js
import { Client } from "csv2geo-sdk";
const c = new Client("geo_live_...");
const r = await c.divisionsByPostcode(
  "90210", "US",
  { include: "geometry",
    precision: "med" });
console.log(r.result.bbox);

What people build with it

From customer interviews — the four patterns that show up over and over.

Catchment-area maps

Render delivery zones, sales territories, school catchments — fed straight from postcodes. One call per zone, polygon arrives ready to draw.

Postal-zone analytics

Group customer addresses by postal area, compute spatial stats per zone, render heatmaps of conversion or revenue density.

Jurisdictional checks

"Which county is this address in?" Point-in-polygon against admin boundaries — sales tax, congressional district, school district lookups.

Address enrichment

Walk the admin chain on every CRM record. Population, region code, ISO-3166-2, Wikidata, parent IDs — drop straight into a database row.

Pricing

Boundary endpoints are bundled into the same price as a regular geocode call. No separate SKU. If you have an API key with the boundaries permission (on by default for every plan, including Free), all six boundary endpoints work.

See full API pricing
block is still compiled — and in THIS file that makes the partial include ITSELF. It recurses until PHP's memory limit dies and every page that renders it returns 500. That is not hypothetical: this header carried exactly such an example on 2026-08-15 and it was caught only by tests/Unit/BladeRendersWithoutRecursionTest.php on a full-suite run. It was invisible while the partial was wired into nothing, and went live the moment it was included. Describe the call in words; never spell a directive in a comment. Pass the URL in the SAME form the page's own tag uses. A layout that emits a bare /js/min/common.min.js should pass the bare path, so the retry hits the same cache entry the browser already tried rather than downloading a second copy. A layout whose tag is cache-busted should pass AssetVersion::url() for the same reason — note the asset-version Blade directive ECHOES, so it cannot go inside a PHP array; call \App\Support\AssetVersion::url() directly. --}}