Cut 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.
Endpoint
POST https://api.geoprimitives.dev/v1/geometry/difference
1calls / 30dmeasured by x402.new
1paying wallets / 30dmeasured by x402.new
—uptime / 24hmeasured by x402-list.com
—compliance grademeasured by x402-list.com
Listed in 2 directories: bazaar, x402-new. First seen 5 Sept 2026.
Call this service
The endpoint answers an unpaid request with an HTTP 402 and a payment challenge. @x402/fetch signs it, pays in USDC, and retries — you get the result back in one call.
TypeScript · @x402/fetch
import { wrapFetchWithPayment } from "@x402/fetch";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.PRIVATE_KEY);
const pay = wrapFetchWithPayment(fetch, account);
const res = await pay("https://api.geoprimitives.dev/v1/geometry/difference", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ /* see the 402 challenge for the shape */ }),
});
console.log(await res.json());
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 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
BaseEverything else
744 calls / 30d$0.005/ call
geoprimitives · US Geographies
api.geoprimitives.dev
Give 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. Noth
BaseEverything else
4 calls / 30d$0.01/ call
geoprimitives · Timezone
api.geoprimitives.dev
Give 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 insid
BaseEverything else
4 calls / 30d$0.005/ call
geoprimitives · Elevation
api.geoprimitives.dev
Give 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 rast
BaseEverything else
3 calls / 30d$0.005/ call
geoprimitives · Hello
api.geoprimitives.dev
Echo 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.
BaseSocial & community
3 calls / 30d$0.001/ call
geoprimitives · Marine Hazards
api.geoprimitives.dev
Give 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 ra
BaseEverything else
3 calls / 30d$0.005/ call
Uptime and compliance: x402-list.com (CC BY 4.0). Safety scores: x402.direct. Call and payer counts: x402.new. AMNT indexes this endpoint and does not host, operate or vouch for it.