# Mapbox-compatible API

If your code calls Mapbox's Directions, Matrix, Isochrone, Optimization v1
or Geocoding APIs over HTTP, you can point it at MapMap by changing the
base URL and the token. The paths, the query parameters and the response
shapes stay Mapbox's. Each path is an alias of a MapMap endpoint that
already exists, it is served by that endpoint, and it is billed exactly as
that endpoint is.

```text
https://api.mapbox.com      ->  https://api.mapmap.ai
access_token=pk.…           ->  access_token=snk_…
```

Get a key with one call, no card:

```sh
curl -fsS -X POST "https://api.mapmap.ai/v1/keys" \
  -H "Content-Type: application/json" \
  -d '{"email": "you@example.com", "accept_tos": true}'
```

## The rules every alias follows

- **The key.** `access_token` is accepted as your MapMap key on these
  paths only. `?api_key=` and `Authorization: Bearer snk_…` work too. A
  Mapbox `pk.` token is refused with a 401. These paths are not in the
  keyless demo lane: every call needs a key.
- **`{user}` is `mapbox` or `mapmap`.** Profiles are `driving`,
  `driving-traffic`, `walking`, `cycling`, and `truck`, MapMap's extension
  (premium class, with lorry dimensions and ADR tunnel codes). Any other
  profile answers 404 `ProfileNotFound`.
- **`driving-traffic` is served as `driving`.** There is no separate
  traffic profile. MapMap's traffic-aware car times come from National
  Highways data on England's strategic road network only; outside it, car
  times are the routing engine's own. This is not live traffic everywhere,
  and `annotations=congestion` is refused rather than faked.
- **An option is honoured or refused, never dropped.** Mapbox refuses a
  query option it does not recognise, and so do these aliases. A Mapbox
  option MapMap cannot honour (`bearings`, `radiuses`,
  `annotations=congestion`, `continue_straight=true` and the others listed
  below) is a 400 whose message names the options or values that are
  supported. A refusal is not billed.
- **Errors keep Mapbox's body, with MapMap's statuses.** Routing errors are
  `{"code": …, "message": …}`. Where Mapbox answers 422 `InvalidInput`,
  MapMap answers 400 `InvalidValue` or `InvalidQuery`. `NoRoute`,
  `NoSegment` and Optimization's `NotImplemented` answer HTTP 200 with that
  code, as Mapbox documents. Geocoding errors are `{"message": …}`.
  Authentication, scope and quota errors (401, 403, 429) are MapMap's
  problem+json, the same on every endpoint.
- **Billing.** Each alias is metered as the endpoint it wraps, and counted
  on its own usage row (`/directions/v5`, `/directions-matrix/v1`,
  `/isochrone/v1`, `/optimized-trips/v1`, `/search/geocode/v6`,
  `/geocoding/v5`) so you can see switched traffic on its own. Prices are
  on [the pricing page](/pricing).
- **Key scope.** A browser (`maps`) key may call the geocoding aliases, as
  it may call `/geocode`. The routing aliases need a full key.

## Directions

`GET /directions/v5/{user}/{profile}/{coordinates}`, served by the
OSRM-compatible route service (`GET /route/v1`), whose responses were
already Mapbox-shaped: routes, legs, steps, `voiceInstructions`,
`bannerInstructions` and waypoints. The response adds `uuid` (the request
id), and every route carries `weight` and `weight_name: "duration"`.

What maps:

| Mapbox option | On MapMap |
|---|---|
| `alternatives`, `geometries`, `overview`, `steps`, `language` | Passed through |
| `voice_instructions`, `banner_instructions` | Passed through. Voice text is metric, so `voice_units=metric` is accepted and `imperial` is refused |
| `annotations` | `distance`, `duration`, `speed` and `maxspeed`. The first three are each manoeuvre's engine length and time spread over its segments by length, so they sum to the step totals and `speed` is the manoeuvre's average |
| `exclude` | `toll`, `motorway`, `ferry`, `tunnel`, as hard exclusions |
| `depart_at`, `arrive_by` | Local time, `YYYY-MM-DDThh:mm` |
| `approaches` | `curb` or `unrestricted`, one per coordinate |
| `continue_straight`, `roundabout_exits`, `waypoints` | Accepted only where they describe what MapMap does: `false`, `false`, and a list naming every coordinate |

What is refused, and why:

- `annotations=congestion`, `congestion_numeric`, `closure`,
  `state_of_charge`: MapMap has no live congestion feed to annotate with.
- `bearings`, `radiuses`: coordinates snap to the nearest suitable road.
- `max_height`, `max_width`, `max_weight`: use the `truck` profile with
  `height`, `width`, `length` and `weight` (metres and tonnes), which also
  takes `hazmat` and an ADR `tunnel_code`.
- `exclude=unpaved`, `cash_only_tolls`, borders and `point(…)`.
- `continue_straight=true`: every coordinate may be left in either
  direction. Mapbox defaults to `true` for driving, so a route through
  intermediate waypoints can differ from Mapbox's there.
- `waypoint_names`, `waypoint_targets`, `waypoints_per_route`, `engine`,
  EV options and the rest of Mapbox's list.

One difference worth knowing: Mapbox's exclusions are best effort, and
MapMap's are hard. Where an excluded road cannot be avoided, MapMap answers
`NoRoute` rather than a route that uses it.

Billing: as `GET /route/v1`, one call per started block of 20 waypoints;
premium on the `truck` profile.

```sh
# Before
curl "https://api.mapbox.com/directions/v5/mapbox/driving/-0.1410,50.8290;-0.1370,50.8220?steps=true&geometries=geojson&access_token=$MAPBOX_TOKEN"

# After
curl "https://api.mapmap.ai/directions/v5/mapbox/driving/-0.1410,50.8290;-0.1370,50.8220?steps=true&geometries=geojson&access_token=$MAPMAP_KEY"
```

## Matrix

`GET /directions-matrix/v1/{user}/{profile}/{coordinates}`, served by
`POST /matrix`.

What maps: `sources` and `destinations` (`;`-separated coordinate indices,
or `all`, the default); `annotations` (`duration`, the default, `distance`,
or both); `depart_at`.

What is refused: `approaches`, `bearings` and `fallback_speed`. An
unreachable pair is `null`, as on Mapbox, never a straight-line guess.

Waypoints in `sources` and `destinations` carry the coordinate you sent as
`location` and an empty `name`: the matrix engine does not report where it
snapped each point, so no snapped position or snap distance is made up.
Car durations in Great Britain include typical junction delay, as on
`POST /matrix`, and the response says so in `eta_calibration`.

Billing: as `POST /matrix`, `sources × destinations` elements at one call
per started block of 25 elements (up to 10,000 elements); premium on
`truck`.

```sh
# Before
curl "https://api.mapbox.com/directions-matrix/v1/mapbox/driving/-0.14,50.82;-0.13,50.83;-0.12,50.84?sources=0;2&annotations=duration,distance&access_token=$MAPBOX_TOKEN"

# After
curl "https://api.mapmap.ai/directions-matrix/v1/mapbox/driving/-0.14,50.82;-0.13,50.83;-0.12,50.84?sources=0;2&annotations=duration,distance&access_token=$MAPMAP_KEY"
```

## Isochrone

`GET /isochrone/v1/{user}/{profile}/{lon},{lat}`, served by
`POST /isochrone`.

What maps: `contours_minutes` or `contours_meters` (one or the other, up
to 10 contours where Mapbox allows four, each within 120 minutes or
200 km); `contours_colors`; `polygons`; `denoise`; `generalize`;
`depart_at`.

The GeoJSON features carry Mapbox's properties: `contour` (minutes, or
metres for a distance contour), `metric` (`time` or `distance`) and
`color`, with `fill` and `fillColor` on polygons.

What is refused: `exclude`. Car contours are the engine's free-flowing
times, because a typical delay that differs by road type cannot honestly
be scaled onto a shape; the response says so in `eta_calibration`.

Billing: as `POST /isochrone`, one call per contour; premium on `truck`.

```sh
# Before
curl "https://api.mapbox.com/isochrone/v1/mapbox/walking/-0.1410,50.8290?contours_minutes=5,10&polygons=true&access_token=$MAPBOX_TOKEN"

# After
curl "https://api.mapmap.ai/isochrone/v1/mapbox/walking/-0.1410,50.8290?contours_minutes=5,10&polygons=true&access_token=$MAPMAP_KEY"
```

## Optimization (v1)

`GET /optimized-trips/v1/{user}/{profile}/{coordinates}`, 2 to 12
coordinates. Served in two steps: a one-vehicle solve on `POST /optimise`
(the vehicle starts at the first coordinate and ends at the last one, or
back at the start on a round trip), then the ordered stops routed as a
Directions route, so `trips[0]` is a route object, as Mapbox's is.

What maps: `roundtrip` (default `true`), `source` (`first` or `any`),
`destination` (`last` or `any`), with Mapbox's own table of supported
combinations: `roundtrip=false` needs `source=first&destination=last`, and
any other combination answers 200 `NotImplemented`, unbilled. `steps`,
`geometries`, `overview`, `language`, `annotations` and `approaches` apply
to the route.

What is refused: `distributions`, `bearings` and `radiuses`.

Mapbox Optimization v2 (asynchronous, a different shape) is not covered;
use `POST /optimise` for fleets, time windows and capacities.

Billing: the optimise units plus the route units. A trip of up to 12
coordinates bills 11 calls: 10 for the solve and 1 for the route. When the
order is forced (at most one stop to place) no solve runs and only the
route is billed.

```sh
# Before
curl "https://api.mapbox.com/optimized-trips/v1/mapbox/driving/-0.14,50.82;-0.13,50.83;-0.12,50.84;-0.11,50.85?steps=true&access_token=$MAPBOX_TOKEN"

# After
curl "https://api.mapmap.ai/optimized-trips/v1/mapbox/driving/-0.14,50.82;-0.13,50.83;-0.12,50.84;-0.11,50.85?steps=true&access_token=$MAPMAP_KEY"
```

## Geocoding v6

`GET /search/geocode/v6/forward` and `GET /search/geocode/v6/reverse`,
served by `/geocode` and `/geocode/reverse`.

**The shape is Mapbox's; the results are MapMap's.** Answers come from
MapMap's own geocoder (OpenStreetMap data), rewritten into v6 property
names. They will not match Mapbox's results row for row, and Mapbox's own
place data is not involved. Each response carries the OpenStreetMap
credit in `attribution`.

Forward: `q`, `proximity` (`lon,lat`), `bbox`, `country`, `limit` (1 to
10), `language` (its primary subtag selects the result language), and
`types`, which is applied as a filter on the page returned, so fewer than
`limit` results can come back. `autocomplete`, `session_token` and
`permanent` are accepted and change nothing: every call is one stateless,
billed lookup, and OpenStreetMap data may be stored under the ODbL.
`proximity=ip` and `worldview` are refused.

Reverse: `longitude`, `latitude`, `limit` (default 1), `types` and
`country` (both applied as filters on the page returned).

Properties: `mapbox_id` (MapMap's own id, or `osm:<type>:<id>` for an
OpenStreetMap element), `feature_type`, `name`, `full_address`,
`place_formatted`, `coordinates` and `context` (`country` with
`country_code`, `region`, `postcode`, `place`, `neighborhood`, `street`,
and `address` where there is a house number). A point of interest is
labelled `feature_type: "poi"`, which is not in Mapbox's v6 list, rather
than passed off as an address.

Billing: as `/geocode`, one call per request.

```sh
# Before
curl "https://api.mapbox.com/search/geocode/v6/forward?q=Brighton%20Station&proximity=-0.14,50.83&access_token=$MAPBOX_TOKEN"

# After
curl "https://api.mapmap.ai/search/geocode/v6/forward?q=Brighton%20Station&proximity=-0.14,50.83&access_token=$MAPMAP_KEY"
```

## Geocoding v5 (legacy)

`GET /geocoding/v5/mapbox.places/{query}.json`, for the many integrations
that still call it. A `{query}` that is a `lon,lat` pair is a reverse
lookup, as on Mapbox.

Parameters: `proximity`, `bbox`, `country`, `limit`, `language`, `types`
(a filter on the page returned) and `autocomplete` (accepted, changes
nothing).

Features carry `id`, `type`, `place_type`, `relevance` (1 unless the
geocoder reports a score), `text`, `place_name`, `center`, `geometry`,
`properties` and `context`; the collection carries `query` and
`attribution`. Context ids are readable labels such as `place.brighton`,
not Mapbox's ids. The same rule applies as for v6: Mapbox's shape,
MapMap's results.

Billing: as `/geocode`, one call per request.

```sh
# Before
curl "https://api.mapbox.com/geocoding/v5/mapbox.places/Brighton%20Station.json?limit=5&access_token=$MAPBOX_TOKEN"

# After
curl "https://api.mapmap.ai/geocoding/v5/mapbox.places/Brighton%20Station.json?limit=5&access_token=$MAPMAP_KEY"
```

## What this does not cover

- **The map itself.** Satellite imagery and Mapbox-hosted tilesets are not
  served on Mapbox paths. Moving a Mapbox GL JS map is a renderer swap,
  covered in [migrating from Mapbox](/docs/migrate-from-mapbox).
- **Mobile SDKs.** The Mapbox Navigation and Maps SDKs for iOS and Android
  talk to more endpoints than these and validate their own token format,
  so changing a base URL does not move them. MapMap's
  [mobile SDKs](/docs/mobile) are the route there.
- **Other Mapbox APIs.** Map Matching, Optimization v2, the Search Box API,
  Tilesets, Static Images and the rest have no alias here. Where MapMap
  has an equivalent it is on its own path, listed in the
  [API reference](/docs/api-reference).

The OpenAPI document lists every alias under the `mapbox-compat` tag:
[api.mapmap.ai/openapi.json](https://api.mapmap.ai/openapi.json).
