Docs

API reference

A point-in-polygon boundary API. Send a coordinate; get back the country, region and municipality that contain it, plus matches from your own uploaded boundary sets — in one call. Optionally, the nearest street and house number. It also routes between coordinates, and can route around the zones you upload; and it geocodes addresses to coordinates, in South Africa today.

Reading this as an AI agent?

Fetch the machine-readable reference instead. It is plain Markdown, complete, and covers what an automated client cannot do as well as what it can.

https://atlasfetch.xyz/llms.txt

Base URL

https://api.atlasfetch.xyz

Every path on this page is relative to it. Lookups go to https://api.atlasfetch.xyz/location/lookup — not to atlasfetch.xyz, which is the website.

When to use AtlasFetch

Reach for it when the question is “which place is this point in?”

  • Reverse geocoding to administrative areas — a coordinate becomes the country, region (state, province) and municipality (city, district or county) that contain it, as names plus ISO 3166-1 / ISO 3166-2 codes. Worldwide, from OpenStreetMap boundaries.
  • The nearest street — the street a point is on, and the house number where one is mapped, with the distance to each. Worldwide, in 216 countries.
  • Directions — a car, bike, foot or truck route between 2 and 25 coordinates, with turn-by-turn steps, routed around the zones you name and measured against the ones you watch. A route costs 10 lookups on the standard plans.
  • Geocoding addresses for delivery — an address in, a point out, with a precision that says whether it found the house, the street or only the suburb, so you know when to ask for a pin. South Africa today; one lookup per address, singly or in batches.
  • Geofencing against your own polygons — upload GeoJSON delivery zones, service areas or sales territories and test a point against them in the same call as the administrative layers.
  • Jurisdiction and region checks — which country or region a user, order or device is in, for pricing, availability, tax or licensing rules.
  • Enriching location data — tag GPS pings, orders or sign-ups with region codes, and optionally an H3 cell index or Google Plus Code.
  • Privacy-sensitive workloads — looked-up coordinates are never stored.

Not a fit

  • Geocoding outside South Africa, or place search. Addresses geocode in ZA only for now, and there is no search for businesses or landmarks by name.
  • A guaranteed house number. OpenStreetMap maps them well in some countries and barely in others — in ZA, on about 13% of street names.
  • Traffic-aware ETAs. Routes exist, but their durations are free-flow — speed limits and road classes, with no live or historical traffic. For an arrival time in traffic, use Google or Mapbox.
  • Distance matrices, isochrones or map tiles. Many-to-many distances and reachability areas are planned, not built.
  • Boundary geometry itself. It answers containment; it does not return the reference polygons.
  • Many coordinates in one request. One request is one coordinate.
  • Enter/exit events. It answers which zones contain a point now; tracking transitions between calls is up to your code.

Boundaries follow OpenStreetMap. For a legally binding jurisdiction decision, confirm against the authoritative source.

Authentication

Two mechanisms, and the difference decides what a program can do.

  • API key — Authorization: Bearer <key>. A 64-character hex string, no prefix. This is what a program uses.
  • Session cookie — issued by an interactive Google sign-in in a browser. An automated client cannot obtain one.

A key is enough for lookups, routes, geocoding and managing boundary sets. It is not enough to mint keys, read usage, or manage billing.

Create a key on the dashboard, or call GET https://api.atlasfetch.xyz/demo/key for a shared public one — no signup, but its 1,000 lookups a month and its rate limit are pooled across every anonymous user. Use it to try things, not to ship.

You can show and copy a key from the dashboard whenever you need it. That also means anyone who reaches the dashboard can read it, so restrict each key to your own domains there — treat that as the control, not an option. Keys made before 12 October 2026 were revoked when keys became viewable; a revoked key’s 401 says so.

The lookup

GET https://api.atlasfetch.xyz/location/lookup?lat=51.5072&lng=-0.1276
Authorization: Bearer <key>
curl -H "Authorization: Bearer $ATLASFETCH_KEY" \
  "https://api.atlasfetch.xyz/location/lookup?lat=51.5072&lng=-0.1276"
ParameterDefaultMeaning
latrequiredLatitude, −90 to 90
lngrequiredLongitude, −180 to 180
basecountry, region, municipalComma list: country, region, municipal, street (opt-in)
setnoneComma list of your own boundary sets
encodenoneComma list: h3, pluscode
h3res9H3 resolution 0–15. r9 is ~400 m across
pluslen10Plus Code length: 2, 4, 6, 8, 10–15
streetgeomnonepolyline6 or geojson: each street answer's line. Needs street in base
{
  "base": {
    "country":   { "code": "GB",     "name": "United Kingdom" },
    "region":    { "code": "GB-ENG", "name": "England" },
    "municipal": { "code": "GB-WSM", "name": "City of Westminster" }
  },
  "sets": {
    "delivery_zones": [
      { "name": "Zone A", "properties": { "category": "express" } }
    ]
  },
  "errors": []
}

Guarantees you can rely on

  • errors[] is always present, empty or not. Read it unconditionally.
  • A layer that matched nothing is null, not absent.
  • encoded is absent entirely unless encode was asked for.
  • Every response carries X-Lookups-Remaining.
  • Boundaries are identified by codes, never OSM ids — ISO 3166-1 for country, ISO 3166-2 for region, and for municipal the official ISO code where OSM carries one, otherwise a generated B-<country>-<n>.
  • municipal is the finest unit available, not a consistent kind of thing. Los Angeles returns a city; rural Kansas returns Barton County. Where a country’s fine tier is patchy, a coarser complete tier answers instead — a county beats null for most uses. Do not assume the value names a city.
Generated B- codes are not yet stable across a full reseed. Treat them as display labels rather than durable keys. ISO codes are stable.

Streets

Add street to base and the response gains a fourth layer: the nearest street, answered three times — within 5 m, within 20 m, and however far. Always three entries, in that order. It is not in the default base, so asking for it changes nothing for existing callers, and it costs no extra lookup.

GET https://api.atlasfetch.xyz/location/lookup?lat=-33.9242&lng=18.421&base=country,region,municipal,street

"street": [
  { "radiusMeters": 5,    "streetNumber": null, "streetName": "Adderley Street", "postcode": null,   "distanceMeters": 2.7 },
  { "radiusMeters": 20,   "streetNumber": "25", "streetName": "Adderley Street", "postcode": "8001", "distanceMeters": 9.6 },
  { "radiusMeters": null, "streetNumber": "25", "streetName": "Adderley Street", "postcode": "8001", "distanceMeters": 9.6 }
]

How each answer is picked

  • A house number within the radius wins, even over a nearer street — it is the more precise answer, and it carries the address’s own street. For the unlimited answer the house must still be within 20 m; further than that it is rarely the property the point is on.
  • Otherwise the nearest named street. A road known only by its route number answers with that: N1.
  • Otherwise every field except radiusMeters is null. Unnamed roads are never an answer.
  • distanceMeters is to the building (0 when the point is inside it) or to the nearest point of the street, to 0.1 m.

What to expect

  • Streets cover 216 countries. street: null means the point is outside all of them — at sea, in Antarctica, or in one of the two areas without an ISO country code (Western Sahara; Akrotiri and Dhekelia). An entry of nulls means covered, but nothing within that radius.GET https://api.atlasfetch.xyz/streets/coverage lists every country with its road and address counts.
  • House numbers depend on the country. Where OpenStreetMap has few addresses, most answers are a street name and a distance. postcode only comes with a house number.
  • streetNumber and postcode are strings: numbers look like 44A or 12-14, and some postcodes start with 0.
  • No street geometry unless you ask. With streetgeom=polyline6 (or geojson), each answer gains geometry: the named street’s line, trimmed to about 250 m either side of the point. A house-number answer gets its own street’s line; an answer naming nothing gets null. Without it there is no geometry key at all. Still one lookup; streetgeom without street is a 400.
  • Streets come from OpenStreetMap, like the boundaries. OpenStreetMap ids and road attributes never leave the API.

One call, one lookup

A request is exactly one lookup regardless of how many layers, sets or grid codes it touches. Metering happens before matching, so a call that matches nothing still costs a lookup — the work was done either way. A request rejected with 400 or 429 is not billed.

A route is the exception: POST /route costs 10 lookups on the standard plans, whatever it asks for — steps, alternatives, avoid zones and exposure are included in that one charge. A geocoded address costs 1, found or not. Everything draws on the same monthly allowance.

X-Lookups-Remaining (and a geocoding batch’s remaining) is the truth for the caller: it includes any extra allowance the account has been given, and reflects a custom plan’s prices. Read it rather than working it out.

Partial failure is still a 200

A set you cannot query does not fail the request. It is skipped, an entry is appended to errors[], and the status stays 200. A set is only queryable when it is marked available and granted to the key making the call.

"errors": [
  { "type": "access", "message": "Set 'zones' is not granted to key 'prod'" }
]

type is access (a set was skipped) or usage (quota exhausted, mirroring a 402).

Grid codes

The looked-up point can be returned as a Google Plus Code and/or an H3 cell index. Not metered, not plan-gated.

GET https://api.atlasfetch.xyz/location/lookup?lat=51.5072&lng=-0.1276&encode=h3,pluscode

"encoded": { "h3": "89195da49b7ffff", "pluscode": "9C3XGV4C+VX" }

They are output only

pluscode= and h3= are not accepted as a location; sending one returns 400. That is deliberate, not a missing feature.

A Plus Code and an H3 index name areas, not points. A length-4 Plus Code is a 111 km square; an H3 cell at resolution 4 is 52 km across. Accepting one as the location would mean picking a representative point inside it and returning whichever boundary contains that point as though it were the answer — one of several equally defensible answers, with no way for you to tell which situation you were in.

If you have a cell and want a lookup, decode it yourself and decide what its centre means for your use case. That keeps the judgement where the context is.

Picking a precision

H3WidthPlus CodeWidth
r4~52 km4~111 km
r6~7.4 km6~5.6 km
r8~1.1 km8~278 m
r9 (default)~402 m10 (default)~14 m
r11~57 m11~3 m
r15~1 m12~56 cm

Plus Code widths are at the equator. pluslen=9 is invalid — 9 is not a Plus Code length.

Custom boundary sets

Your own polygons, grouped into named sets, matched in the same call as the reference layers. These endpoints accept an API key or a session.

MethodPathPurpose
GET/boundaries/setsList sets with boundary counts and granted keys
POST/boundaries/setsCreate a set
PATCH/boundaries/sets/:nameToggle available, grant/revoke keys
DELETE/boundaries/sets/:nameDelete the set and its contents
POST/boundariesAdd one GeoJSON Polygon
GET/boundaries/view?set=The set and its boundaries, with geometry
PATCH/boundaries/:idReplace one boundary's shape; body {"geometry": <Polygon>} only
DELETE/boundaries/:idRemove one boundary
A new set is created available: false with no grants, so it matches nothing until you switch it on and grant it to a key. Any key but the shared demo key can switch a set on and grant it, but grants are addressed by API key id, and nothing a key can call tells it its own id — so the first grant happens in the dashboard. After that the key’s id appears in GET /boundaries/sets and later grants can be scripted. A polygon may have at most 50 points on every plan, the closing one included. The shared demo key cannot change or delete a set.

PATCH /boundaries/:id moves or reshapes a zone without losing it: the id, name, properties and set stay as they are. The new shape gets every check POST /boundaries applies, including the plan’s points-per-boundary cap (403). Any other field is a 400; a boundary on another account is a 404. Deleting and re-adding does the same job worse — a new id, and the properties sent again.

Directions

A route between coordinates, worldwide, over the same OpenStreetMap network the street layer uses. Coordinates travel in the body, never a URL, and are not stored.

POST https://api.atlasfetch.xyz/route
Authorization: Bearer <key>
Content-Type: application/json

{
  "locations": [ { "lat": -33.9242, "lng": 18.4210 }, { "lat": -33.9068, "lng": 18.4201 } ],
  "mode": "car",
  "steps": true
}
FieldDefaultMeaning
locationsrequired2 to 25 {lat,lng} in order: origin, stops, destination. The plan caps how many
modecarcar, bike, foot, truck. truck is Pro only
stepsfalseTurn-by-turn steps per leg
geometrynonenone, polyline6, geojson — the route line, off by default because it is long
languageen-USLocale for the step instructions, e.g. en-GB, de-DE
alternatives0Up to the plan's cap. Exactly 2 locations only
avoid.setsnoneNames of your boundary sets to route around
exposure.setsnoneNames of your boundary sets to measure against
vehicletruck defaultsHeight, width, length, weight, axle load, hazmat. Only with truck

Real output, Cape Town CBD to the V&A Waterfront, trimmed to two steps:

{
  "route": {
    "mode": "car",
    "distanceMeters": 3513,
    "durationSeconds": 306,
    "legs": [
      {
        "distanceMeters": 3513,
        "durationSeconds": 306,
        "steps": [
          { "maneuver": "depart", "instruction": "Drive southwest on Adderley Street.", "streetNames": ["Adderley Street"], "distanceMeters": 118, "durationSeconds": 11 },
          { "maneuver": "turn-right", "instruction": "Turn right onto Buitengracht Street/M62.", "streetNames": ["Buitengracht Street", "M62"], "distanceMeters": 1116, "durationSeconds": 82 }
        ]
      }
    ]
  },
  "locations": [
    { "lat": -33.924185, "lng": 18.420978, "snapDistanceMeters": 2.6 },
    { "lat": -33.907242, "lng": 18.419687, "snapDistanceMeters": 62.2 }
  ],
  "warnings": [],
  "errors": []
}
  • Durations are free-flow. Speed limits and road classes, with no live or historical traffic and no departure time. Do not show one as an ETA.
  • Metres and seconds, everywhere.
  • steps, geometry, exposure, alternatives and avoid are absent unless asked for, the same rule as encoded on a lookup.
  • locations[] holds the snapped points, and snapDistanceMeters says how far each moved to reach a road. 62 m above means the route ends on the nearest road, not at the pin.
  • maneuver is a fixed vocabulary: depart, arrive, continue, the four turns with their slight and sharp variants, u-turn, ramp, exit, merge, fork, roundabout, ferry, other.

Trucks

truck is Pro only. A truck over 9 t, or with no weightTonnes, is capped at 80 km/h, as South African law caps goods vehicles over 9 t; a lighter truck keeps car speeds. Cape Town to Stellenbosch: a 30 t truck takes a shorter, slower road (44.6 km, 38 min), a 3.5 t truck matches the car (50.6 km, 35 min). The vehicle’s height, width, length, weight, axle load and hazmat are checked against OpenStreetMap’s limits on each road: Hout Bay to Noordhoek, a car goes over Chapman’s Peak (14.0 km, 2.7 m height limit) and a 3.5 m truck goes round by Constantia (32.4 km). Restrictions are only as complete as OpenStreetMap’s tags, so treat a truck route as a good start, not a legal clearance.

Your own zones, in a route

avoid.sets and exposure.sets take the names of boundary sets on your account — the same sets a lookup matches against — not shapes in the request. Avoid excludes every zone in those sets; exposure changes nothing and reports how many metres of the route ran inside each zone.

"route": {
  "exposure": [
    { "set": "risk_watch", "zone": "Station hotspot", "properties": { "band": "high" }, "metersInside": 426.6 }
  ]
},
"avoid": {
  "applied": [ { "set": "no_go", "zone": "Roadworks" } ],
  "skipped": [ { "set": "no_go", "zone": "Customer estate", "reason": "contains_location", "locationIndex": 1 } ]
}
A zone containing one of your locations cannot be avoided on that route — you would be asking to start or finish somewhere unreachable. It is skipped, listed in avoid.skipped, and a warnings[] entry names it. An avoid set that is missing, switched off or not granted to the key is a hard 403, unlike a lookup, where an unusable set is skipped: routing around a zone you cannot read is not something to guess at. A bad exposure set is soft — it lands in errors[] and the route still answers. So does a route along more than 1,000 exposure zones, or one whose zones take over 5 s to measure: route.exposure is [] and an errors[] entry says exposure was not measured; the route is still billed.

What refuses a route

StatuscodeWhenBilled
400invalid_requestSchema, ranges, an unknown field, more than 25 locations, alternatives with stops, vehicle without truckNo
400too_farThe straight-line legs exceed the plan's limit for that modeNo
400avoid_too_complexAvoid zones near this route exceed the plan's zone or vertex capNo
403plan_featureTruck below Pro, or more locations, alternatives or exposure sets than the plan allowsNo
403avoid_set_unavailableAn avoid set is missing, off, or not granted to this keyNo
422no_road_nearbyA location has no routable road within reachYes
422no_routeNo path exists, for example an island with no ferryYes
503routing_unavailableThe routing engine is not answeringNo, or refunded

GET /route/coverage is public and unmetered: whether routing is answering, which modes exist, what each call costs in lookups, and every plan’s routing caps. Read it rather than hard-coding the numbers. It also prices matrixCell and isochrone, though neither endpoint exists yet — distance matrices and isochrones are planned, and published so they can be budgeted for.

Geocoding

An address in, a point out — built for deliveries, where the question is not only where but how sure. South Africa today; more countries as their data loads. Addresses go in the body, never a URL, and are never logged or stored.

POST https://api.atlasfetch.xyz/geocode
Authorization: Bearer <key>
Content-Type: application/json

{ "q": "25 Adderley Street, Cape Town" }

Send either q (one line, up to 300 characters) or address (housenumber, street, suburb, city, postcode, unit). A form that already splits the address should send address: it matches better.

FieldMeaning
countryISO 3166-1 alpha-2. A hard filter: only that country is searched
regionISO 3166-2 (ZA-WC) or a name. A preference: streets inside it rank first, but a wrong province does not lose the address
setYour boundary sets, matched at the result's point, with each zone's properties
baseThe lookup's layers at the result's point: country, region, municipal
streetgeompolyline6 or geojson: the matched street's line, trimmed to about 250 m

Real answer:

{
  "result": {
    "lat": -33.924455,
    "lng": 18.421305,
    "precision": "address",
    "confidence": 0.96,
    "ambiguous": false,
    "matched": { "housenumber": "25", "street": "Adderley Street", "locality": "Cape Town", "region": "ZA-WC", "country": "ZA" },
    "unmatched": []
  },
  "errors": []
}
  • precision is the field to read first. address: that house is in the data. interpolated: between the numbers either side. intersection: a street corner, where the two streets meet, with the second in matched.crossStreet. site: a named place — a shopping centre, office park, complex, hospital, school, airport or hotel — named in matched.site. street: the right street, not the house. locality: only the suburb or town.
  • confidence runs 0 to 1 and ranks answers; it is not a probability. ambiguous: true means a same-named street elsewhere scored about as well.
  • matched is spelt as the data spells it; unmatched lists what matched nothing.
  • One best guess, or result: null — and nothing found is still billed, because the search was done.
  • alternatives: up to 4 other candidates, best first, each shaped as the answer with the same extras, never with alternatives of their own. Always present, and free. Long Street, Cape Town answers the city centre’s and offers four more in the metro.
  • matched.suburb: the suburb the point lies in, the nearest within about 3 km. matched.locality is the place the caller wrote, so alternatives often share it; the suburb tells them apart (those Long Streets lie in Cape Town City Centre, Observatory, Maitland and so on). Absent at locality and where no suburb is near.

What to do with an answer

The answerDo
address, interpolated, intersection or site, not ambiguous, confidence 0.5 or moreDispatch (for a site, pass on the unit or shop number)
streetThe driver has the right street: ask the recipient for a pin or a landmark
ambiguous, or localityAsk the customer before dispatching, offering the answer and its alternatives

Expect a lot of street in South Africa: OpenStreetMap has house numbers on about 13% of its street names. A misspelling can still land on the right house with less certainty — Cape Tonw finds the same point as above at confidence: 0.59 and ambiguous: true, which the table sends to a person. And a bare place name answers as that place: { "q": "Rondebosch" } gives precision: locality at the suburb itself, which the table also sends to a person — a suburb is not a door.

Street corners

South African addresses are often a corner — Cnr Main Rd and Belmont Rd, Corner of Bree & Strand Streets, Main Rd / Belmont Rd. They answer at the point where the streets meet, and rank with an interpolated house, so dispatch on the same terms. Real output:

{
  "result": {
    "lat": -33.961421,
    "lng": 18.469939,
    "precision": "intersection",
    "confidence": 0.85,
    "ambiguous": false,
    "matched": { "street": "Main Road", "crossStreet": "Belmont Road", "locality": "Rondebosch", "region": "ZA-WC", "country": "ZA" },
    "unmatched": []
  },
  "errors": []
}

Two streets that never meet answer the first at street, with the second in unmatched. A & B and A / B without “Cnr” count only when both end in a street type, so a business name such as Smith & Sons stays a name.

Named sites

Many delivery addresses name a place rather than a street: a shopping centre, office park, complex, hospital, school, airport or hotel. They answer at a point inside it. Real output for Shop 12, Canal Walk, Century City:

{
  "result": {
    "lat": -33.892873,
    "lng": 18.511114,
    "precision": "site",
    "confidence": 0.8,
    "ambiguous": false,
    "matched": { "site": "Canal Walk", "locality": "Century City", "region": "ZA-WC", "country": "ZA" },
    "unmatched": []
  },
  "errors": []
}

The unit is not in the answer: “Shop 12” is yours to pass on to the driver, and it is what finds the door inside a mall. A site ranks with a corner, but a house, a corner or an estimated house in the same text still wins over it. Run-together names find it too — Tygervalley Centre, Bellville gives Tyger Valley Centre at confidence: 0.72 — but a place of the same name wins: a bare Tygervalley, Bellville is the suburb Tyger Valley, with the centre among its alternatives. Same-named sites far apart, schools especially, come back ambiguous.

Addresses that cannot be placed

An erf, stand, plot or farm portion (land parcels; there is no cadastre), a township stand, a PO box or a private bag has no point in the data. The part is set aside in unmatched and the answer is the place named, at locality — Erf 1234, Khayelitsha gives Khayelitsha, confidence: 0.36, unmatched: ["erf 1234"]. That is the table’s “ask the customer” row: ask for a street address or a pin. ambiguous: true also marks a near-miss spelling with no house number to confirm it.

POST /geocode/batch: a delivery list

{
  "country": "ZA",
  "items": [
    { "id": "order-1041", "q": "25 Adderley Street, Cape Town" },
    { "id": "order-1042", "address": { "street": "Long Street", "city": "Cape Town" } },
    { "id": "order-1043", "q": "   " }
  ]
}
  • Answered in order, each item found, not_found or invalid (with a code), plus billed and remaining. Invalid items are free.
  • country and region can be set per item; set, base and streetgeom apply to the batch. id is your label, echoed back.
  • A batch always completes. If the allowance runs out partway, every item is still answered and only what was left is charged; the next request is a 402.
  • Items per request, on the standard plans: 10 (demo), 100 (Personal), 1,000 (Basic and Pro). Over the cap is a 400 whose max says what you may send.

Refusals

StatuscodeWhenBilled
400invalid_requestThe body fails validation, or a batch is over the capNo
400empty_addressq is blank, or address has no street (in a batch: that item)No
400unsupported_countryA named country is not covered; the message lists those that areNo
402The allowance was already at 0No
200Found, or nothing foundYes
There is no coverage endpoint. The unsupported_country message is the live list of covered countries; today it reads Covered: ZA.

Public endpoints

No authentication, no metering.

MethodPathPurpose
GET/healthLiveness
GET/demo/keyThe shared public demo key
GET/streets/coverageCountries that answer base=street, with address counts
GET/route/coverageWhether routing answers, its modes, the cost in lookups, and each plan's routing caps
GET/payments/plansPlan catalogue with prices and caps
GET/versionWhich API build is serving
GET/openapi.jsonOpenAPI 3.1 spec — generate a client from it
There is no bulk geometry endpoint. /explore/world and /explore/country/:code served simplified country outlines and were removed on 8 September 2026 — unauthenticated, uncached and expensive (243 KB and 16.8 s per call), they were the one lever an anonymous caller could pull against the database. AtlasFetch answers point-in-polygon questions; it does not hand out boundary geometry.

Status codes

CodeMeaningRetry?
200Success — check errors[] for per-set problems—
400Invalid parameters; error says whatNo
401Missing, malformed or revoked keyNo
402Monthly allowance exhaustedAfter reset
403Plan does not include this, or a cap exceededNo
409Conflict, e.g. duplicate set nameNo
422Routes only: no road near a location, or no path. Billed — the route was attemptedNo
429Per-key rate limit — honour Retry-AfterYes
503Routes only: the routing engine is not answering. Not billedIn a few minutes

Rate limiting is per key, per second. Quota is per account, per month. Route errors also carry a machine-readable code beside error: invalid_request, too_far, avoid_too_complex, plan_feature, avoid_set_unavailable, no_road_nearby, no_route, routing_unavailable. Geocoding adds empty_address and unsupported_country.

Privacy and data

We do not store the coordinates you look up. Usage metering records the endpoint, the key and a timestamp — never the point. When a request comes from an official AtlasFetch client such as the MCP server, that client’s name and version are recorded too (for example atlasfetch-mcp/0.1.4); any other User-Agent is ignored and never stored.

Reference boundaries derive from OpenStreetMap under the ODbL. Attribution and share-alike obligations pass through to you — see data attribution. House-level answers also draw on municipal address points. In Cape Town: Contains address data from the City of Cape Town Open Data Portal, © City of Cape Town. Also contains address data from these municipalities, © each municipality: eThekwini Municipality (Durban); City of Tshwane (Pretoria); City of Ekurhuleni; Nelson Mandela Bay Municipality (Gqeberha); George Municipality; Langeberg Municipality (Robertson, Montagu, Ashton, Bonnievale, McGregor). AtlasFetch is not affiliated with or endorsed by any of these municipalities. Your own uploaded boundaries are private to your account and are never returned to anyone else.

Try it without writing any code

The playground loads a demo key automatically and shows the exact request URL.

Open the playground

Use it inside Claude Code

claude mcp add atlasfetch -- npx -y atlasfetch-mcp

Without a key it uses the shared demo key, 1,000 lookups a month across everyone. Add your own with -e ATLASFETCH_API_KEY.