# Postal Code & Geocoding API — reverse-geocode coordinates or postal codes to administrative regions worldwide: country, state/county, ZIP/PIN, FIPS, NUTS/LAU codes and census GEOIDs

> Coordinates OR postal code → EU/EFTA/candidate statistical regions: country, NUTS 0-3 hierarchy, LAU commune (GISCO NUTS-2024 + LAU-2024 + TERCET pc2025).
> ReefAPI engine `geo-postal` · 11 endpoints · clean JSON, no scraping or browsers to manage.

## How to call
- **Endpoint:** `POST https://api.reefapi.com/geo-postal/v1/<action>` with a JSON body.
- **Auth:** header `x-api-key: <YOUR_REEFAPI_KEY>` — create one free (1,000 credits, no card): https://reefapi.com/signup
- **Response (every call):** `{ ok: boolean, data: ..., meta: { record_count, credits, ... }, error: { code, message } }` — branch on `ok`. Failed or blocked calls are free.
- **One key + one shared credit pool** across every ReefAPI API. Per-call credits are listed on each endpoint below.
- **Use it from an AI agent (MCP):** connect `https://api.reefapi.com/mcp` (remote streamable-http, `Authorization: Bearer <key>`) and your assistant can call these actions directly.

## Endpoints

### POST /geo-postal/v1/eu_regions — 2 credits
Coordinates OR postal code → EU/EFTA/candidate statistical regions: country, NUTS 0-3 hierarchy, LAU commune (GISCO NUTS-2024 + LAU-2024 + TERCET pc2025).

**Parameters:**
- `lat` (number, optional) — WGS84 latitude of the point to resolve (decimal degrees).
- `lon` (number, optional) — WGS84 longitude of the point to resolve (decimal degrees).
- `postal_code` (string, optional) — Postal code to resolve. Spaces/dashes are ignored, case-insensitive (e.g. '10115', '75001', 'SW1A1AA').
- `country` (string, optional) — ISO-3166 alpha-2 country code (e.g. DE, FR, TR, IN, US). Greece accepts GR or EL.
- `include_geometry` (boolean, optional, default false) — If true, include the (simplified) boundary polygon as GeoJSON. Default false — geometries are large; lookups never depend on this.

**Returns:** found, country{code,name}, nuts0..nuts3{code,name}, lau{gisco_id,lau_id,name}, postal_places[] (postal input), normalized_input, match_type, confidence, low_confidence, source, dataset_version (+geometry if requested)

**Example request body:**
```json
{
  "lat": 52.52,
  "lon": 13.405
}
```

### POST /geo-postal/v1/tr_regions — 2 credits
Türkiye: koordinat, posta kodu VEYA il/ilçe/mahalle adı → resmî idari hiyerarşi (il+plaka, ilçe, mahalle/köy, posta kodları) + NUTS-TR istatistik kodları.

**Parameters:**
- `lat` (number, optional) — WGS84 latitude of the point to resolve (decimal degrees).
- `lon` (number, optional) — WGS84 longitude of the point to resolve (decimal degrees).
- `postal_code` (string, optional) — Postal code to resolve. Spaces/dashes are ignored, case-insensitive (e.g. '10115', '75001', 'SW1A1AA').
- `province` (string, optional) — Province (il) name, e.g. İstanbul, Ankara, İzmir. Turkish characters optional (istanbul works).
- `district` (string, optional) — District (ilçe) name within the province.
- `neighborhood` (string, optional) — Neighborhood (mahalle) or village (köy) name.
- `include_geometry` (boolean, optional, default false) — If true, include the (simplified) boundary polygon as GeoJSON. Default false — geometries are large; lookups never depend on this.

**Returns:** found, province{id,plate,name,nuts1..3,region,population,coordinates,phone_area_codes,is_metropolitan}, district{id,name,population}, matches[] (mahalle/köy w/ postal_code,population,kind), postal_codes[], normalized_input, match_type, confidence, source, dataset_version

**Example request body:**
```json
{
  "lat": 41.0082,
  "lon": 28.9784
}
```

### POST /geo-postal/v1/in_postal — 2 credits
India: PIN code OR post-office name → post offices, district, state, lat/lon centroid (official All-India Pincode Directory).

**Parameters:**
- `pincode` (string, optional) — 6-digit Indian PIN code, e.g. 400001 (Mumbai GPO).
- `office` (string, optional) — Post-office name to search (exact or prefix), when the PIN is unknown.
- `state` (string, optional) — Optional state filter for office search.
- `district` (string, optional) — Optional district filter for office search.

**Returns:** found, pincode, offices[]{name,type,delivery,district,state,lat,lon}, district, state, centroid{lat,lon}, office_count, normalized_input, match_type, confidence, source, dataset_version

**Example request body:**
```json
{
  "pincode": "400001"
}
```

### POST /geo-postal/v1/us_regions — 2 credits
United States: ZIP code OR coordinates → state, county + 5-digit county FIPS, place, ZIP centroid, and the CGAZ state/county boundary. Built WITHOUT census.gov (GeoNames ZIP directory carries county FIPS; ANSI state-FIPS is a public-domain constant). For census-grade ZCTA + tract/block GEOIDs use the `us_census` action (TIGER/ZCTA polygons + Census Geocoder, gov-pending/91).

**Parameters:**
- `postal_code` (string, optional) — Postal code to resolve. Spaces/dashes are ignored, case-insensitive (e.g. '10115', '75001', 'SW1A1AA').
- `lat` (number, optional) — WGS84 latitude of the point to resolve (decimal degrees).
- `lon` (number, optional) — WGS84 longitude of the point to resolve (decimal degrees).
- `include_geometry` (boolean, optional, default false) — If true, include the (simplified) boundary polygon as GeoJSON. Default false — geometries are large; lookups never depend on this.

**Returns:** found, zip, place, state{code,name,fips}, county{name,fips}, centroid{lat,lon}, boundary{adm1,adm2} (coord mode), census{zcta,tract,...} when the TIGER extension is loaded else census_pending (see us_census), normalized_input, match_type, confidence, source, dataset_version

**Example request body:**
```json
{
  "postal_code": "10001"
}
```

### POST /geo-postal/v1/us_census — 3 credits
United States census-grade geocode: street ADDRESS, ZIP, or coordinates → state, county (+5-digit FIPS), census TRACT GEOID, census BLOCK GEOID, ZCTA, and tract/ZCTA land-area — with match_type + confidence. Coordinates/ZIP resolve the ZCTA offline from pinned TIGER/ZCTA-2024 polygons (zero egress); the authoritative tract/block GEOID + address geocoding come from the US Census Geocoder, cached. US-Gov public domain (Title 17 §105).

**Parameters:**
- `address` (string, optional) — One-line US street address to geocode to its census tract/block (uses the Census Geocoder; rooftop interpolation, not a deliverability/CASS claim).
- `postal_code` (string, optional) — Postal code to resolve. Spaces/dashes are ignored, case-insensitive (e.g. '10115', '75001', 'SW1A1AA').
- `lat` (number, optional) — WGS84 latitude of the point to resolve (decimal degrees).
- `lon` (number, optional) — WGS84 longitude of the point to resolve (decimal degrees).
- `include_geometry` (boolean, optional, default false) — If true, include the (simplified) boundary polygon as GeoJSON. Default false — geometries are large; lookups never depend on this.
- `live` (boolean, optional, default true) — If true (default) and an exact tract/block GEOID is needed, query the Census Geocoder (cached). Set false for offline-only (ZCTA polygon + nearest-tract centroid) with no .gov call.

**Returns:** found, input_kind, zcta{geoid,land_sqmi,water_sqmi}, state{code,name,fips}, county{name,fips}, tract{geoid,number,land_sqmi,match}, block{geoid} (live), matched_address (address mode), coordinates{lat,lon}, source, match_type, confidence, low_confidence, normalized_input, dataset_version (+geometry if requested)

**Example request body:**
```json
{
  "lat": 40.7505,
  "lon": -73.9967,
  "live": true
}
```

### POST /geo-postal/v1/admin_boundary — 2 credits
GLOBAL coordinates → administrative boundary hierarchy ADM0/ADM1/ADM2 (geoBoundaries CGAZ composite, CC-BY 4.0).

**Parameters:**
- `lat` (number, required) — WGS84 latitude of the point to resolve (decimal degrees).
- `lon` (number, required) — WGS84 longitude of the point to resolve (decimal degrees).
- `levels` (string, optional, default "0,1,2") — Comma-separated geoBoundaries levels to resolve: 0 (country), 1 (state/region), 2 (county/municipality). Default all three.
- `include_geometry` (boolean, optional, default false) — If true, include the (simplified) boundary polygon as GeoJSON. Default false — geometries are large; lookups never depend on this.

**Returns:** found, adm0{id,name,iso3}, adm1{id,name,iso3}, adm2{id,name,iso3} (each w/ own match_type+confidence), normalized_input, match_type, confidence, source, dataset_version (+geometry if requested)

**Example request body:**
```json
{
  "lat": 35.6762,
  "lon": 139.6503
}
```

### POST /geo-postal/v1/postal_lookup — 1 credit
country + postal code → region metadata for ~100 countries (GeoNames postal directory) with country-specific enrichment (EU→NUTS3, TR→mahalle, IN→offices).

**Parameters:**
- `country` (string, required) — ISO-3166 alpha-2 country code (e.g. DE, FR, TR, IN, US). Greece accepts GR or EL.
- `postal_code` (string, required) — Postal code to resolve. Spaces/dashes are ignored, case-insensitive (e.g. '10115', '75001', 'SW1A1AA').

**Returns:** found, country, postal_code, places[]{name,admin1(+code),admin2(+code),admin3(+code),lat,lon,accuracy}, nuts3 (EU), tr_matches[] (TR), in_offices[] (IN), coverage_note, normalized_input, match_type, confidence, source, dataset_version

**Example request body:**
```json
{
  "country": "FR",
  "postal_code": "75001"
}
```

### POST /geo-postal/v1/postal_search — 1 credit
country + city/place → its postal codes (zip-by-city); country + admin1 only → that state's codes (zip-by-state); country only → the country's states/regions list (GeoNames directory).

**Parameters:**
- `country` (string, required) — ISO-3166 alpha-2 country code (e.g. DE, FR, TR, IN, US). Greece accepts GR or EL.
- `place` (string, optional) — Place/city name to list postal codes for. Accent and case-insensitive (munchen finds München). Omit it and pass only admin1 for a state-wide listing; omit both for the country's admin-1 (state/region) list.
- `admin1` (string, optional) — Optional admin-1 (state/region) filter, name or code.
- `limit` (integer, optional, default 50) — Max codes to return (1-100, default 50).

**Returns:** found, country, place, postal_codes[]{postal_code,name,admin1,admin2,lat,lon}, code_count, truncated — OR (country-only mode) regions[]{name,code,postal_code_count}; normalized_input, match_type, confidence, source, dataset_version

**Example request body:**
```json
{
  "country": "DE",
  "place": "München",
  "limit": 10
}
```

### POST /geo-postal/v1/postal_distance — 1 credit
Great-circle distance between two postal codes (same or different countries).

**Parameters:**
- `country` (string, required) — ISO-3166 alpha-2 country code (e.g. DE, FR, TR, IN, US). Greece accepts GR or EL.
- `from` (string, required) — Origin postal code (in `country`).
- `to` (string, required) — Destination postal code (in `to_country`, default same country).
- `to_country` (string, optional) — Destination ISO-3166 alpha-2 country if different.
- `unit` (enum, optional, default "km") — Distance unit (km default, mi). [one of: km, mi]

**Returns:** found, distance, unit, from{postal_code,name,lat,lon}, to{postal_code,name,lat,lon}, method ('haversine_centroid' — postal CENTROIDS, not rooftop), normalized_input, match_type, confidence, source, dataset_version

**Example request body:**
```json
{
  "country": "US",
  "from": "10001",
  "to": "90210"
}
```

### POST /geo-postal/v1/postal_radius — 1 credit
All postal codes within a radius of a postal code (centroid-based).

**Parameters:**
- `country` (string, required) — ISO-3166 alpha-2 country code (e.g. DE, FR, TR, IN, US). Greece accepts GR or EL.
- `postal_code` (string, required) — Postal code to resolve. Spaces/dashes are ignored, case-insensitive (e.g. '10115', '75001', 'SW1A1AA').
- `radius_km` (number, required) — Search radius in kilometres (0.1-100).
- `limit` (integer, optional, default 50) — Max codes to return, nearest first (1-100).

**Returns:** found, center{postal_code,name,lat,lon}, results[]{postal_code,name,admin1,distance_km}, result_count, truncated, normalized_input, match_type, confidence, source, dataset_version

**Example request body:**
```json
{
  "country": "DE",
  "postal_code": "10115",
  "radius_km": 3
}
```

### POST /geo-postal/v1/batch — 2 credits
Resolve up to 50 mixed lookups in one call (cheap local queries).

**Parameters:**
- `items` (array, required) — Up to 50 lookup items. Each is an object with an `action` (eu_regions|tr_regions|in_postal|admin_boundary|postal_lookup) plus that action's params. Items resolve independently — one bad item never fails the batch.

**Returns:** count, found_count, results[] (each = the single-action record, tagged with its action)

**Example request body:**
```json
{
  "items": [
    {
      "action": "eu_regions",
      "lat": 48.8566,
      "lon": 2.3522
    },
    {
      "action": "postal_lookup",
      "country": "TR",
      "postal_code": "34710"
    }
  ]
}
```

## More
- Try it live, no code: https://reefapi.com/playground?engine=geo-postal
- Human docs page: https://reefapi.com/docs/geo-postal
- Every ReefAPI API in one file (for your AI): https://reefapi.com/llms-full.txt
