OpenDiveMap

API Reference

REST API

The OpenDiveMap API serves dive site data as GeoJSON over HTTP, following the OGC API - Features response conventions. All responses use Content-Type: application/json. No authentication is required for read endpoints.

Base URL

https://api.opendivemap.com/v1

All endpoints are served under the /v1 prefix. Unversioned paths redirect to /v1 for backward compatibility.

Units

All measurements are always returned in SI / metric, following international diving standards (PADI, SSI) and the same convention used by OpenStreetMap, NOAA, and GBIF. Field names encode the unit: max_depth_m (meters), visibility_avg_m (meters).

The API schema is fixed — field names and units never change. If your application needs imperial display, convert client-side: meters × 3.28084 = feet.

Error format

All error responses use a consistent JSON structure with a machine-readable code, a human-readable message, and optional details for field-level validation errors.

Error response
{
  "code": "INVALID_PARAMETER",
  "message": "Human-readable description of what went wrong",
  "details": [
    { "field": "topology", "reason": "Got \"invalid_value\"" }
  ]
}
INVALID_PARAMETER 400

A query parameter has an invalid value.

NOT_FOUND 404

The requested resource does not exist.

INTERNAL_ERROR 500

Unexpected server failure.

Pagination

The GET /sites endpoint supports pagination following the OGC API - Features convention. The response includes:

numberMatched — total features matching the filters (before pagination)
numberReturned — features in this page
links — array with self, next, and prev navigation URLs

Use ?limit= (default 100, max 1000) and ?offset= (default 0) to paginate. When there are more results, a next link is included.

GET /enums

Returns all accepted values for environment, topology, and level with human-readable labels and descriptions. Use this to populate filter dropdowns, validate input, or display classification info in your UI.

Example request

Request
curl "https://api.opendivemap.com/v1/enums"

Response

Response
All accepted enum values with human-readable labels and descriptions.
{
  "environments": [
    { "value": "ocean",  "label": "Ocean",  "description": "Open ocean and sea dives" },
    { "value": "lake",   "label": "Lake",   "description": "Freshwater lake dives" },
    ...
  ],
  "topologies": [
    { "value": "reef",  "label": "Reef",  "description": "Coral reef or rocky reef" },
    { "value": "wall",  "label": "Wall",  "description": "Vertical walls and drop-offs" },
    ...
  ],
  "levels": [
    { "value": "beginner",     "label": "Beginner",     "description": "Open Water or equivalent" },
    { "value": "intermediate", "label": "Intermediate", "description": "Advanced Open Water or equivalent" },
    { "value": "advanced",     "label": "Advanced",     "description": "Deep / technical certifications" }
  ]
}

GET /stats

Returns aggregate counts for the entire dataset. Use this for dashboards, badges, or homepage stats instead of fetching all sites.

Example request

Request
curl "https://api.opendivemap.com/v1/stats"

Response

Response
Aggregate counts for the entire dataset.
{
  "total_sites": 4,
  "total_countries": 3,
  "by_level": {
    "beginner": 1,
    "intermediate": 1,
    "advanced": 2
  },
  "by_environment": {
    "ocean": 4
  },
  "by_topology": {
    "pinnacle": 1,
    "cave": 1,
    "blue_hole": 1,
    "reef": 1,
    "wreck": 1
  }
}

GET /sites

Returns dive sites as a GeoJSON FeatureCollection with OGC pagination metadata.

Parameters

country string optional

Filter by ISO 3166-1 alpha-2 country code. E.g. ?country=MX

level string optional

Filter by difficulty. One of: beginner, intermediate, advanced.

environment string optional

Filter by environment. One of: ocean, lake, river, spring, quarry, fjord, pool.

topology string optional

Filter by topology. Returns sites that have the given topology. One of: reef, wall, pinnacle, wreck, cave, cavern, blue_hole, muck, kelp_forest, channel, artificial_reef, open_water.

bbox string optional

Bounding box filter. Format: west,south,east,north (decimal degrees).

limit integer optional

Maximum number of features per page. Default: 100. Max: 1000.

offset integer optional

Number of features to skip. Default: 0.

Example request

Request
curl "https://api.opendivemap.com/v1/sites?country=MX&level=advanced&limit=10"

Response

Response
GeoJSON FeatureCollection with OGC pagination metadata.
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [-110.3981, 24.2156]
      },
      "properties": {
        "id": "dccaf1e1-5df6-4058-a10c-00cdc85e87fb",
        "name": "El Bajo Seamount",
        "country_code": "MX",
        "environment": "ocean",
        "topologies": ["pinnacle"],
        "max_depth_m": 40,
        "level": "advanced",
        "tags": {
          "wildlife": ["hammerhead_shark", "manta_ray"],
          "current": "strong",
          "entry": "boat"
        }
      }
    }
  ],
  "numberMatched": 2,
  "numberReturned": 1,
  "links": [
    {
      "rel": "self",
      "href": "https://api.opendivemap.com/v1/sites?country=MX&level=advanced&limit=1",
      "type": "application/geo+json"
    },
    {
      "rel": "next",
      "href": "https://api.opendivemap.com/v1/sites?country=MX&level=advanced&limit=1&offset=1",
      "type": "application/geo+json"
    }
  ]
}

GET /sites/:id

Returns a single dive site as a GeoJSON Feature. Includes created_at and updated_at timestamps in the properties.

Path parameters

id UUID required

The dive site UUID.

Example request

Request
curl "https://api.opendivemap.com/v1/sites/dccaf1e1-5df6-4058-a10c-00cdc85e87fb"

Response

Response
Returns a GeoJSON Feature with timestamps.
{
  "type": "Feature",
  "geometry": {
    "type": "Point",
    "coordinates": [-110.3981, 24.2156]
  },
  "properties": {
    "id": "dccaf1e1-5df6-4058-a10c-00cdc85e87fb",
    "name": "El Bajo Seamount",
    "country_code": "MX",
    "environment": "ocean",
    "topologies": ["pinnacle"],
    "max_depth_m": 40,
    "level": "advanced",
    "tags": { ... },
    "created_at": "2026-02-22T03:02:19.013216+00:00",
    "updated_at": "2026-02-22T03:02:30.255437+00:00"
  }
}

GET /sites/:id/history

Returns an array of previous versions of the dive site, ordered by most recent change first. Each entry contains the full row snapshot at the time of the change.

Path parameters

id UUID required

The dive site UUID.

Example request

Request
curl "https://api.opendivemap.com/v1/sites/dccaf1e1-5df6-4058-a10c-00cdc85e87fb/history"

Response

Response
Array of previous versions, newest first. Empty array if never edited.
[
  {
    "data": {
      "id": "dccaf1e1-5df6-4058-a10c-00cdc85e87fb",
      "name": "El Bajo Seamount",
      "max_depth_m": 40,
      "level": "advanced",
      ...
    },
    "changed_at": "2026-02-22T03:02:30.255437+00:00"
  }
]