tools · Listed · eip155:8453 · Found · 50 endpoints · Gateway-eligible
api.geoprimitives.dev
api.geoprimitives.dev
Give it a latitude and longitude and it answers with what the nautical chart says about the water there: the depth range of the charted depth area containing th
Indexed from this operator's public /.well-known/x402.json. Found is not operator-owned and is not attested. Claim or opt out.
Agent Read · Cleared Index
ROUTE
Route when you need tools at published x402 prices.
confidence
78%
source
signal
Index before you pay. Same payload for agents:
GET /api/cleared/agent-read?slug=cat-api-geoprimitives-dev
When to call
- Need tools via x402 and want Cleared-indexed payTo with a live scorecard.
- Budget between $1000 and $10000 per call on published endpoints.
- Give it a latitude and longitude and it answers with what the nautical chart says about the water there: the depth range of the charted depth area containing th
Risks
- Found — not operator-owned; claim status unknown.
- No Cleared settlement receipt on file yet.
- No Gateway traffic yet — market share unproven.
Price posture
Published 50 endpoints from $1000 to $10000.
Category · Gateway
tools · no Gateway routes yet — early / unproven on Cleared market share.
Endpoint hints
POST /v1/helloEcho endpoint. Returns the name you send, the network the payment settled on, and the price you paid. Use it to prove your x402 client works end to end -- disco
POST /v1/geojson/validateValidate GeoJSON against RFC 7946 and get back a structured list of problems, not a bare boolean. Checks structure, coordinate range, ring closure, ring winding
POST /v1/geojson/repairRepair GeoJSON and get the fixed document back with a log of every alteration: rings rewound to RFC 7946 winding order, consecutive duplicate vertices dropped,
POST /v1/transformReproject coordinates between EPSG coordinate reference systems, with the datum shift applied. Accepts a bare position, an array of positions at any nesting dep
POST /v1/geometry/bufferBuffer a geometry by a distance in metres and get the offset polygon back. Use it for service areas, setbacks, and proximity envelopes you will later test with
POST /v1/geometry/simplifyDrop vertices from a geometry while keeping its shape. Use it to cut a boundary down before a call whose cost is vertex-driven -- POST /v1/pip caps on points ti
POST /v1/geometry/unionDissolve overlapping or adjacent polygons into a single shape: merging parcels, coverage areas or service zones into one boundary before measuring or testing it
POST /v1/geometry/intersectClip polygons to their common area: how much of a parcel falls inside a flood zone, where two service areas overlap, what a boundary and a study area share. Pai
Evidence (Cleared)
- → Intake verified · Gateway-eligible
- → Trust 70/100 · pass · tier listed
- → Protocol x402 · eip155:8453
- → Manifest reachable · schema valid
- → Found listing — indexed from public x402.json, not operator-attested.
Endpoints
Echo a string back. The cheapest way to test your payment client.
Not used$1000POST https://api.geoprimitives.dev/v1/helloEcho endpoint. Returns the name you send, the network the payment settled on, and the price you paid. Use it to prove your x402 client works end to end -- discover, read the 402, sign, retry, get a body -- before you spend anything on a real endpoint. It is the cheapest route in the catalog and it computes nothing, so a failure here is a payment problem and never a geospatial one. Do not use it for anything else: it has no geospatial behaviour at all. It is priced at exactly what it costs us to settle one onchain transaction, so the whole of what you pay here goes to the facilitator and none of it to us. That is deliberate: this route is worth having only because it is genuinely paid, since a free echo would prove nothing about whether your client can pay. Price: $0.001 per call (0.001 USD, 1000 atomic units of USDC at 6 decimals), settled on eip155:8453 via the x402 protocol. There is no account, no API key and no subscription; the payment travels with the request. Requests are validated before they are priced. A request that is over the size cap, is not parseable JSON, declares a Content-Type that is not JSON, is missing a field this schema marks required is refused with a 400 carrying a machine code, and no 402 is issued at all: nothing is quoted, nothing is signed, nothing settles. Anything that needs the geometry read is checked after payment, and a 400 from that stage cancels settlement rather than completing it, so it still costs you a round trip but not the price. Satisfy the schema before you send: that is what it is published for.
Check GeoJSON against RFC 7946 and say exactly what is wrong and where.
$5000POST https://api.geoprimitives.dev/v1/geojson/validateValidate GeoJSON against RFC 7946 and get back a structured list of problems, not a bare boolean. Checks structure, coordinate range, ring closure, ring winding order, consecutive duplicate vertices, self-intersections, and hole nesting. Every issue carries the feature, geometry, ring and position index it sits on, so a 500-feature collection is actionable rather than merely condemned. Reach for it before paying for an operation on data you did not author, and to find out why /v1/geometry, /v1/h3/polyfill or /v1/pip refused a document: those endpoints reject unclosed rings and out-of-range coordinates rather than guessing. Errors make a document invalid; warnings -- duplicate vertices, the crs member RFC 7946 removed -- do not, and are reported separately. One price per call whatever the document: a single Point costs exactly what a 1 MiB FeatureCollection costs, so send the whole collection rather than a document at a time. That is measured rather than assumed -- a Point is about 0.0015 ms of compute on this runtime and a 1 MiB collection about 10 ms, a spread far below what it costs to settle one payment -- which is why there is no size tier here to choose between and no bulk sibling to look for. Nothing is modified here: POST /v1/geojson/repair is the endpoint that rewrites. Limits: 1 MiB request body, and a self-intersection budget of 2000000 segment-pair comparisons per request, enough for any ring under about 1400 vertices. Rings past the budget are listed in checksSkipped with the reason, never silently passed as clean. Price: $0.005 per call (0.005 USD, 5000 atomic units of USDC at 6 decimals), settled on eip155:8453 via the x402 protocol. There is no account, no API key and no subscription; the payment travels with the request. Limits the handler enforces. Every one of these is a 400, and see the note below about when a 400 is refused free: - bodyBytes: at most 1048576 bytes. 1 MiB request body, checked against Content-Length and again against the body itself. Roughly 20k-40k positions of real GeoJSON. Split the collection and post it in parts. - selfIntersectionBudget: at most 2000000 segment-pair comparisons. The self-intersection pass is quadratic in a ring's segments, so it runs cheapest ring first until this budget is spent. Anything left over is reported in checksSkipped with its path. Covers any ring under about 1400 vertices outright. Requests are validated before they are priced. A request that is over the size cap, is not parseable JSON, declares a Content-Type that is not JSON is refused with a 400 carrying a machine code, and no 402 is issued at all: nothing is quoted, nothing is signed, nothing settles. Anything that needs the geometry read is checked after payment, and a 400 from that stage cancels settlement rather than completing it, so it still costs you a round trip but not the price. Satisfy the schema before you send: that is what it is published for.
Rewrite broken GeoJSON into RFC 7946 form and log every change.
Not used$10000POST https://api.geoprimitives.dev/v1/geojson/repairRepair GeoJSON and get the fixed document back with a log of every alteration: rings rewound to RFC 7946 winding order, consecutive duplicate vertices dropped, unclosed rings closed, and polygon nesting fixed (mis-ordered exterior rings, holes that fall outside their exterior, MultiPolygon members that are really holes). Reach for it when POST /v1/geojson/validate reported errors, or when /v1/geometry, /v1/h3/polyfill or /v1/pip refused your document -- unclosed rings are the usual reason, and this is the cheapest way to clear them. Conservative by design: it only rewrites the classes above, and anything else it found is returned in unrepaired rather than guessed at, so it will not invent geometry or move a coordinate. Idempotent -- a conformant document comes back untouched with changed:false, so a second call costs money and changes nothing. Your input object is never mutated. Coordinate values are preserved exactly; nothing is rounded or reprojected. Limit: 1 MiB request body. Price: $0.01 per call (0.01 USD, 10000 atomic units of USDC at 6 decimals), settled on eip155:8453 via the x402 protocol. There is no account, no API key and no subscription; the payment travels with the request. Limits the handler enforces. Every one of these is a 400, and see the note below about when a 400 is refused free: - bodyBytes: at most 1048576 bytes. 1 MiB request body, checked against Content-Length and again against the body itself. Split the collection and post it in parts. Requests are validated before they are priced. A request that is over the size cap, is not parseable JSON, declares a Content-Type that is not JSON, is missing a field this schema marks required is refused with a 400 carrying a machine code, and no 402 is issued at all: nothing is quoted, nothing is signed, nothing settles. Anything that needs the geometry read is checked after payment, and a 400 from that stage cancels settlement rather than completing it, so it still costs you a round trip but not the price. Satisfy the schema before you send: that is what it is published for.
Reproject coordinates or GeoJSON between 1380 EPSG codes, datum shifts included.
$5000POST https://api.geoprimitives.dev/v1/transformReproject coordinates between EPSG coordinate reference systems, with the datum shift applied. Accepts a bare position, an array of positions at any nesting depth, or a GeoJSON Feature, FeatureCollection or Geometry, and answers in whichever shape it was given, preserving properties, ids and foreign members. Covers 1380 codes: WGS 84, ITRF2020, NAD27 and every NAD83 realization as geographic, Web Mercator, all 120 WGS 84 UTM zones north and south, the NAD27 and NAD83 UTM zones, and the NAD27 and NAD83 State Plane and low-distortion zones, Oregon and Alaska included. Reach for it before calling /v1/geometry, /v1/h3/index, /v1/h3/polyfill or /v1/pip, which take EPSG:4326 only and refuse anything outside -180..180 and -90..90. One price per call whatever the payload: 10000 positions cost exactly what one position costs, so batch rather than sending a position at a time. Horizontal only: elevations pass through untouched. Datum shifts, and read this before buying: two mechanisms run between the two sides and the response names every step. Between NAD27 and the NAD83 realizations (NAD83, HARN, NSRS2007, 2011, PA11) the NOAA NGS NADCON 5.0 grids are applied, 2016-09-01 release, public domain, biquadratic interpolation as NGS specifies, covering the conterminous United States, Alaska, Hawaii and Puerto Rico with the US Virgin Islands; a position outside that coverage is refused with outside_datum_grid rather than answered without the shift. Between WGS 84 and NAD83(2011) or NAD83(PA11) the EPSG time-dependent Helmert transformations 10334 and 10336 are applied with WGS 84 read as ITRF2020, which the current WGS 84 (G2296) realizes to a few centimetres. That transformation depends on when the coordinates were observed, because the North American plate moves about 2 cm a year against ITRF2020: send "epoch" as a decimal year (2024.5) to say when, or it is evaluated at 2010.0, the NAD83(2011) reference epoch, and the response says which. Every response carries datumShiftApplied and a datumShift object listing the grids or Helmert that ran, the region, the epoch and the data provenance; when both sides share a datum it says so and applies nothing. Accuracy was measured against PROJ 9.5.1 running the same grids and the same EPSG operations: the projection arithmetic agrees to under 1e-8 m and the shifted result to under 1e-6 m, so the 1.29 m gap this route carried before the grids is closed to the limit of the source data, which NGS states as a few centimetres for the realization grids and decimetres for NAD27. If a grid cannot be read the request is answered 503 and not charged; it is never answered without the shift. A position that lands outside the target projection's valid domain is rejected with its index, never returned as a null or infinite coordinate. Limits: 10000 positions per call across the whole request, 512 datum-grid tiles per call (about one degree square per grid step; a state-sized batch is well under), and a 1 MiB request body. Price: $0.005 per call (0.005 USD, 5000 atomic units of USDC at 6 decimals), settled on eip155:8453 via the x402 protocol. There is no account, no API key and no subscription; the payment travels with the request. Limits the handler enforces. Every one of these is a 400, and see the note below about when a 400 is refused free: - positions: at most 10000 positions. Counted across the whole request, coordinates and geojson alike, at every nesting depth. Split the batch and post it in parts. - gridTiles: at most 512 tiles. Distinct NADCON5 grid tiles a request may touch, summed over every grid step. A tile is 64 by 64 grid cells, about one degree square on the finest grids; a batch of any size within one state stays well under. Group positions by area and post them in parts. - bodyBytes: at most 1048576 bytes. 1 MiB request body, checked against Content-Length and again against the body itself. Requests are validated before they are priced. A request that is over the size cap, is not parseable JSON, declares a Content-Type that is not JSON, is missing a field this schema marks required is refused with a 400 carrying a machine code, and no 402 is issued at all: nothing is quoted, nothing is signed, nothing settles. Anything that needs the geometry read is checked after payment, and a 400 from that stage cancels settlement rather than completing it, so it still costs you a round trip but not the price. Satisfy the schema before you send: that is what it is published for.
Offset a geometry by a distance in metres, corrected for the ellipsoid.
$10000POST https://api.geoprimitives.dev/v1/geometry/bufferBuffer a geometry by a distance in metres and get the offset polygon back. Use it for service areas, setbacks, and proximity envelopes you will later test with POST /v1/pip. Accepts any geometry -- point, line, polygon, or a collection of them, each buffered about its own centre latitude -- and answers with a Feature, or a FeatureCollection when you sent several. A buffer that collapses (a negative distance larger than the shape, or any negative distance on a point or a line) answers geojson:null with empty:true, which is an answer and not an error. Geodesic-aware: turf buffers in an azimuthal equidistant plane scaled by a sphere, which is a systematic -0.56% error at the equator running to +0.44% at 80 deg latitude, and this endpoint cancels that by scaling the distance to the Gaussian radius of curvature at the feature's own centre latitude. Measured against GeographicLib the result is then within +/-0.34% of the requested distance at the equator, +/-0.08% at 60 deg, +/-0.04% at 70 deg and +/-0.01% at 80 deg; the residual is the ellipsoid's north-south versus east-west curvature, which no single sphere can represent, and it shrinks towards the poles. Between vertices the polygon edge cuts a further 1 - cos(pi/(4*steps)) inside the true offset curve, 0.054% at the default steps=24. Input is EPSG:4326 only. Limits: 2000 positions across the whole document, 1 MiB request body, distance within +/-1000000 m, steps 1 to 128. Price: $0.01 per call (0.01 USD, 10000 atomic units of USDC at 6 decimals), settled on eip155:8453 via the x402 protocol. There is no account, no API key and no subscription; the payment travels with the request. Limits the handler enforces. Every one of these is a 400, and see the note below about when a 400 is refused free: - positions: at most 2000 positions. Counted across the whole document. This cap is CPU, not bandwidth: jsts BufferOp is quadratic in practice, and 2000 positions of pathological input is already about 250 ms. - distanceMeters: at most 1000000 metres. 1000 km either way. Past that the azimuthal equidistant plane distorts by more than every other error here combined: 0.004% at 100 km, 0.41% at 1000 km, 1.7% at 2000 km. - steps: at most 128 segments per quadrant. Minimum 1, default 24. Higher costs almost nothing; the cost is in the noding pass. - bodyBytes: at most 1048576 bytes. 1 MiB request body, refused on Content-Length before the body is read and again on the body itself. Split the request and post it in parts. Requests are validated before they are priced. A request that is over the size cap, is not parseable JSON, declares a Content-Type that is not JSON, is missing a field this schema marks required is refused with a 400 carrying a machine code, and no 402 is issued at all: nothing is quoted, nothing is signed, nothing settles. Anything that needs the geometry read is checked after payment, and a 400 from that stage cancels settlement rather than completing it, so it still costs you a round trip but not the price. Satisfy the schema before you send: that is what it is published for.
Thin out vertices with Douglas-Peucker, in metres or in degrees.
$10000POST https://api.geoprimitives.dev/v1/geometry/simplifyDrop vertices from a geometry while keeping its shape. Use it to cut a boundary down before a call whose cost is vertex-driven -- POST /v1/pip caps on points times polygon vertices, and POST /v1/geometry/buffer and the overlay ops cap at 2000 positions -- and to shrink what you store or draw. The response reports outputVertices next to inputVertices, so the saving is measurable before you spend again. The algorithm is Douglas-Peucker. Send toleranceMeters for a ground tolerance applied isotropically: both axes are scaled to metres at the input's centre latitude first, so at 70 deg N the simplification is not three times as aggressive east-west as north-south, which a raw degree tolerance makes it. Send tolerance instead for the raw degree tolerance if you want turf's behaviour unchanged. Retained vertices come back exactly as sent rather than round-tripped through the scaling. Not topology-preserving: adjacent polygons simplified separately can gap or overlap along a shared edge. Input is EPSG:4326 only. Limits: 100000 positions across the whole document, 1 MiB request body. Price: $0.01 per call (0.01 USD, 10000 atomic units of USDC at 6 decimals), settled on eip155:8453 via the x402 protocol. There is no account, no API key and no subscription; the payment travels with the request. Limits the handler enforces. Every one of these is a 400, and see the note below about when a 400 is refused free: - positions: at most 100000 positions. Counted across the whole document. Douglas-Peucker is linear, so in any realistic request the 1 MiB body cap binds first: this is the backstop against a body of repeated [0,0]. - bodyBytes: at most 1048576 bytes. 1 MiB request body, refused on Content-Length before the body is read and again on the body itself. Split the request and post it in parts. Requests are validated before they are priced. A request that is over the size cap, is not parseable JSON, declares a Content-Type that is not JSON, is missing a field this schema marks required is refused with a 400 carrying a machine code, and no 402 is issued at all: nothing is quoted, nothing is signed, nothing settles. Anything that needs the geometry read is checked after payment, and a 400 from that stage cancels settlement rather than completing it, so it still costs you a round trip but not the price. Satisfy the schema before you send: that is what it is published for.
Merge two or more polygons into one shape.
$10000POST https://api.geoprimitives.dev/v1/geometry/unionDissolve overlapping or adjacent polygons into a single shape: merging parcels, coverage areas or service zones into one boundary before measuring or testing it. Send a FeatureCollection of Polygons or MultiPolygons; the answer is a single Polygon or MultiPolygon Feature, or null with empty:true when nothing is left. Clipping is planar in longitude/latitude, the way every polygon-clipping library does it: edges are straight in degrees rather than geodesics, and the two differ on edges spanning more than about a degree. The 2000-position cap is CPU rather than bandwidth: the sweep-line overlay is quadratic on self-intersecting input, where 2000 positions is already about 350 ms. Input is EPSG:4326 only. Limits: 2000 positions across all members, 1 MiB request body, and at least two polygonal geometries or the call is refused. Price: $0.01 per call (0.01 USD, 10000 atomic units of USDC at 6 decimals), settled on eip155:8453 via the x402 protocol. There is no account, no API key and no subscription; the payment travels with the request. Limits the handler enforces. Every one of these is a 400, and see the note below about when a 400 is refused free: - positions: at most 2000 positions. Counted across every member of the collection. The cap is CPU rather than bandwidth: the sweep-line overlay is quadratic on self-intersecting input, where 2000 positions is already about 350 ms and 10000 is 16 seconds. - bodyBytes: at most 1048576 bytes. 1 MiB request body, refused on Content-Length before the body is read and again on the body itself. Split the request and post it in parts. Requests are validated before they are priced. A request that is over the size cap, is not parseable JSON, declares a Content-Type that is not JSON, is missing a field this schema marks required is refused with a 400 carrying a machine code, and no 402 is issued at all: nothing is quoted, nothing is signed, nothing settles. Anything that needs the geometry read is checked after payment, and a 400 from that stage cancels settlement rather than completing it, so it still costs you a round trip but not the price. Satisfy the schema before you send: that is what it is published for.
Return the area two or more polygons have in common.
$10000POST https://api.geoprimitives.dev/v1/geometry/intersectClip polygons to their common area: how much of a parcel falls inside a flood zone, where two service areas overlap, what a boundary and a study area share. Pair it with POST /v1/geometry/area to turn the overlap into square metres. Disjoint input answers geojson:null with empty:true, which is a real answer and not an error, so check empty rather than treating null as a failure. Send a FeatureCollection of Polygons or MultiPolygons; the answer is the common area as a single Feature. Clipping is planar in longitude/latitude, so edges are straight in degrees rather than geodesics. The 2000-position cap is CPU rather than bandwidth: the sweep-line overlay is quadratic on self-intersecting input. Input is EPSG:4326 only. Limits: 2000 positions across all members, 1 MiB request body, and at least two polygonal geometries or the call is refused. Price: $0.01 per call (0.01 USD, 10000 atomic units of USDC at 6 decimals), settled on eip155:8453 via the x402 protocol. There is no account, no API key and no subscription; the payment travels with the request. Limits the handler enforces. Every one of these is a 400, and see the note below about when a 400 is refused free: - positions: at most 2000 positions. Counted across every member of the collection. The cap is CPU rather than bandwidth: the sweep-line overlay is quadratic on self-intersecting input, where 2000 positions is already about 350 ms and 10000 is 16 seconds. - bodyBytes: at most 1048576 bytes. 1 MiB request body, refused on Content-Length before the body is read and again on the body itself. Split the request and post it in parts. Requests are validated before they are priced. A request that is over the size cap, is not parseable JSON, declares a Content-Type that is not JSON, is missing a field this schema marks required is refused with a 400 carrying a machine code, and no 402 is issued at all: nothing is quoted, nothing is signed, nothing settles. Anything that needs the geometry read is checked after payment, and a 400 from that stage cancels settlement rather than completing it, so it still costs you a round trip but not the price. Satisfy the schema before you send: that is what it is published for.
Subtract later polygons from the first one. Order matters.
$10000POST https://api.geoprimitives.dev/v1/geometry/differenceCut holes out of a polygon: the buildable part of a parcel once setbacks are removed, the land outside a protected area, the part of a route not already covered. Order is significant, so this is the one overlay op where swapping the operands gives a different answer. Subtracting everything answers geojson:null with empty:true, a real answer rather than an error. Send a FeatureCollection of Polygons or MultiPolygons; the first member in document order has every later member subtracted from it. The answer is a single Feature, or null with empty:true when nothing survives. Clipping is planar in longitude/latitude, so edges are straight in degrees rather than geodesics. The 2000-position cap is CPU rather than bandwidth: the sweep-line overlay is quadratic on self-intersecting input. Input is EPSG:4326 only. Limits: 2000 positions across all members, 1 MiB request body, and at least two polygonal geometries or the call is refused. Price: $0.01 per call (0.01 USD, 10000 atomic units of USDC at 6 decimals), settled on eip155:8453 via the x402 protocol. There is no account, no API key and no subscription; the payment travels with the request. Limits the handler enforces. Every one of these is a 400, and see the note below about when a 400 is refused free: - positions: at most 2000 positions. Counted across every member of the collection. The cap is CPU rather than bandwidth: the sweep-line overlay is quadratic on self-intersecting input, where 2000 positions is already about 350 ms and 10000 is 16 seconds. - bodyBytes: at most 1048576 bytes. 1 MiB request body, refused on Content-Length before the body is read and again on the body itself. Split the request and post it in parts. Requests are validated before they are priced. A request that is over the size cap, is not parseable JSON, declares a Content-Type that is not JSON, is missing a field this schema marks required is refused with a 400 carrying a machine code, and no 402 is issued at all: nothing is quoted, nothing is signed, nothing settles. Anything that needs the geometry read is checked after payment, and a 400 from that stage cancels settlement rather than completing it, so it still costs you a round trip but not the price. Satisfy the schema before you send: that is what it is published for.
Ellipsoidal area of a polygon in square metres, holes subtracted.
$10000POST https://api.geoprimitives.dev/v1/geometry/areaMeasure how much ground a polygon covers, in square metres and square kilometres, with holes subtracted and every part of a MultiPolygon summed. Use it after POST /v1/geometry/intersect to size an overlap, or on a boundary you already hold. Points and lines contribute zero rather than an error, so a mixed collection is safe to send. Not turf's area, which applies the spherical-excess formula to geodetic latitudes on a 6371008.8 m sphere and comes out 0.45% high at the equator and 0.85% low at 80 deg latitude. This maps latitude to authalic latitude on the WGS 84 authalic sphere first, landing within 5e-3% of GeographicLib's geodesic area on parcel-scale rings and converging to 1e-6% as ring edges are densified. Ring edges are treated as straight in longitude/latitude, per RFC 7946 and turf; a geodesic-edge area differs on long slanted edges, by 1.9% on a 1.7-degree triangle. Winding order is ignored: ring 0 is the exterior and the rest are holes. Input is EPSG:4326 only. Limits: 100000 positions across the whole document, 1 MiB request body. Price: $0.01 per call (0.01 USD, 10000 atomic units of USDC at 6 decimals), settled on eip155:8453 via the x402 protocol. There is no account, no API key and no subscription; the payment travels with the request. Limits the handler enforces. Every one of these is a 400, and see the note below about when a 400 is refused free: - positions: at most 100000 positions. Counted across the whole document. The measurement is linear, so the 1 MiB body cap binds first in any realistic request; this is the backstop against a body of repeated [0,0]. - bodyBytes: at most 1048576 bytes. 1 MiB request body, refused on Content-Length before the body is read and again on the body itself. Split the request and post it in parts. Requests are validated before they are priced. A request that is over the size cap, is not parseable JSON, declares a Content-Type that is not JSON, is missing a field this schema marks required is refused with a 400 carrying a machine code, and no 402 is issued at all: nothing is quoted, nothing is signed, nothing settles. Anything that needs the geometry read is checked after payment, and a 400 from that stage cancels settlement rather than completing it, so it still costs you a round trip but not the price. Satisfy the schema before you send: that is what it is published for.
True area centroid of a polygon, weighted correctly at high latitude.
$10000POST https://api.geoprimitives.dev/v1/geometry/centroidFind the area centroid of a polygon: a representative point for labelling, joining, or reverse geocoding a shape you only have a boundary for. It is the centre of mass of the area with holes subtracted, not the average of the vertices, so densely sampled edges do not drag it. Know what a centroid is not: on a concave or ring-shaped polygon the centre of mass can fall outside the polygon itself, and this endpoint returns it anyway rather than nudging the point inside. Test it with POST /v1/pip if you need a point that is guaranteed to be inside. Not turf's centroid, which is the arithmetic mean of the vertices and drifts towards whichever part of the boundary happens to be densely sampled. The moment integral is evaluated in a Lambert cylindrical equal-area projection of the WGS 84 authalic sphere, so the cos(latitude) area weighting is right at high latitude, then inverted back to longitude and latitude. Non-areal input, and polygons that enclose zero area, fall back to the vertex mean and say so in the method field. Input is EPSG:4326 only. Limits: 100000 positions across the whole document, 1 MiB request body. Price: $0.01 per call (0.01 USD, 10000 atomic units of USDC at 6 decimals), settled on eip155:8453 via the x402 protocol. There is no account, no API key and no subscription; the payment travels with the request. Limits the handler enforces. Every one of these is a 400, and see the note below about when a 400 is refused free: - positions: at most 100000 positions. Counted across the whole document. The moment integral is linear, so the 1 MiB body cap binds first in any realistic request. - bodyBytes: at most 1048576 bytes. 1 MiB request body, refused on Content-Length before the body is read and again on the body itself. Split the request and post it in parts. Requests are validated before they are priced. A request that is over the size cap, is not parseable JSON, declares a Content-Type that is not JSON, is missing a field this schema marks required is refused with a 400 carrying a machine code, and no 402 is issued at all: nothing is quoted, nothing is signed, nothing settles. Anything that needs the geometry read is checked after payment, and a 400 from that stage cancels settlement rather than completing it, so it still costs you a round trip but not the price. Satisfy the schema before you send: that is what it is published for.
Extent of any geometry as [minLon, minLat, maxLon, maxLat].
$10000POST https://api.geoprimitives.dev/v1/geometry/bboxGet the extent of a geometry or a whole collection: a map viewport, a tile query window, a cheap prefilter before you pay for a containment test. Takes any geometry type, not just polygons. Bounding box of any geometry as [minLongitude, minLatitude, maxLongitude, maxLatitude]. Naive minimum and maximum per axis, so a geometry spanning the antimeridian reports a full-width longitude range rather than the narrow band it occupies; the response flags that case instead of guessing which side you meant. Input is EPSG:4326 only. Limits: 100000 positions across the whole document, 1 MiB request body. Price: $0.01 per call (0.01 USD, 10000 atomic units of USDC at 6 decimals), settled on eip155:8453 via the x402 protocol. There is no account, no API key and no subscription; the payment travels with the request. Limits the handler enforces. Every one of these is a 400, and see the note below about when a 400 is refused free: - positions: at most 100000 positions. Counted across the whole document. A min/max walk is linear, so the 1 MiB body cap binds first in any realistic request. - bodyBytes: at most 1048576 bytes. 1 MiB request body, refused on Content-Length before the body is read and again on the body itself. Split the request and post it in parts. Requests are validated before they are priced. A request that is over the size cap, is not parseable JSON, declares a Content-Type that is not JSON, is missing a field this schema marks required is refused with a 400 carrying a machine code, and no 402 is issued at all: nothing is quoted, nothing is signed, nothing settles. Anything that needs the geometry read is checked after payment, and a 400 from that stage cancels settlement rather than completing it, so it still costs you a round trip but not the price. Satisfy the schema before you send: that is what it is published for.
Index positions to their containing H3 cell, at one or more resolutions.
$5000POST https://api.geoprimitives.dev/v1/h3/indexIndex points to H3 cells. Send "points" as [longitude, latitude] positions in EPSG:4326 and get each one's containing H3 cell at one or several resolutions, optionally with the cell's centre and its closed boundary ring. Use it to bucket points into a fixed hexagonal grid so datasets that share no keys can be joined, aggregated or deduplicated on cell id. This route indexes positions and nothing else. It does not accept polygons: a body carrying "geojson" is refused rather than served, and because "points" is required here that refusal costs you nothing -- it happens before a price is quoted. To cover a Polygon or MultiPolygon with the cells that tile it, POST /v1/h3/polyfill instead. That is a separate route at a higher price because it is a materially more expensive job: measured on this runtime, indexing one point takes about 0.003 ms and returns 168 bytes, while the cheapest possible polyfill takes about 1.5 ms -- the cell count has to be estimated before any cell is generated -- and a polyfill at its cell cap is about 200 ms and 1.8 MB. This route is priced with POST /v1/transform, which does the same class of per-position work, and below POST /v1/h3/polyfill. Every call needs exactly one of "resolution" or "resolutions". Input is EPSG:4326, longitude then latitude; out-of-range values are refused rather than normalised, because H3 will happily index a latitude of 950 and answer with a real, wrong cell. Limits: 10000 points, 4 resolutions per call, and a 1 MiB request body. Price: $0.005 per call (0.005 USD, 5000 atomic units of USDC at 6 decimals), settled on eip155:8453 via the x402 protocol. There is no account, no API key and no subscription; the payment travels with the request. Limits the handler enforces. Every one of these is a 400, and see the note below about when a 400 is refused free: - points: at most 10000 positions. 100000 calls to latLngToCell is 72 ms, so this is bandwidth rather than CPU: 10000 points at four resolutions is a 2.1 MB response, and 15.8 MB with includeBoundary set. - resolutions: at most 4 resolutions. Per call, distinct. Every extra resolution multiplies the response. - bodyBytes: at most 1048576 bytes. 1 MiB request body, refused on Content-Length before the body is read and again on the body itself. Split the request and post it in parts. Requests are validated before they are priced. A request that is over the size cap, is not parseable JSON, declares a Content-Type that is not JSON, is missing a field this schema marks required is refused with a 400 carrying a machine code, and no 402 is issued at all: nothing is quoted, nothing is signed, nothing settles. Anything that needs the geometry read is checked after payment, and a 400 from that stage cancels settlement rather than completing it, so it still costs you a round trip but not the price. Satisfy the schema before you send: that is what it is published for.
Cover a polygon with the set of H3 cells that tile it.
$10000POST https://api.geoprimitives.dev/v1/h3/polyfillPolyfill: turn a polygon into the set of H3 cells that cover it, so a spatial question becomes a set-membership question you can answer with a join. Send "geojson" as a Polygon, a MultiPolygon, or a Feature or FeatureCollection wrapping them, in EPSG:4326, plus exactly one of "resolution" or "resolutions". Choose the containment rule with "containment": "center" returns cells whose centre is inside the polygon, which is H3's default and under-covers at the edges; "overlapping" returns every cell the polygon touches, so the set fully covers it; "full" returns only cells entirely inside. Holes are respected, a MultiPolygon is treated as one polygon with its parts unioned and deduplicated, and feature ids are echoed so rows join back to your records. This route covers polygons and nothing else: a body carrying "points" is refused rather than served, and because "geojson" is required here that refusal happens before a price is quoted and costs you nothing. To index individual positions to their containing cell, POST /v1/h3/index instead, which is cheaper because it is a much smaller job. What you are paying for here, measured on this runtime: cell count grows about sevenfold per resolution step, so the cost has to be estimated before any cell is generated, which puts a floor of about 1.5 ms on even a city-block polyfill against about 0.003 ms for a one-point index call, and a polyfill at the 100000-cell cap is about 200 ms and 1.8 MB. Anything estimated above that cap is refused with the finest resolution that would have fitted, before a cell is generated; set "estimateOnly" to size a job for the price of the call and nothing else. Input is EPSG:4326, longitude then latitude, and non-areal geometry is refused by name. Limits: 500 polygons, 10000 polygon positions, 4 resolutions per call, 100000 cells per call, and a 1 MiB request body. Price: $0.01 per call (0.01 USD, 10000 atomic units of USDC at 6 decimals), settled on eip155:8453 via the x402 protocol. There is no account, no API key and no subscription; the payment travels with the request. Limits the handler enforces. Every one of these is a 400, and see the note below about when a 400 is refused free: - resolutions: at most 4 resolutions. Per call, distinct. All of them are counted against the one cell cap, so asking for four at once buys a quarter of the budget each. - polygons: at most 500 polygons. A MultiPolygon counts as one polygon. - polygonPositions: at most 10000 positions. Counted across every ring of every polygon. - polyfillCells: at most 100000 cells. Estimated before a single cell is generated, and refused above this. About 200 ms and 1.8 MB at the cap, measured on this runtime. Cell count grows about sevenfold per resolution step, so one step too deep is the difference between a cheap call and an impossible one; set estimateOnly to size a job for the price of the call and nothing else. - bodyBytes: at most 1048576 bytes. 1 MiB request body, refused on Content-Length before the body is read and again on the body itself. Split the request and post it in parts. Requests are validated before they are priced. A request that is over the size cap, is not parseable JSON, declares a Content-Type that is not JSON, is missing a field this schema marks required is refused with a 400 carrying a machine code, and no 402 is issued at all: nothing is quoted, nothing is signed, nothing settles. Anything that needs the geometry read is checked after payment, and a 400 from that stage cancels settlement rather than completing it, so it still costs you a round trip but not the price. Satisfy the schema before you send: that is what it is published for.
Test many points against many polygons and get the full matrix.
$10000POST https://api.geoprimitives.dev/v1/pipAnswer which of your points fall inside which of your polygons, in one call. Use it to tag events with the district, catchment or territory they belong to, to filter a point set down to a study area, or to join two datasets that share nothing but geography. The answer comes back twice, as a dense 0/1 matrix and as a per-point list of polygon indices, so neither shape needs rebuilding on your side, and polygon ids are echoed as column labels so the matrix joins straight back to your records. Rings must be closed and coordinates must be EPSG:4326: run POST /v1/geojson/repair first if they are not, and POST /v1/transform if they are not in degrees. Costs scale with polygon detail, not just polygon count, so POST /v1/geometry/simplify on the polygons is often the cheapest way under the caps. The matrix is dense: every point is tested against every polygon, one row per point and one column per polygon, 1 for inside and 0 for outside. A point exactly on an edge or vertex counts as inside unless "ignoreBoundary" is set. Two caps, both on products rather than on either side: at most 250000 point-polygon pairs, and at most 25000000 point-vertex tests, since a ray cast walks every edge. Input is EPSG:4326, longitude then latitude. Limits: 250000 points, 2000 polygons, 100000 polygon positions, and a 1 MiB request body. Price: $0.01 per call (0.01 USD, 10000 atomic units of USDC at 6 decimals), settled on eip155:8453 via the x402 protocol. There is no account, no API key and no subscription; the payment travels with the request. Limits the handler enforces. Every one of these is a 400, and see the note below about when a 400 is refused free: - points: at most 250000 positions. Rejected while points is being read rather than after. Set where the 1 MiB body cap binds anyway: 250000 positions of [0,0] is about 1.5 MB. - polygons: at most 2000 polygons. A MultiPolygon is one polygon, and one column of the matrix. - polygonPositions: at most 100000 positions. Counted across every ring of every polygon. A backstop under the work cap. - pointPolygonPairs: at most 250000 pairs. points x polygons, checked before any test runs. The cap is on the product, not on either side, so split whichever side is easier to batch. - pointVertexTests: at most 25000000 tests. points x total polygon vertices. A ray cast walks every edge, so cost is points times vertices and not points times polygons: one 10000-vertex polygon against 250000 points is 2.5 billion edge tests, which a per-side cap would wave straight through. Simplify the polygons or send fewer points. - bodyBytes: at most 1048576 bytes. 1 MiB request body, refused on Content-Length before the body is read and again on the body itself. Split the request and post it in parts. Requests are validated before they are priced. A request that is over the size cap, is not parseable JSON, declares a Content-Type that is not JSON, is missing a field this schema marks required is refused with a 400 carrying a machine code, and no 402 is issued at all: nothing is quoted, nothing is signed, nothing settles. Anything that needs the geometry read is checked after payment, and a 400 from that stage cancels settlement rather than completing it, so it still costs you a round trip but not the price. Satisfy the schema before you send: that is what it is published for.
IANA timezone, current UTC offset and DST flag for a coordinate.
$5000GET https://api.geoprimitives.dev/v1/timezoneGive it a longitude and latitude and it answers with the IANA timezone identifier (America/New_York), the UTC offset in force there right now, whether daylight saving is currently in effect and by how much, the local wall-clock time, and the zone's standard offset. Pass an optional ISO 8601 `at` instant to ask about any moment between 1900 and 2100 instead of now, which is what you want when scheduling something months out across a DST boundary. Parameters go in the query string: this is a GET, there is no body. Two halves, with different vintages, and the difference matters. The offset and the DST rules come from the runtime's own timezone database and are current: a rule change reaches this endpoint when Cloudflare updates ICU, with no redeploy here. The *boundaries* come from tz_world (efele.net), CC0 1.0 and derived from VMAP0, a public-domain US government product. That map was frozen at tzdb 2016d in May 2016, and this catalog sells public-domain and permissively-licensed data only, which is why it is used rather than the current OpenStreetMap-derived alternative. What the vintage costs, measured against IANA's own coordinates for all 312 zones in zone1970.tab rather than estimated: 293 agree. Of the 19 that do not, 7 are Antarctic research stations, which this map does not cover at all and which fall back to the open-water rule; 7 more return a different identifier for the identical offset and DST all year (Australia/Brisbane for Australia/Lindeman, America/Regina for America/Swift_Current, and five like them), so the time is right and only the name is old. That leaves four regions where the answer is up to one hour wrong because the zone was split after 2016: Chilean Magallanes and Aysen (answered as America/Santiago rather than America/Punta_Arenas or America/Coyhaique), the Mexican border municipalities around Ciudad Juarez, Saratov Oblast in Russia, and the weeks each spring when Asia/Hebron and Asia/Jerusalem change on different dates. If you query inside one of those, do not trust this endpoint. Boundary precision is a separate question from vintage. The map is indexed as compacted H3 cells at resolution 6, whose half-width is about 1.6 km, chosen to match VMAP0's own stated positional accuracy of roughly 2 km rather than to flatter the index. So a point within a couple of kilometres of a zone border may be attributed to the neighbour, and no amount of the source's precision would fix that because the source does not have it. Away from a border the answer is exact. Every answer says where it came from under `source`. `boundary` means the point is inside a mapped timezone polygon and is the answer you are paying for. `coastal` means it is over water or on land this map does not cover, and the identifier is the nearest zone within about 60 km -- correct for a harbour or a coastal city whose coordinate falls just offshore, and not a boundary result. `nautical` means open water more than about 60 km from mapped land, answered with the tz database's own rule for international waters, meridians 15 degrees apart; it is not the time kept aboard any vessel and it ignores territorial waters. Longitude and latitude are checked against EPSG:4326 bounds before anything else touches them, because H3 silently normalises an out-of-range latitude into a real but wrong cell rather than refusing it. Reproject with POST /v1/transform first if you are not already in WGS 84. The DST flag is decided on offsets rather than on the tz database's own isdst bit, so Europe/Dublin reads as daylight saving in summer like everywhere else, and it is measured over a window either side of the instant so that a zone which changes its standard offset mid-year -- Asia/Almaty in 2024 -- is not mistaken for one on daylight saving. Price: $0.005 per call (0.005 USD, 5000 atomic units of USDC at 6 decimals), settled on eip155:8453 via the x402 protocol. There is no account, no API key and no subscription; the payment travels with the request. Limits the handler enforces. Every one of these is a 400, and see the note below about when a 400 is refused free: - latitude: at most 90 degrees. Absolute value. Outside EPSG:4326 bounds the request is refused rather than normalised, because H3 answers an out-of-range latitude with a real but wrong cell. - longitude: at most 180 degrees. Absolute value. Reproject with POST /v1/transform if you are not in WGS 84. - year: at most 2100 year. Latest year the optional `at` parameter may name; the earliest is 1900. Outside that the daylight-saving rules are guesswork rather than data. Requests are validated before they are priced. A request that is missing a query parameter this schema marks required is refused with a 400 carrying a machine code, and no 402 is issued at all: nothing is quoted, nothing is signed, nothing settles. Anything that needs the geometry read is checked after payment, and a 400 from that stage cancels settlement rather than completing it, so it still costs you a round trip but not the price. Satisfy the schema before you send: that is what it is published for.
US Census FIPS codes for a coordinate: state, county and congressional district.
$10000GET https://api.geoprimitives.dev/v1/us/geographiesGive it a longitude and latitude inside the United States and it answers with the Census FIPS codes for that point: the 2-digit state code, the 5-digit county GEOID, and the GEOID of the congressional district. Parameters go in the query string: this is a GET, there is no body. The codes are the product and the names are a courtesy. A county GEOID is the join key to essentially every geography-keyed federal dataset -- the American Community Survey, BLS Local Area Unemployment Statistics, BEA regional accounts, CDC WONDER, HUD, USDA, FEMA -- and the state code is its first two digits, so one call gives you both keys already in the form those tables expect. County GEOID is also the first five digits of a tract GEOID (11 digits) and of a block group GEOID (12), so it is the prefix you would filter a tract-level extract down with. COVERAGE, stated up front because the shortfall is the point: this endpoint answers state, county and congressional district, and it does NOT answer tract, block group, place or ZCTA. That is a size limit, not an oversight, and it was measured rather than guessed. A national compacted H3 index of tracts is 3196 KiB gzipped and of block groups 5335 KiB, at a resolution already too coarse for either; the whole Worker has 3 MiB compressed to spend and most of that was gone before this route existed. Those four tiers need an object store behind the Worker and will arrive with one. Every response repeats this under `coverage`, so an agent never has to infer it from a missing field. In the meantime geocoding.geo.census.gov answers all seven tiers, free, authoritative, at the cost of a round trip and a dependency. VINTAGE: TIGER/Line 2025, congressional districts for the 119th Congress. It is on every response under `vintage` and it matters more here than almost anywhere else in this catalog, because TIGER is re-cut annually and districts move with redistricting, so a stale district code is a wrong answer a caller cannot detect from the outside. Nothing here self-updates; a new vintage means a rebuild. The data is a work of the United States government and is in the public domain (17 U.S.C. 105) -- no attribution clause and no redistribution terms. ACCURACY, and this is the part to read before you trust a code. The boundaries themselves are full-resolution TIGER, the same geometry the Census Geocoder answers from, not a simplified copy. What is approximate is the lookup: the polygons are indexed as compacted H3 cells at resolution 7, about 1.22 km across, so a point within roughly 610 m of a boundary may be attributed to the neighbour. County equivalents and districts too small to describe at that resolution -- Falls Church city, the other Virginia independent cities, the congressional districts inside New York City -- carry a second index at resolution 9, about 87 m, and `boundaryAccuracyMetres` says which one answered you. Because a 610 m index is only useful if you know when not to trust it, every response carries `boundaryUncertain.county` and `boundaryUncertain.congressionalDistrict`. They are true when a neighbouring cell resolves to a different code, which means the boundary is close enough that the index cannot settle which side you are on. Measured against geocoding.geo.census.gov on 650 coordinates: on 400 population-weighted points the county code was right 389 times and the district code 383, and every one of the 11 and 17 disagreements was flagged; on 250 points drawn deliberately 100 m to 1500 m from a county line the county code was right 183 times, and every one of the 67 wrong county codes and 25 wrong district codes was flagged. Across all 650 it never returned a wrong code with the flag clear. It is generous in the other direction -- it flagged 74 of the 400 correct-heavy sample -- so treat `true` as "verify this one if it matters" rather than as "this is wrong". Outside the United States the answer is `found: false` with null codes and a paid 200, because "this coordinate is not in the US" is the answer you asked for. County polygons include their legal water area out to the three-mile limit and across the Great Lakes, so harbours, piers and lake coordinates resolve normally and only genuinely foreign or high-seas points fall out. Coverage is the 50 states, DC, Puerto Rico, American Samoa, Guam, the Northern Mariana Islands and the US Virgin Islands. A small amount of water is inside a county but inside no congressional district, which TIGER publishes as a real polygon; that answers with a district whose number is null rather than with a guess. Longitude and latitude are checked against EPSG:4326 bounds before anything else touches them, because H3 silently normalises an out-of-range latitude into a real but wrong cell rather than refusing it. Reproject with POST /v1/transform first if you are not already in WGS 84. TIGER publishes in NAD83, which this reads as WGS 84; the two differ by under two metres, which is a fortieth of the index's own resolution. Price: $0.01 per call (0.01 USD, 10000 atomic units of USDC at 6 decimals), settled on eip155:8453 via the x402 protocol. There is no account, no API key and no subscription; the payment travels with the request. Limits the handler enforces. Every one of these is a 400, and see the note below about when a 400 is refused free: - latitude: at most 90 degrees. Absolute value. Outside EPSG:4326 bounds the request is refused rather than normalised, because H3 answers an out-of-range latitude with a real but wrong cell. - longitude: at most 180 degrees. Absolute value. Reproject with POST /v1/transform if you are not in WGS 84. - boundaryAccuracyMetres: at most 610 metres. Half-width of one resolution 7 H3 cell, and so how close to a boundary an answer stops being reliable. Falls to 87 m where a refined index answered. `boundaryUncertain` tells you per response which side of that you are on. - answeredTiers: at most 3 tiers. State, county and congressional district. Tract, block group, place and ZCTA are not answered at any price here; see `coverage` on every response for why. Requests are validated before they are priced. A request that is missing a query parameter this schema marks required is refused with a 400 carrying a machine code, and no 402 is issued at all: nothing is quoted, nothing is signed, nothing settles. Anything that needs the geometry read is checked after payment, and a 400 from that stage cancels settlement rather than completing it, so it still costs you a round trip but not the price. Satisfy the schema before you send: that is what it is published for.
Ground elevation in metres above NAVD88 at a coordinate, from the USGS 3DEP DEM. Alaska.
$5000GET https://api.geoprimitives.dev/v1/elevationGive it a latitude and longitude and it answers with the ground elevation there in metres above NAVD88, read from the USGS 3D Elevation Program seamless DEM. Parameters go in the query string: this is a GET, there is no body. COVERAGE, stated up front because it is the first thing to check: the state of Alaska, whole, and nothing else yet. The coverage gate reads the coordinate, so it runs in the handler and after payment, not in front of the 402: a coordinate outside Alaska is quoted like any other and then refused with outside_coverage. A refusal does not settle, so nothing is charged, but it costs a signature and a second round trip to find out. Check the coverage against this description or the free catalog first. The conterminous United States and Hawaii are the next extension and arrive through the same pipeline with no change to this interface; every response repeats the coverage under `coverage` so an agent never infers it from an error. Canada is not served even where USGS publishes pixels over it: those come from Natural Resources Canada under a licence with an attribution clause, and this catalog carries public-domain data only, so they were not stored. SOURCE AND VINTAGE: USGS 3DEP seamless 2 arc-second product, the resolution USGS publishes for Alaska, a work of the United States government in the public domain (17 U.S.C. 105). A 2 arc-second pixel is about 62 m north to south and 20 to 30 m east to west at Alaskan latitudes; the response states the pixel size at your latitude. USGS republishes blocks as new lidar and IFSAR arrive, and the response carries the publication date of the block that answered you. Heights are orthometric, NAVD88, metres; horizontal positions are NAD83, which this endpoint reads as WGS 84 since the two differ by under two metres, a fraction of a pixel. INTERPOLATION: bilinear over the four pixel centres around the point by default, which is what GDAL and PROJ do for a continuous surface. Pass interpolation=nearest for the raster's own value at the pixel the point falls in, which is bit-identical to gdallocationinfo on the USGS file and the right choice when you want the source unblended. Checked against GDAL rather than against itself: of 200 coordinates drawn over the built blocks, the 150 inside the coverage agreed with `gdallocationinfo -r bilinear` on the USGS GeoTIFFs to 6.2e-4 m at worst, and interpolation=nearest agreed with plain `gdallocationinfo` to the millimetre this endpoint rounds to. The response always names the method used. VOIDS AND WATER: gaps in the USGS footprint and the Canadian side of the border are void, and so is open water beyond the three-mile county limit. A void pixel inside a bilinear window is dropped and the remaining weights renormalised, as gdalwarp treats nodata, and the response says how many of the four were used. A window that is entirely void is refused with no_data and not charged, never answered with zero. Inshore water is a different case and you should know it: USGS writes 0 over the sea in some blocks and nodata in others, so a coordinate in a harbour or a bay inside the county limit may answer 0 m, which is the source's value and not a measurement of anything. ACCURACY of the data itself is USGS's, not this endpoint's: the Alaska seamless layer is built from IFSAR at 5 m resampled to 2 arc-seconds, and USGS states its vertical accuracy per source project rather than for the seamless layer as a whole. Expect metre-level agreement with a survey on open terrain and worse under forest and on steep slopes. This endpoint adds nothing to that error beyond interpolation. Longitude and latitude are checked against EPSG:4326 bounds first. Reproject with POST /v1/transform if you are not already in WGS 84 or NAD83. For a line rather than a point use POST /v1/elevation/profile, which samples along a polyline in one call. Price: $0.005 per call (0.005 USD, 5000 atomic units of USDC at 6 decimals), settled on eip155:8453 via the x402 protocol. There is no account, no API key and no subscription; the payment travels with the request. Limits the handler enforces. Every one of these is a 400, and see the note below about when a 400 is refused free: - latitude: at most 90 degrees. Absolute value. Outside EPSG:4326 bounds the request is refused. - longitude: at most 180 degrees. Absolute value. Reproject with POST /v1/transform if you are not in WGS 84 or NAD83. - coverageBlocks: at most 439 blocks. 1-degree blocks in the store, all in Alaska. A coordinate outside them is refused with outside_coverage, which does not settle. See `coverage` on every response. - pixelArcSeconds: at most 2 arc-seconds. Source resolution: about 62 m north-south and 20 to 30 m east-west in Alaska. Nothing finer is answered; the response states the pixel size at your latitude. Requests are validated before they are priced. A request that is missing a query parameter this schema marks required is refused with a 400 carrying a machine code, and no 402 is issued at all: nothing is quoted, nothing is signed, nothing settles. Anything that needs the geometry read is checked after payment, and a 400 from that stage cancels settlement rather than completing it, so it still costs you a round trip but not the price. Satisfy the schema before you send: that is what it is published for.
Elevation profile along a polyline: equally spaced samples, gain, loss, min and max. Alaska.
$10000POST https://api.geoprimitives.dev/v1/elevation/profileSend a polyline as an array of [longitude, latitude] positions and it answers with elevations sampled at equal geodesic spacing along it, from the USGS 3D Elevation Program seamless DEM, plus the cumulative gain and loss and the minimum and maximum. One call for a whole route rather than one call per point: 2000 samples cost what two cost. COVERAGE is the state of Alaska, whole, and nothing else yet; see GET /v1/elevation for the full statement, which this route shares and repeats under `coverage` on every response. Every sample is checked against the coverage before a tile is read, and a profile with any sample outside Alaska is refused with outside_coverage naming the sample. That check reads coordinates, so it runs after payment like the rest of the handler; the refusal does not settle and nothing is charged, but it costs a signature and a second round trip. Canada is not served even where USGS publishes pixels over it, for the licensing reason stated there. SAMPLING: samples are placed at equal geodesic distance (GRS 80) along the polyline, each on the great circle of its segment, the first at the first position and the last at the last. Omit `samples` to sample every 60 m, the native pixel height, up to 2000; pass it to choose the count. Each sample is bilinear over the four pixel centres around it by default, or `nearest` for the raster's own values; the response names the method. The source is the 2 arc-second product USGS publishes for Alaska, about 62 m north to south, so asking for spacing much under 60 m buys interpolation rather than information. VOIDS inside the line: gaps in the USGS footprint, the Canadian side of the border and water beyond the county limit are void. A sample whose window is entirely void is null in the list and counted in `stats.voidSamples`; gain and loss are summed over consecutive non-null samples so a crossing of void water is not a descent to zero and back. Inshore water is 0 in some USGS blocks rather than void, so a line across a bay may read 0 m there, which is the source's value; see GET /v1/elevation. A profile that is void throughout is refused with no_data and not charged. SOURCE, VINTAGE, DATUMS AND ACCURACY are those of GET /v1/elevation: USGS 3DEP, public domain, NAVD88 metres, NAD83 horizontal read as WGS 84, IFSAR-derived in Alaska with metre-level vertical accuracy on open ground. Gain and loss over a 2 arc-second surface are an estimate of the terrain's, not of a track's: fine structure narrower than a pixel is not in the data. Limits: 500 positions in the polyline, 2000 samples, 1000 km of length, 512 elevation tiles read per call (about 2000 km of profile at the coarsest sampling; a 100 km profile at native spacing reads about 40), and a 1 MiB body. All are checked before a tile is read. Price: $0.01 per call (0.01 USD, 10000 atomic units of USDC at 6 decimals), settled on eip155:8453 via the x402 protocol. There is no account, no API key and no subscription; the payment travels with the request. Limits the handler enforces. Every one of these is a 400, and see the note below about when a 400 is refused free: - bodyBytes: at most 1048576 bytes. 1 MiB request body, refused on Content-Length before the body is read and again on the body itself. Split the request and post it in parts. - positions: at most 500 positions. Vertices in the polyline. Simplify a dense GPS track first (POST /v1/geometry/simplify) rather than sending every fix. - samples: at most 2000 samples. Samples returned per call. The default spacing of 60 m runs into this at 120 km; longer lines are sampled more coarsely and the response says so, or split the line. - lengthMetres: at most 1000000 metres. Geodesic length of the whole polyline. Over it the request is refused with profile_too_long before a tile is read. - tiles: at most 512 tiles. Distinct 64x64 pixel tiles one call may read, counted before any read. A 100 km profile at native spacing needs about 40; only a long line at dense sampling approaches it. - coverageBlocks: at most 439 blocks. 1-degree blocks in the store, all in Alaska. A sample outside them refuses the whole profile with outside_coverage, which does not settle. - pixelArcSeconds: at most 2 arc-seconds. Source resolution. Sampling finer than about 60 m returns interpolated values, not new information. Requests are validated before they are priced. A request that is over the size cap, is not parseable JSON, declares a Content-Type that is not JSON, is missing a field this schema marks required is refused with a 400 carrying a machine code, and no 402 is issued at all: nothing is quoted, nothing is signed, nothing settles. Anything that needs the geometry read is checked after payment, and a 400 from that stage cancels settlement rather than completing it, so it still costs you a round trip but not the price. Satisfy the schema before you send: that is what it is published for.
Charted depth, nearest sounding and survey quality at a coordinate, from NOAA ENC charts. Southeast and southcentral Alaska.
$5000GET https://api.geoprimitives.dev/v1/marine/depthGive it a latitude and longitude and it answers with what the nautical chart says about the water there: the depth range of the charted depth area containing the position, the nearest charted sounding and how far away it is, and the chart's own zone of confidence for that area. Parameters go in the query string: this is a GET, there is no body. NOT FOR NAVIGATION, stated first because it is a condition of using the source. This is derived from NOAA Electronic Navigational Charts and redistributed; it is not an official NOAA ENC and does not meet federal chart carriage regulations under 33 CFR 164 or SOLAS chapter V, which only charts from NOAA or a certified NOAA ENC distributor do. Every response repeats this under `disclaimer`. NOAA and NOAA ENC are registered trademarks of NOAA, which does not endorse this service. COVERAGE is 65 NOAA ENC cells of usage bands 3, 4 and 5 over five working areas of southeast and southcentral Alaska: Sitka Sound; Kasaan Bay and Clarence Strait; Metlakatla, Ketchikan and Revillagigedo Channel; Juneau, Gastineau Channel and Lynn Canal; and Cordova with eastern Prince William Sound. It is not statewide, it is not the whole coast, and the small-scale cells that would make it look wider were deliberately left out. The coverage check reads the coordinate, so it runs in the handler after payment rather than in front of the 402: a position off the charts is quoted like any other and then refused with outside_coverage. A refusal does not settle and nothing is charged, but it costs a signature and a round trip, so check the coverage first. Every response repeats it under `coverage`. NOTHING IS INTERPOLATED, and that is the difference between this and GET /v1/elevation. Terrain is a continuous surface and sampling it between pixels is legitimate; a chart is a set of assertions a hydrographer made, and averaging two of them invents a depth nobody surveyed. So `depthAreaMinimum` is DRVAL1 of the charted depth area the position falls inside, the shoalest depth that area is guaranteed to hold, and `nearestSounding` is a real charted sounding with the geodesic distance to it. The two routinely disagree and are meant to: a sounding is a measurement at a point and the area is a guarantee over a region. The guarantee is the number to navigate a plan by; the sounding tells you how the area was arrived at. WHICH CHART ANSWERS: ENC cells overlap on purpose, so a position in Sitka sits on a 1:12,000 harbour cell, a 1:45,000 approach cell and a 1:180,000 coastal cell at once, and they do not agree because the smaller scales generalise. The largest-scale cell covering the position wins outright, usage band first and then compilation scale, which is the rule an ECDIS follows. The response names the cell, its band, its scale, its edition and its NOAA issue date, and lists any cell it consulted first that charted no depth area there. SURVEY QUALITY is on every answer as the chart's CATZOC zone of confidence, with the position and depth accuracy that zone implies, and the survey start and end dates when the chart gives them. Read it before you trust a depth. NOAA's own warning applies to all of it: much of the depth information on NOAA charts is from surveys conducted before 1940 and the shoreline is more than 20 years old. Zone A1 is a modern full-coverage survey; zone D or U means the chart is telling you it does not know. DATUMS: depths are metres below Mean Lower Low Water, which is the US chart datum, so a real depth at a real moment is this number plus the tide. Heights on the same charts are above Mean High Water. Positions are WGS 84. Reproject with POST /v1/transform if you are in State Plane or NAD27; the datum shift matters here, since a NAD27 Alaskan position is over a hundred metres from its WGS 84 one. VINTAGE: the charts are a snapshot taken on 2026-05-02. NOAA reissues ENCs weekly for Notice to Mariners and this store does not follow them, so a cell here can be behind NOAA's current edition. The cell edition, update number and issue date that answered are in `source` for exactly that comparison. REFUSALS that cost nothing: a position with no charted depth area is `no_depth_area`, which means land, a drying area the chart gives no depth for, or a gap between cells, and it is refused rather than answered with null. A position off the charts is `outside_coverage`. A chart object that cannot be read is a 503, not a guess. For what is in the way rather than how deep it is, use GET /v1/marine/hazards, which returns charted rocks, obstructions and wrecks within a radius. Price: $0.005 per call (0.005 USD, 5000 atomic units of USDC at 6 decimals), settled on eip155:8453 via the x402 protocol. There is no account, no API key and no subscription; the payment travels with the request. Limits the handler enforces. Every one of these is a 400, and see the note below about when a 400 is refused free: - coverageCells: at most 65 cells. NOAA ENC cells in the store, all in southeast and southcentral Alaska. A position off them refuses with outside_coverage, which does not settle. - tiles: at most 100 tiles. Chart tiles one call may read, counted before any read. A depth answer normally reads one, or up to nine when the nearest sounding may lie in a neighbouring tile, so this is a backstop rather than a limit you will meet. - objectClasses: at most 7 S-57 classes. SOUNDG, DEPARE, DRGARE, UWTROC, OBSTRN, WRECKS and M_QUAL. Buoys, lights, cables, restricted areas and everything else on the chart are not in this store at all. Requests are validated before they are priced. A request that is missing a query parameter this schema marks required is refused with a 400 carrying a machine code, and no 402 is issued at all: nothing is quoted, nothing is signed, nothing settles. Anything that needs the geometry read is checked after payment, and a 400 from that stage cancels settlement rather than completing it, so it still costs you a round trip but not the price. Satisfy the schema before you send: that is what it is published for.
Charted rocks, obstructions and wrecks within a radius of a coordinate, from NOAA ENC charts. Southeast and southcentral Alaska.
$5000GET https://api.geoprimitives.dev/v1/marine/hazardsGive it a latitude, a longitude and a radius in metres and it answers with every charted underwater rock, obstruction and wreck inside that circle, nearest first, each with its charted depth, its water level effect, its name where the chart names it, and the geodesic distance from your position. Parameters go in the query string: this is a GET, there is no body. NOT FOR NAVIGATION, stated first because it is a condition of using the source. Derived from NOAA Electronic Navigational Charts and redistributed; not an official NOAA ENC, and it does not meet federal chart carriage regulations under 33 CFR 164 or SOLAS chapter V. Every response repeats this under `disclaimer`. NOAA and NOAA ENC are registered trademarks of NOAA, which does not endorse this service. COVERAGE is the same 65 cells as GET /v1/marine/depth: bands 3 to 5 over Sitka Sound; Kasaan Bay and Clarence Strait; Metlakatla, Ketchikan and Revillagigedo Channel; Juneau, Gastineau Channel and Lynn Canal; and Cordova with eastern Prince William Sound. Not statewide and not the whole coast. The check reads the coordinate, so it happens after payment; a position off the charts is refused with outside_coverage, which does not settle. Every response repeats the coverage under `coverage`. WHAT AN EMPTY ANSWER MEANS, because this is the one that can hurt: an empty list means the chart shows nothing there, not that the water is clear. Read the zone of confidence from GET /v1/marine/depth for the same position before you read anything into a clean result. Zones C, D and U mean the area was never fully searched and NOAA says so; in zone D large depth anomalies may be expected. Only three S-57 classes are searched -- UWTROC, OBSTRN and WRECKS -- so a charted danger that is a buoy, a cable, a fish haven area, a spoil ground or a restricted area is not in this store and will not appear. WHICH CHART ANSWERS, and why it is not just one: the largest-scale cell covering each part of the search wins, applied per location rather than once for the whole circle. A harbour cell ends at its own edge and the water a kilometre outside it is charted by the approach cell, so a search that fixed on one chart would drop those hazards silently. `cellsSearched` lists every cell that contributed, and each hazard names the cell it came from. Where two cells chart the same rock at a seam it is returned once. DEPTHS are metres below Mean Lower Low Water. A null depth is a hazard the chart gives no sounding for; read `waterLevel` instead, since an object that covers and uncovers, or is awash, has no single depth. `chartedAsArea` marks a wreck or obstruction the chart carries as an area rather than a point, in which case the position given is its centroid and the thing itself is larger. LIMITS: the radius runs from 1 to 5000 metres and defaults to 1000. A search may read 100 chart tiles, counted from the geometry before any read, so an over-wide request is refused for free rather than part-served; the widest legal radius needs at most 81, so the radius is the limit you meet and the tile count is a backstop. At most 200 hazards come back, nearest first, and the response says when it truncated. VINTAGE is a 2026-05-02 snapshot of charts NOAA reissues weekly; the cell edition and issue date that answered are in each hazard's cell and in `source`. For how deep the water is rather than what is in it, use GET /v1/marine/depth. Price: $0.005 per call (0.005 USD, 5000 atomic units of USDC at 6 decimals), settled on eip155:8453 via the x402 protocol. There is no account, no API key and no subscription; the payment travels with the request. Limits the handler enforces. Every one of these is a 400, and see the note below about when a 400 is refused free: - radiusMetres: at most 5000 metres. Widest search radius. Over it the call is refused before any chart is read, so it costs nothing; split a wider sweep into several calls. - hazards: at most 200 hazards. Hazards returned, nearest first. A truncated list says so in notes; narrow the radius for a complete one. - tiles: at most 100 tiles. Chart tiles one call may read, counted from the search geometry before any read. The widest legal radius needs at most 81 anywhere in the coverage, so radiusMetres is the limit you will actually meet and this one is a backstop. - coverageCells: at most 65 cells. NOAA ENC cells in the store. A position off them refuses with outside_coverage, which does not settle. - objectClasses: at most 3 S-57 classes. UWTROC, OBSTRN and WRECKS. A charted danger of any other class -- a buoy, a cable, a fish haven, a restricted area -- is not searched and will not appear. Requests are validated before they are priced. A request that is missing a query parameter this schema marks required is refused with a 400 carrying a machine code, and no 402 is issued at all: nothing is quoted, nothing is signed, nothing settles. Anything that needs the geometry read is checked after payment, and a 400 from that stage cancels settlement rather than completing it, so it still costs you a round trip but not the price. Satisfy the schema before you send: that is what it is published for.
Checks
reachable
valid
2026-10-07T15:16:13.833Z
No settlement evidence found in chain signals.
Gateway routing
Score ≥70/100 — Cleared attestation pass. Route via Gateway before pay.
Gateway traffic
Leaderboard →0
Routes to you
0
Category · 7d
0
Unique agents
—
Your win rate
No category routes this week yet. When agents arrive, Cleared ($49/mo) gets the +12 routing preference Listed doesn't. One click →
Claim this listing to upgrade to Cleared attestation.
Claim listing