{"openapi":"3.1.0","info":{"title":"AtlasFetch","version":"1.0.0","summary":"Point-in-polygon boundary lookup and geofencing.","description":"Send a coordinate; get back the country, region and municipality that contain it,\nplus matches from your own uploaded boundary sets — in one call.\n\n**When to use it:** reverse geocoding to administrative areas, geofencing against your\nown polygons, jurisdiction and region checks, enriching location data with region codes.\n\n**When not to:** street addresses or postcodes (this is not forward geocoding, and\nreverse lookups stop at the municipality), routing or map tiles, retrieving boundary\ngeometry, many coordinates in one request, or enter/exit event tracking.\n\nA fuller reference for automated clients is at https://atlasfetch.xyz/llms.txt\n\nReference boundaries derive from OpenStreetMap under the ODbL; attribution and\nshare-alike obligations pass through to you. Looked-up coordinates are not stored.","termsOfService":"https://atlasfetch.xyz/terms","contact":{"name":"AtlasFetch","url":"https://atlasfetch.xyz"},"license":{"name":"ODbL (reference boundary data)","url":"https://atlasfetch.xyz/attribution"}},"servers":[{"url":"https://api.atlasfetch.xyz","description":"Production"}],"externalDocs":{"description":"Human-readable documentation","url":"https://atlasfetch.xyz/docs"},"tags":[{"name":"Lookup","description":"The point-in-polygon lookup."},{"name":"Boundary sets","description":"Your own polygons, grouped into named sets."},{"name":"Public","description":"No authentication, no metering."}],"paths":{"/location/lookup":{"get":{"tags":["Lookup"],"summary":"Resolve a coordinate to the boundaries that contain it","description":"Point-in-polygon lookup. Returns the administrative boundaries containing the point\nand any matches from the account's own boundary sets.\n\nOne request is one billed lookup, however many layers, sets or grid codes it touches.\nMetering happens before matching, so a call that matches nothing still bills.\n\n`errors[]` is present on every response. A set that cannot be queried is skipped and\nreported there with HTTP 200 — it does not fail the request.","operationId":"locationLookup","deprecated":false,"security":[{"bearerAuth":[]}],"parameters":[{"name":"lat","in":"query","required":true,"description":"Latitude, -90 to 90.","schema":{"type":"number","minimum":-90,"maximum":90}},{"name":"lng","in":"query","required":true,"description":"Longitude, -180 to 180.","schema":{"type":"number","minimum":-180,"maximum":180}},{"name":"base","in":"query","required":false,"description":"Comma-separated reference layers to resolve. Defaults to all three.","schema":{"type":"string","default":"country,region,municipal"},"example":"country,region"},{"name":"set","in":"query","required":false,"description":"Comma-separated names of your own boundary sets to match against. Defaults to none.","schema":{"type":"string"},"example":"delivery_zones"},{"name":"encode","in":"query","required":false,"description":"Comma-separated grid encodings to express the point in: `h3`, `pluscode`. Output only — neither is accepted as a location. Not metered.","schema":{"type":"string"},"example":"h3,pluscode"},{"name":"h3res","in":"query","required":false,"description":"H3 resolution, 0-15. r9 is roughly 400 m across.","schema":{"type":"integer","minimum":0,"maximum":15,"default":9}},{"name":"pluslen","in":"query","required":false,"description":"Plus Code length. 9 is not a valid Plus Code length.","schema":{"type":"integer","enum":[2,4,6,8,10,11,12,13,14,15],"default":10}}],"responses":{"200":{"description":"The point resolved. Check `errors[]` for per-set problems.","headers":{"X-Lookups-Remaining":{"description":"Lookups left in the current billing period.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LookupResponse"}}}},"400":{"description":"Invalid parameters — including a Plus Code or H3 index sent as the location, which is refused on purpose. Not billed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Monthly lookup allowance exhausted. Retry after the period resets.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-key rate limit exceeded. Honour Retry-After. Not billed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/location/info":{"get":{"tags":["Lookup"],"summary":"Resolve a coordinate (deprecated alias of /location/lookup)","description":"Point-in-polygon lookup. Returns the administrative boundaries containing the point\nand any matches from the account's own boundary sets.\n\nOne request is one billed lookup, however many layers, sets or grid codes it touches.\nMetering happens before matching, so a call that matches nothing still bills.\n\n`errors[]` is present on every response. A set that cannot be queried is skipped and\nreported there with HTTP 200 — it does not fail the request.","operationId":"locationInfo","deprecated":true,"security":[{"bearerAuth":[]}],"parameters":[{"name":"lat","in":"query","required":true,"description":"Latitude, -90 to 90.","schema":{"type":"number","minimum":-90,"maximum":90}},{"name":"lng","in":"query","required":true,"description":"Longitude, -180 to 180.","schema":{"type":"number","minimum":-180,"maximum":180}},{"name":"base","in":"query","required":false,"description":"Comma-separated reference layers to resolve. Defaults to all three.","schema":{"type":"string","default":"country,region,municipal"},"example":"country,region"},{"name":"set","in":"query","required":false,"description":"Comma-separated names of your own boundary sets to match against. Defaults to none.","schema":{"type":"string"},"example":"delivery_zones"},{"name":"encode","in":"query","required":false,"description":"Comma-separated grid encodings to express the point in: `h3`, `pluscode`. Output only — neither is accepted as a location. Not metered.","schema":{"type":"string"},"example":"h3,pluscode"},{"name":"h3res","in":"query","required":false,"description":"H3 resolution, 0-15. r9 is roughly 400 m across.","schema":{"type":"integer","minimum":0,"maximum":15,"default":9}},{"name":"pluslen","in":"query","required":false,"description":"Plus Code length. 9 is not a valid Plus Code length.","schema":{"type":"integer","enum":[2,4,6,8,10,11,12,13,14,15],"default":10}}],"responses":{"200":{"description":"The point resolved. Check `errors[]` for per-set problems.","headers":{"X-Lookups-Remaining":{"description":"Lookups left in the current billing period.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LookupResponse"}}}},"400":{"description":"Invalid parameters — including a Plus Code or H3 index sent as the location, which is refused on purpose. Not billed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Monthly lookup allowance exhausted. Retry after the period resets.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-key rate limit exceeded. Honour Retry-After. Not billed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/boundaries/sets":{"get":{"tags":["Boundary sets"],"summary":"List the account's boundary sets","description":"Includes each set's boundary count and the ids of the API keys it is granted to.","operationId":"listBoundarySets","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The sets belonging to the account.","content":{"application/json":{"schema":{"type":"object","properties":{"sets":{"type":"array","items":{"$ref":"#/components/schemas/BoundarySet"}}},"required":["sets"]}}}},"401":{"description":"Missing, malformed or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Boundary sets"],"summary":"Create a boundary set","description":"A new set is created with `available: false` and no key grants, so it matches nothing\nuntil both are changed.\n\n**A client holding only an API key cannot finish this on its own.** Grants are addressed\nby API key *id*, and the only endpoint that lists key ids requires a browser session, so\nthe grant step happens in the dashboard at https://atlasfetch.xyz/dashboard","operationId":"createBoundarySet","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$","description":"1-32 letters, digits, hyphens or underscores, starting with a letter or digit."}},"required":["name"]}}}},"responses":{"201":{"description":"The set was created — unavailable and granted to no key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoundarySetCreated"}}}},"400":{"description":"The name does not match the allowed pattern.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The plan's set cap was reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A set of that name already exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/boundaries/sets/{name}":{"parameters":[{"name":"name","in":"path","required":true,"description":"The set name.","schema":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$","description":"1-32 letters, digits, hyphens or underscores, starting with a letter or digit."}}],"patch":{"tags":["Boundary sets"],"summary":"Toggle availability, grant or revoke keys","description":"A set is only queryable when `available` is true **and** the calling key is granted it.","operationId":"updateBoundarySet","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"available":{"type":"boolean","description":"The account-wide master switch for this set."},"grant":{"type":"array","maxItems":50,"items":{"type":"string"},"description":"API key ids to grant."},"revoke":{"type":"array","maxItems":50,"items":{"type":"string"},"description":"API key ids to revoke."}}}}}},"responses":{"200":{"description":"The updated set, in the same shape as the list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoundarySet"}}}},"401":{"description":"Missing, malformed or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such set on this account, or a key id in grant/revoke is not one of its keys.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Boundary sets"],"summary":"Delete a set and every boundary in it","operationId":"deleteBoundarySet","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ok"}}}},"401":{"description":"Missing, malformed or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such set on this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/boundaries":{"post":{"tags":["Boundary sets"],"summary":"Add a polygon to a set","description":"Stores one polygon in an existing set. Each call creates a new boundary; the same name twice\nstores two.\n\nPlan caps apply to vertices per polygon (every ring position counts, including the closing\none), properties per boundary, and boundaries per set. At the boundaries-per-set cap the\nPublic plan — the shared demo key — silently deletes the oldest boundary to make room; every\nother plan refuses with 403.","operationId":"createBoundary","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"set":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$","description":"The existing set to add it to."},"name":{"type":"string","minLength":1,"maxLength":100,"description":"Returned when a looked-up point falls inside it."},"geometry":{"$ref":"#/components/schemas/PolygonGeometry"},"properties":{"$ref":"#/components/schemas/BoundaryProperties"}},"required":["set","name","geometry"]}}}},"responses":{"201":{"description":"The boundary was stored. Geometry is not echoed back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoundaryCreated"}}}},"400":{"description":"Not a valid GeoJSON Polygon (wrong type, unclosed or short ring, out-of-range or non-numeric positions, self-intersection), a bad name, or a property of the wrong type or length.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"A plan cap was hit: too many vertices, too many properties, a property the Public plan does not allow, or a full set on a plan that refuses rather than evicts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such set on this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/boundaries/view":{"get":{"tags":["Boundary sets"],"summary":"Read back your own boundaries, with geometry","description":"Only ever returns boundaries belonging to the calling account. Reference boundaries are never returned.","operationId":"viewBoundaries","security":[{"bearerAuth":[]}],"parameters":[{"name":"set","in":"query","required":true,"description":"The set to read.","schema":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$","description":"1-32 letters, digits, hyphens or underscores, starting with a letter or digit."}}],"responses":{"200":{"description":"The set and its boundaries, oldest first, with geometry.","content":{"application/json":{"schema":{"type":"object","properties":{"set":{"type":"string"},"available":{"type":"boolean"},"boundaries":{"type":"array","items":{"$ref":"#/components/schemas/BoundaryWithGeometry"}}},"required":["set","available","boundaries"]}}}},"401":{"description":"Missing, malformed or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such set on this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/boundaries/{id}":{"delete":{"tags":["Boundary sets"],"summary":"Delete one boundary","operationId":"deleteBoundary","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The boundary id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ok"}}}},"401":{"description":"Missing, malformed or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such boundary on this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/health":{"get":{"tags":["Public"],"summary":"Liveness","operationId":"health","security":[],"responses":{"200":{"description":"The service is up.","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","const":"ok"}},"required":["status"]}}}}}}},"/version":{"get":{"tags":["Public"],"summary":"Which build is serving","operationId":"version","security":[],"responses":{"200":{"description":"The image tag and Cloud Run revision.","content":{"application/json":{"schema":{"type":"object","properties":{"version":{"type":"string","examples":["v23"]},"revision":{"type":["string","null"]}},"required":["version","revision"]}}}}}}},"/openapi.json":{"get":{"tags":["Public"],"summary":"This document","operationId":"openapi","security":[],"responses":{"200":{"description":"The OpenAPI description of this API."}}}},"/demo/key":{"get":{"tags":["Public"],"summary":"The shared public demo key","description":"Returns an API key belonging to one communal account. Its quota, rate limit and single\nboundary set are pooled across every anonymous user — fine for evaluation, wrong for\nanything real. Create your own key at https://atlasfetch.xyz/dashboard","operationId":"demoKey","security":[],"responses":{"200":{"description":"The demo key and the public plan's caps.","content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string"},"plan":{"type":"object","properties":{"label":{"type":"string"},"monthlyLookups":{"type":"integer"},"rateLimitPerSec":{"type":"integer"},"boundaries":{"$ref":"#/components/schemas/BoundaryCaps"}}}},"required":["key","plan"]}}}},"404":{"description":"No demo key is configured on this deployment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/payments/plans":{"get":{"tags":["Public"],"summary":"The plan catalogue","operationId":"plans","security":[],"responses":{"200":{"description":"Every plan, with prices and caps.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Plan"}}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"An API key, sent as `Authorization: Bearer <key>` — a 64-character hex string with no\nprefix. Create one at https://atlasfetch.xyz/dashboard, or call `GET /demo/key` for the\nshared public key.\n\nThe dashboard endpoints (key management, usage, billing, members) accept only a browser\nsession, which an automated client cannot obtain, and are not described here."}},"schemas":{"Error":{"type":"object","description":"Every 4xx and 5xx body has this shape.","properties":{"error":{"type":"string","description":"What went wrong, in a sentence."},"details":{"type":"object","additionalProperties":true,"description":"Per-field validation problems, when there are any."}},"required":["error"]},"Ok":{"type":"object","properties":{"ok":{"type":"boolean","const":true}},"required":["ok"]},"ApiError":{"type":"object","description":"A problem with part of a request that still succeeded.","properties":{"type":{"type":"string","enum":["access","usage"],"description":"`access` — a set was skipped. `usage` — quota exhausted, mirroring a 402."},"message":{"type":"string"}},"required":["type","message"]},"LayerHit":{"type":["object","null"],"description":"A matched boundary. `null` when that layer matched nothing.","properties":{"code":{"type":["string","null"],"description":"ISO 3166-1 for country, ISO 3166-2 for region, and for municipal the official ISO code where OpenStreetMap carries one, otherwise a generated `B-<country>-<n>`. Generated codes are not yet stable across a full reseed; ISO codes are.","examples":["GB-ENG"]},"name":{"type":"string","examples":["England"]}},"required":["code","name"]},"SetMatch":{"type":"object","properties":{"name":{"type":"string","description":"The boundary name you gave it."},"properties":{"type":"object","additionalProperties":true,"description":"Whatever you attached to that boundary."}},"required":["name"]},"LookupResponse":{"type":"object","properties":{"base":{"type":"object","description":"One entry per layer named in `base`. A layer that matched nothing is present and null, never absent.","properties":{"country":{"$ref":"#/components/schemas/LayerHit"},"region":{"$ref":"#/components/schemas/LayerHit"},"municipal":{"allOf":[{"$ref":"#/components/schemas/LayerHit"}],"description":"The finest unit available below the region tier — not a consistent kind of thing. Los Angeles returns a city; rural Kansas returns a county. Do not assume it names a city."}}},"sets":{"type":"object","description":"Keyed by set name; each value is every boundary in that set containing the point.","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/SetMatch"}}},"encoded":{"type":"object","description":"Absent entirely unless `encode` was requested.","properties":{"h3":{"type":"string","examples":["89195da49b7ffff"]},"pluscode":{"type":"string","examples":["9C3XGV4C+VX"]}}},"errors":{"type":"array","description":"Always present, empty or not. Read it unconditionally.","items":{"$ref":"#/components/schemas/ApiError"}}},"required":["base","sets","errors"]},"BoundarySet":{"type":"object","description":"Returned by GET /boundaries/sets and PATCH /boundaries/sets/{name}.","properties":{"id":{"type":"string"},"name":{"type":"string"},"available":{"type":"boolean","description":"The account-wide switch. False on creation."},"boundaryCount":{"type":"integer"},"grantedKeys":{"type":"array","description":"The API keys allowed to query this set.","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"}},"required":["id","label"]}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","available","boundaryCount","grantedKeys","createdAt","updatedAt"]},"BoundarySetCreated":{"type":"object","description":"Returned by POST /boundaries/sets. Narrower than BoundarySet: a new set has no boundaries and no grants.","properties":{"id":{"type":"string"},"name":{"type":"string"},"available":{"type":"boolean","const":false},"createdAt":{"type":"string","format":"date-time"}},"required":["id","name","available","createdAt"]},"PolygonGeometry":{"type":"object","description":"A single GeoJSON Polygon. MultiPolygon, Feature and FeatureCollection are rejected. Positions are [longitude, latitude] within range. Each ring has at least 4 positions and is closed (last equals first); rings after the first are holes. Self-intersecting polygons are rejected.","properties":{"type":{"type":"string","const":"Polygon"},"coordinates":{"type":"array","minItems":1,"items":{"type":"array","minItems":4,"items":{"type":"array","minItems":2,"items":{"type":"number"}}}}},"required":["type","coordinates"],"examples":[{"type":"Polygon","coordinates":[[[-0.2,51.4],[-0.2,51.6],[0,51.6],[0,51.4],[-0.2,51.4]]]}]},"BoundaryProperties":{"type":"object","description":"Flat key/value pairs returned with every match. Keys up to 32 characters; values a string (up to 256 characters), number or boolean. The Public plan (the shared demo key) accepts only category (zone, area, route, place, other), color (green, blue, red, yellow, purple, orange) and priority (low, medium, high).","additionalProperties":{"anyOf":[{"type":"string","maxLength":256},{"type":"number"},{"type":"boolean"}]}},"BoundaryCreated":{"type":"object","description":"Returned by POST /boundaries.","properties":{"id":{"type":"string"},"set":{"type":"string"},"name":{"type":"string"},"properties":{"$ref":"#/components/schemas/BoundaryProperties"},"pointCount":{"type":"integer","description":"Positions across all rings, the number the plan cap counts."},"createdAt":{"type":"string","format":"date-time"}},"required":["id","set","name","properties","pointCount","createdAt"]},"BoundaryWithGeometry":{"type":"object","description":"One boundary as returned inside GET /boundaries/view.","properties":{"id":{"type":"string"},"name":{"type":"string"},"properties":{"$ref":"#/components/schemas/BoundaryProperties"},"pointCount":{"type":"integer"},"createdAt":{"type":"string","format":"date-time"},"geometry":{"$ref":"#/components/schemas/PolygonGeometry"}},"required":["id","name","properties","pointCount","createdAt","geometry"]},"BoundaryCaps":{"type":"object","properties":{"maxSets":{"type":"integer"},"maxGeomsPerSet":{"type":"integer"},"maxPointsPerGeom":{"type":"integer"},"maxPropsPerGeom":{"type":"integer"},"propsMode":{"type":"string","enum":["allowlist","freeform"]},"eviction":{"type":"string","enum":["evict","reject"],"description":"At the cap: `evict` replaces the oldest, `reject` refuses."}}},"Plan":{"type":"object","properties":{"id":{"type":"string","enum":["public","personal","basic","pro"]},"label":{"type":"string"},"priceGBP":{"type":"integer","description":"Pence per month. 0 for the free plans."},"priceDisplay":{"type":"string","examples":["£9"]},"monthlyLookups":{"type":"integer"},"rateLimitPerSec":{"type":"integer"},"boundaries":{"$ref":"#/components/schemas/BoundaryCaps"},"selfServe":{"type":"boolean","description":"Whether it can be bought online."}}}}}}