Axiomancer
API reference — Overview

Density

Global vessel density by type, bucketed into daily H3 cells over the trailing 30 days — endpoints for density layers, choropleth maps, and lane occupancy.

Global vessel density at H3 resolution 8 (~0.74 km² per cell), bucketed by day and vessel type. The underlying matview is sourced from ais_positions joined to vessels.vessel_type, with per-IMO-per-hour bucketing so that vessels broadcasting every 10 seconds don't drown out vessels broadcasting hourly.

The matview holds a rolling 30-day window and is refreshed nightly at 02:00 UTC, after the previous day's bucket has closed. Use GET /api/v1/density to render heatmaps, identify traffic corridors, or surface where a given vessel class is concentrated on a specific day.

GET /api/v1/density

H3 cells with vessel density for a given vessel type and day, ordered by positions_count descending.

Parameters

NameTypeRequiredDescription
typestringVessel type filter. Default bulk_carrier. There is no all aggregate — the matview is keyed per (h3_index, vessel_type, day), so an unfiltered pull would return duplicate per-type cells rather than a true union; the route always filters on a single vessel type. Common values: bulk_carrier, tanker, container, lng_carrier, chemical_tanker, general_cargo, cargo, passenger, fishing, service, other
daystringISO date (YYYY-MM-DD). Defaults to yesterday in UTC — the most recent closed-day bucket the matview will have populated
limitnumberCells per response. Default 1,000. Clamped to your tier's per-call ceiling (free/public 20, starter 100, pro 500, admin 10,000), then hard-capped at 1,000 regardless of tier — PostgREST truncates TABLE responses at 1,000 rows on this project, so a higher tier limit is unreachable here.

Response

{
  "day": "2026-04-28",
  "vessel_type": "bulk_carrier",
  "cells": [],
  "count": 0,
  "tier": "starter"
}

Each cells entry carries h3_index, vessel_type, positions_count, unique_vessels:

  • h3_index — H3 resolution-8 cell ID (~0.74 km²)
  • positions_count — distinct per-IMO-per-hour position records that fell in this cell on this day
  • unique_vessels — distinct IMOs that pinged inside this cell on this day

Errors

  • 500 — Internal server error (matview query failed)

Example

curl https://www.axiomoverwatch.io/api/v1/density?type=bulk_carrier&day=2026-04-28
# A different vessel type, with an explicit (tier-bounded) limit
curl https://www.axiomoverwatch.io/api/v1/density?type=tanker&day=2026-04-28&limit=500

Freshness

Every response reflects the matview as of its most recent successful refresh. The dashboard surface at /dashboard/density displays the last refresh timestamp and status (ok or otherwise) sourced from the derived_views_freshness ledger. If you need to programmatically detect a stale matview before consuming density data, request a recent day and check that count is non-zero.

If the latest scheduled refresh failed, the most recent closed day may not yet be queryable — fall back to the prior day until the ledger reports ok.

Was this page helpful?

On this page