G

geoprimitives · Marine Hazards

by geoprimitives

GETBase

$0.005

per call · USD Coin on Base

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 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.

Endpoint

GET https://api.geoprimitives.dev/v1/marine/hazards

3

Calls / 30d

1

Unique payers / 30d

Aug 21

Last called

exact

Payment scheme

Call this service

TypeScript · @x402/fetch
import { wrapFetchWithPayment } from "@x402/fetch";
import { privateKeyToAccount } from "viem/accounts";

const account = privateKeyToAccount(process.env.PRIVATE_KEY);
const fetchWithPay = wrapFetchWithPayment(fetch, account);

const res = await fetchWithPay("https://api.geoprimitives.dev/v1/marine/hazards", {
  method: "GET",
});
const data = await res.json();
cURL
curl -X GET \
  "https://api.geoprimitives.dev/v1/marine/hazards?lat=57.0405&lon=-135.3421&radiusMetres=250" \
  -H "X-PAYMENT: <signed x402 payment>"

Example response

{
  "cellsSearched": [
    "US5AK2NE"
  ],
  "classes": [
    "UWTROC",
    "OBSTRN",
    "WRECKS"
  ],
  "coverage": {
    "areas": [
      "Sitka Sound",
      "Kasaan Bay and Clarence Strait",
      "Metlakatla, Ketchikan and Revillagigedo Channel",
      "Juneau, Gastineau Channel and Lynn Canal",
      "Cordova and eastern Prince William Sound"
    ],
    "bands": [
      "coastal (band 3)",
      "approach (band 4)",
      "harbour (band 5)"
    ],
    "cells": 65,
    "classes": [
      "SOUNDG",
      "DEPARE",
      "DRGARE",
      "UWTROC",
      "OBSTRN",
      "WRECKS",
      "M_QUAL"
    ],
    "reason": "The charts are the 65 NOAA ENC cells of usage bands 3 to 5 that cover five working areas of southeast and southcentral Alaska. This is not statewide and it is not the whole coast. The coverage check reads the coordinate, so it runs in the handler and after payment, not 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, so nothing is charged, but it costs a signature and a round trip. Check the coverage against this list first.",
    "region": "Southeast and southcentral Alaska, 65 NOAA ENC cells"
  },
  "disclaimer": {
    "notForNavigation": true,
    "origin": "https://www.charts.noaa.gov/ENCs/",
    "statement": "Derived from NOAA ENC data and redistributed. This is NOT an official NOAA ENC and does NOT meet federal chart carriage regulations under 33 CFR 164 or SOLAS chapter V; only charts from NOAA or a certified NOAA ENC distributor do. Do not navigate on this answer.",
    "surveyAge": "NOAA's own warning, repeated because it governs how much any of this is worth: much of the depth information on NOAA charts comes from surveys conducted before 1940 and the shoreline is more than 20 years old. The zone of confidence in every depth answer is the chart's own assessment of that, per area.",
    "trademark": "NOAA and NOAA ENC are registered trademarks of the National Oceanic and Atmospheric Administration, which does not endorse this service.",
    "vintage": "Chart data as held on 2026-05-02. NOAA reissues ENCs weekly for Notice to Mariners and this snapshot does not track them; the cell edition and issue date that answered are in \"source\" so you can compare against NOAA's own catalog."
  },
  "hazardCount": 3,
  "hazards": [
    {
      "category": null,
      "cell": "US5AK2NE",
      "chartedAsArea": false,
      "classCode": "UWTROC",
      "className": "underwater rock",
      "depth": 4.2,
      "distanceMetres": 104.73,
      "lat": 57.0403539,
      "lon": -135.3438043,
      "name": null,
      "waterLevel": "always under water/submerged"
    },
    {
      "category": null,
      "cell": "US5AK2NE",
      "chartedAsArea": false,
      "classCode": "UWTROC",
      "className": "underwater rock",
      "depth": 1.5,
      "distanceMetres": 189.3,
      "lat": 57.0390562,
      "lon": -135.340454,
      "name": "Mitchell Rock",
      "waterLevel": "always under water/submerged"
    },
    {
      "category": null,
      "cell": "US5AK2NE",
      "chartedAsArea": false,
      "classCode": "UWTROC",
      "className": "underwater rock",
      "depth": null,
      "distanceMetres": 236.4,
      "lat": 57.0422918,
      "lon": -135.3400117,
      "name": null,
      "waterLevel": "covers and uncovers"
    }
  ],
  "notes": [
    "A hazard with a null depth is one the chart gives no sounding for (VALSOU absent). Read its waterLevel instead: an object that covers and uncovers, or is awash, has no single depth."
  ],
  "query": {
    "lat": 57.0405,
    "lon": -135.3421
  },
  "radiusMetres": 250,
  "soundingDatum": "Mean Lower Low Water",
  "source": {
    "cell": null,
    "cellBand": null,
    "cellEdition": null,
    "cellIssued": null,
    "cellScale": null,
    "cellUpdate": null,
    "dataset": "NOAA Electronic Navigational Charts, S-57 edition 3.1",
    "horizontalDatum": "WGS 84",
    "publisher": "NOAA Office of Coast Survey (IHO S-62 producer code 550)",
    "release": "20260502",
    "rights": "Public domain (US government work, 17 U.S.C. 105). NOAA ENC is a registered trademark of NOAA.",
    "soundingDatum": "Mean Lower Low Water",
    "units": "metres",
    "url": "https://www.charts.noaa.gov/ENCs/"
  },
  "tilesRead": 1,
  "units": "metres"
}

Payment details

Pay to0x76a51b5dd3729950b3b60c17f19252d221968fce
AssetUSD Coin · 0x833589fcd6edb6e08f4c7c32d4f71b54bda02913
NetworksBase
Schemesexact

Is this your API?

Pin it to the top of Other and the homepage with a featured placement.

Get featured →

More from geoprimitives & similar services

G

geoprimitives · Marine Depth

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 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.

GETBaseOther
$0.005 / call766 calls / 30d
G

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. 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.

GETBaseOther
$0.01 / call4 calls / 30d
G

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 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.

GETBaseOther
$0.005 / call4 calls / 30d