TL;DR
- 9 paid endpoints under
/api/v1/vessels/*and/api/v1/hormuz/*, gated by the same x402 payment flow as the events corpus. Per-call USDC pricing from $0.001 to $0.025 depending on the route. - 1 free discovery endpoint:
/api/v1/vessels/facetsreturns the distinct enum values (flags, AIS types, subtypes, commercial market names, country codes) you'll need to build valid/searchand/in-areaqueries. Daily-cached, small payload, free for everyone — paying agents would otherwise burn$0.002per/searchmiss probing unknown values. - Same UA gate as the rest of the paid surface: real browsers
and recognised AI-search / link-preview bots (Perplexity,
ChatGPT-User, Googlebot, etc.) get the JSON for free; training
crawlers and generic automation UAs (
python-requests,axios,node, anonymouscurl, x402 buyer SDKs, …) pay per request. The free/api/v1/vessels/facetstaxonomy stays free for everyone so even a paying agent can pre-flight cheaply. - Sources: MarineTraffic for vessel identity + AIS positions (~600k vessels tracked in our mirror, including ~1,400 sanctioned tankers from OFAC / UK / EU / Switzerland), and the ShipTracker upstream for the Strait of Hormuz transit feed (rolling 14-day window).
- Cursor-based pagination on every list endpoint; opaque base64-JSON cursors that encode the original filter set, so paginating across thousands of rows always costs the same per page (no priced "rewind" or "skip-N" patterns).
What's live
| Path | Default price | Bucket |
|---|---|---|
/api/v1/vessels/facets |
free | Discovery: enum dump of legal flag / type / subtype / commercial_market_name / country_code values. Daily-cached. |
/api/v1/vessels/{imo} |
$0.001 | Identity row for one vessel by IMO. Returns canonical metadata: name, AIS name, MMSI, flag, type, subtype, length, build year, transponder class, commercial market, sanctioned flag. |
/api/v1/vessels/{imo}/position |
$0.001 | Most recent AIS position for one vessel — (captured_utc, lat, lon, sog, cog, heading, destination, age_seconds). |
/api/v1/vessels/{imo}/sanctions |
$0.001 | Sanctions-list memberships for one vessel (OFAC SDN, UK OFSI, EU CFSP, Swiss SECO, …). One row per sanctioning body + listing date. |
/api/v1/vessels/{imo}/track |
$0.010 | AIS polyline for one vessel over the last hour (downsampled, oldest-first). |
/api/v1/vessels/search |
$0.002 | Faceted vessel-identity search across the ~600k-vessel mirror. Free-text q matches name prefix, IMO, or MMSI; facet filters on flag/type/subtype/commercial market; boolean sanctioned / watchlisted; numeric min_length / min_year_built. 20 rows/page. |
/api/v1/vessels/in-area |
$0.010 | Vessels currently inside a bbox (e.g. Strait of Hormuz), snapshot cursor for stable pagination. 10 rows/page. |
/api/v1/vessels/sanctioned |
$0.025 | Bulk sanctioned-fleet feed with last-known position for each row. 10 rows/page. ~1,400 vessels total → ~140 pages. |
/api/v1/hormuz/crossings |
$0.010 | Per-vessel Strait of Hormuz crossings over a rolling 14-day window. Date facets, sanctioned-only filter, direction (0/1/unknown). 10 rows/page. |
/api/v1/hormuz/summary |
$0.005 | Daily aggregate analytics: per-day transit counts (split by direction, sanctioned / watchlisted / distinct-operators), top-10 operators, top-10 flags. Single paid call returns three rollups in one response. |
Per-route prices are env-tunable (X402_PRICE_MARINE_* knobs on
the gate). The defaults above are what's currently in production.
Why these specific endpoints
We started from "what does an agent actually need to answer a maritime question?" and worked backwards:
- What's that ship? →
/api/v1/vessels/{imo}for the identity row +/positionfor where it is right now. - Find me ships matching X. →
/searchwith facets. - What's currently in this area? →
/in-areawith a bbox snapshot cursor. - Show me the sanctioned fleet. →
/sanctionedfor the bulk dump, or/{imo}/sanctionsfor one vessel. - What's been moving through Hormuz? →
/hormuz/crossingsfor the raw feed,/hormuz/summaryfor daily rollups. - What facet values are legal for filtering? →
/vessels/facets(free, pre-flight).
That's it — no kitchen-sink "vessels API" with 50 verbs. Every paid route does one job and returns the smallest payload that answers it.
Pagination contract (worth understanding before you pay)
Every list endpoint uses an opaque cursor that encodes the original filter set. The pattern is:
- First call: pass your filters (
?country_code=PA&type=Tanker&...), no cursor. - Response includes
next_cursorandhas_more: true. - Subsequent calls: pass
?cursor=<the opaque string>alone — DO NOT also repeat the original filters. The cursor carries them. - When
has_more: false(ornext_cursor: null), you're done.
This matters because:
- Cursors are stable across snapshots for /in-area (the
cursor pins the snapshot timestamp, so pagination doesn't drift
even if AIS data updates between calls).
- Pages are hard-capped at 10-20 rows so per-page cost is
predictable. A 20-row /search page is always $0.002 — never
more, never less.
- Repeating filters with a cursor is a soft conflict; the
server prefers the cursor's encoded filters and ignores the
duplicates. Saves the cost-walk debugging trip.
Free-tier walkthrough: /api/v1/vessels/facets
Before you spend a cent, hit /api/v1/vessels/facets. It's a
single JSON response containing seven distinct enum dumps:
{
"flags": ["PA", "LR", "MH", "..."],
"types": ["Tanker", "Cargo", "Tug", "..."],
"subtypes": ["Crude Oil Tanker", "Chemical Tanker", "..."],
"markets": ["Tanker", "Wet Bulk", "..."],
"countries": ["Panama", "Liberia", "Marshall Islands", "..."],
"sanction_sources": ["OFAC SDN", "UK OFSI", "EU CFSP", "..."],
"operators": [{"name": "...", "vessel_count": 472}, "..."]
}
Cache for ~24 hours and use the values verbatim as query-string
inputs to /search. Without this dump, an agent has to guess
that ?type=Tanker is valid but ?type=tanker isn't — and
every guess is a paid call.
The endpoint is free for everyone, no x402, no UA gate. We
take the position that taxonomy discovery is part of the public
contract, not part of the revenue surface. Same policy as
/api/v1/regions, /countries, and /event-types on the
events side.
Sanctioned-fleet specifics
The /api/v1/vessels/sanctioned row count tracks the union of
OFAC SDN, OFAC NS-MBS, UK OFSI, EU CFSP, Swiss SECO, and a
handful of public sanctions watchlists. As of this post that's
roughly 1,400 vessels. We don't deduplicate listings across
bodies — a vessel listed by both OFAC and OFSI returns separate
rows from /{imo}/sanctions, with each (body, listing_date)
pair preserved.
Each row in the bulk feed carries the vessel's last known AIS
position (or null if the vessel hasn't broadcast since being
added). That makes the endpoint usable as a "where are the
sanctioned ships right now?" query without an N+1 follow-up
into /{imo}/position.
Hormuz endpoints
/api/v1/hormuz/crossings and /hormuz/summary are derived
from a separate ShipTracker pipeline that watches a fixed bbox
over the Strait of Hormuz and records each unique vessel
transit (direction inferred from the trajectory's east-west
component).
- Crossings is the raw event stream — one row per
(vessel, crossing_utc, direction). Useful for "what came through yesterday?" and "is X moving in or out?" questions. - Summary rolls the same data up to daily aggregates plus top-N operators and flags over the window. Useful for trend questions ("are sanctioned-tanker crossings increasing?") without paying per-row.
Both endpoints honour a ?sanctioned=true filter that
intersects against the sanctioned-vessel set above.
Why per-call x402 vs a flat API key
The events corpus answered this two posts ago. Same answer
here: AI agents are stateless, want to evaluate a single
question without first negotiating a contract, and prefer the
cost of an answer to scale with the size of the answer. A
$0.001 per-call price on /vessels/{imo} means an agent
asking "what's the IMO 9217981 vessel?" pays $0.001 and gets
the JSON. No signup, no key rotation, no monthly minimum.
For high-volume buyers running thousands of /sanctioned page
walks a day, the per-call model is also defensible: a full
sweep of the sanctioned fleet (~140 pages × $0.025) costs
~$3.50, runs in under a minute, and is fully reproducible
across runs because cursors are deterministic for a given filter.
Live mainnet receipts
Every advertised (facilitator, chain) cell on the marine
routes was validated with a real paid request before this post
went live, across the same four facilitators that already cover
the events corpus (xpay on Base, CDP on Base + Polygon, PayAI
on Base + Polygon + Avalanche, Dexter on Base via Permit2). The
seeded settlements are visible in the per-facilitator counters
on /admin/ai-traffic and on-chain via block explorers
(basescan.org, polygonscan.com, snowtrace.io).
Links
- Free facets endpoint:
/api/v1/vessels/facets - Live discovery:
/openapi.json+/.well-known/x402 - Free policy explainer:
/x402 - Per-route pricing table:
/x402— search "Maritime" - LLM-friendly docs:
/llms.txt,/llms-full.txt - Prior post on the payment plumbing: Multi-facilitator, multi-chain x402