Marine Depth
by geoprimitives in Weather & location
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
GET https://api.geoprimitives.dev/v1/marine/depth
Last 30 days
Status and outage history · Atom feed
- Uptime
- 100%
- Response time
- 739 ms typical, 1.3 s slowest 5%
- Last check
- 2 h ago
- Next check
- any minute now
How to call it
GET https://api.geoprimitives.dev/v1/marine/depth
Each call costs $0.005 in USDC on Base, paid with the request over x402.
Run a free test callOne unpaid request, the way an agent sends it. You see the payment challenge it gets back. Nothing is charged.
Example request
From the listing.
GET https://api.geoprimitives.dev/v1/marine/depth?lat=57.0405&lon=-135.3421Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
lat | number | Yes | Latitude in decimal degrees, EPSG:4326. Refused outside -90..90. |
lon | number | Yes | Longitude in decimal degrees, EPSG:4326. Refused outside -180..180. |
Example response
The provider's example. We can't check it without paying.
Example response, 89 lines
{
"chart": {
"band": "harbour",
"cell": "US5AK2NE",
"cellsConsulted": [
"US5AK2NE"
],
"edition": 3,
"issued": "20260302",
"scale": 12000,
"update": 0
},
"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"
},
"depthAreaMaximum": 36.5,
"depthAreaMinimum": 18.2,
"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."
},
"interpolation": "none. depthAreaMinimum is DRVAL1 of the charted depth area containing the position, and nearestSounding is a real charted sounding with the distance to it. Nothing between them is invented.",
"nearestSounding": {
"depth": 20.1,
"distanceMetres": 5.88,
"lat": 57.0405259,
"lon": -135.3420156
},
"notes": [],
"query": {
"lat": 57.0405,
"lon": -135.3421
},
"soundingDatum": "Mean Lower Low Water",
"source": {
"cell": "US5AK2NE",
"cellBand": "harbour",
"cellEdition": 3,
"cellIssued": "20260302",
"cellScale": 12000,
"cellUpdate": 0,
"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/"
},
"surveyQuality": {
"depthAccuracy": "0.50 m + 1% depth; full area search, all significant seafloor features detected",
"positionAccuracy": "5 m + 5% depth",
"positionAccuracyMetres": null,
"soundingAccuracyMetres": null,
"surveyEnd": "20020510",
"surveyStart": "20020429",
"zoneOfConfidence": "A1"
},
"tilesRead": 1,
"units": "metres"
}Call it from code
The curl command shows the payment challenge. The JavaScript and Python versions pay and call, and never pay more than $0.005.
curl: see the payment challenge
# bash or zsh: see the payment challenge. Nothing is charged.
curl -sS -g -i 'https://api.geoprimitives.dev/v1/marine/depth?lat=57.0405&lon=-135.3421'
# x402 v2 sends the challenge base64-encoded in the PAYMENT-REQUIRED header (v1 sends it in the body).
# To read the header:
curl -sS -g -o /dev/null -D - 'https://api.geoprimitives.dev/v1/marine/depth?lat=57.0405&lon=-135.3421' \
| grep -i '^payment-required:' | cut -d' ' -f2 | tr -d '\r' | base64 --decodeJavaScript: pay and call
// npm i @x402/fetch @x402/evm viem
import { wrapFetchWithPayment, x402Client } from "@x402/fetch";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
// Pays at most $0.005 per call.
const client = new x402Client().setSpendControls({ maxAmountPerPayment: "$0.005" });
registerExactEvmScheme(client, { signer: privateKeyToAccount(process.env.EVM_PRIVATE_KEY) });
const pay = wrapFetchWithPayment(fetch, client);
// Not sure it's safe to pay? ToolVet checks it first for $0.005. Pay for that from a separate
// client: this one is capped at $0.005 per payment. GET https://toolvet.app/api/v1/check?url=https%3A%2F%2Fapi.geoprimitives.dev%2Fv1%2Fmarine%2Fdepth
const res = await pay("https://api.geoprimitives.dev/v1/marine/depth?lat=57.0405&lon=-135.3421");
console.log(res.status, await res.text());Python: pay and call
# pip install "x402[requests,evm]"
import os
from eth_account import Account
from x402 import x402ClientSync
from x402.http.clients import x402_requests
from x402.mechanisms.evm import EthAccountSigner
from x402.mechanisms.evm.exact.register import register_exact_evm_client
# Pays at most $0.005 per call.
client = x402ClientSync().set_spend_controls({"max_amount_per_payment": "$0.005"})
register_exact_evm_client(client, EthAccountSigner(Account.from_key(os.environ["EVM_PRIVATE_KEY"])))
with x402_requests(client) as session:
res = session.get("https://api.geoprimitives.dev/v1/marine/depth?lat=57.0405&lon=-135.3421")
print(res.status_code, res.text)Security scan
- No findings. We scan names, descriptions and tool definitions for hidden instructions and other prompt-injection patterns.
Recent checks
| When | Result | HTTP | Time | Price |
|---|---|---|---|---|
| 2 h ago | Passed | 402 | 952 ms | $0.005 |
| 6 h ago | Passed | 402 | 677 ms | $0.005 |
| 10 h ago | Passed | 402 | 682 ms | $0.005 |
| 14 h ago | Passed | 402 | 539 ms | $0.005 |
| 18 h ago | Passed | 402 | 533 ms | $0.005 |
| 22 h ago | Passed | 402 | 1.1 s | $0.005 |
| 25 h ago | Passed | 402 | 996 ms | $0.005 |
| 29 h ago | Passed | 402 | 715 ms | $0.005 |
| 33 h ago | Passed | 402 | 1.3 s | $0.005 |
| 36 h ago | Passed | 402 | 655 ms | $0.005 |
| 40 h ago | Passed | 402 | 3.3 s | $0.005 |
| 43 h ago | Passed | 402 | 905 ms | $0.005 |
| 47 h ago | Passed | 402 | 731 ms | $0.005 |
| 2 days ago | Passed | 402 | 1.1 s | $0.005 |
| 2 days ago | Passed | 402 | 1.0 s | $0.005 |