Migrating from Google Maps: what changes and what does not

# Migrating from Google Maps: what changes and what does not

Google made the Directions, Distance Matrix and Places APIs legacy on 1 March 2025: frozen feature sets, no new Cloud projects allowed to enable them, and volume discounts capped at the 100,000-call tier. Anyone rebuilding on the Routes API that replaced them will also find it has no truck mode at all. This post maps our endpoints against Google's honestly, including where we are weaker, and every fact below is dated 26 July 2026. Google's terms change; check the current wording yourself before you rely on any of this.

## Why teams are looking at this migration now

Legacy status is not the same as switched off. The old Directions, Distance Matrix and Places APIs keep answering calls for projects that already had them enabled, but they get no new features, no architectural improvements, and their volume discount tops out at the 100,000+ tier that the newer Routes API and Places API (New) both exceed. New Cloud projects cannot enable the legacy APIs at all. If you are starting a project today you are pushed onto Routes API and Places API (New) whether you planned to move or not, and that is the point at which the gaps below start to matter.

## The Routes API has no truck mode

Google's Routes API defines exactly five travel modes: `DRIVE`, `BICYCLE`, `WALK`, `TWO_WHEELER` and `TRANSIT`. There is no `TRUCK`, no dimensional routing, and no dangerous-goods costing. `WALK`, `BICYCLE` and `TWO_WHEELER` are still beta. If your product routes lorries, that half of the API surface does not exist for you.

The `routeModifiers` avoidance flags do not exclude either. `avoidTolls`, `avoidHighways` and `avoidFerries` each read "avoids ... where reasonable, giving preference to routes not containing ..." in Google's own reference. They bias the route away from tolls, highways or ferries; they do not guarantee the route has none. A driver who cannot legally use a toll road, or a 44-tonne artic that cannot use a height-restricted bridge, needs a hard constraint, not a preference.

MapMap's `truck` profile takes the opposite approach: declared vehicle dimensions and an ADR hazmat/tunnel-code profile are enforced in costing, not applied as a bias afterwards. `POST /route` with `costing: "truck"` and an `adr` block, or `GET /route/v1/truck/{coordinates}` with `height`, `width`, `length`, `weight`, `hazmat` and `tunnel_code` query params, both refuse edges the vehicle cannot legally use. Full reference: [truck and ADR routing](/docs/conventions#adr-and-tunnel-codes).

## Endpoint mapping

| Google Maps Platform | MapMap | Notes |
|---|---|---|
| Routes API `computeRoutes`, or the legacy Directions API | `GET /route/v1/{profile}/{coordinates}` or `POST /route` | Profiles: `driving`, `truck`, `bus`, `bicycle`, `walking`, `scooter`, `motorcycle`. Response is OSRM-shaped, not Google-shaped |
| Routes API `computeRouteMatrix`, or the legacy Distance Matrix API | `POST /matrix` | One JSON response, `durations` in seconds and `distances` in metres, `null` for unreachable pairs. No element-by-element streaming |
| Places API / Places API (New) | `GET /geocode`, `GET /geocode/reverse` | Resolves names and addresses from OpenStreetMap. Not a places-richness product; see the gap list below |
| Directions API waypoint optimisation, or Google's separate route-planning product for multi-stop fleets | `POST /optimise` (alias `/optimize`) | Multi-vehicle VRP; truck and ADR constraints shape the plan |
| Maps JavaScript API (`google.maps.Map`) | MapLibre GL via `@mapmap/maps`, or a `style.json` URL directly | Vector tiles you render, not a hosted renderer |

## Coordinate order is the first bug you will hit

Google is `lat,lng` everywhere: `{lat, lng}` objects, `origin=lat,lng` query strings. MapMap follows the OSRM and GeoJSON convention, **`lon,lat`**, in URLs, `location` arrays and geocoding results (`POST /route` request bodies use named `{lat, lon}` keys, which is the one place we keep named fields rather than an ordered pair). Audit every coordinate pair you pass across the boundary; this is the single most common first bug in a Google port and it fails silently, plotting a real point in the wrong country rather than throwing an error.

## The response shapes do not match, so budget for an adapter

Google's Routes API returns `routes[].legs[].steps[]` with duration strings like `"1234s"`, `polyline.encodedPolyline`, and HTML-formatted instructions; the legacy Directions API returns `{text, value}` pairs and `overview_polyline`. Neither looks like the other, and neither looks like ours. MapMap's compatible endpoint answers in the OSRM envelope: `routes[].distance` in metres and `duration` in seconds as plain numbers, `geometry` as an encoded polyline (`geometries=geojson` for GeoJSON, `polyline6` for six-digit precision; five-digit is the default), and `legs`/`steps` in OSRM's structure. If you already run an OSRM client library, pointing it at our endpoint is usually less work than adapting your existing Google parser.

## What you can do with a response once you have it

This is where the two platforms differ most, and it rarely gets a line in a migration guide. Google's terms cap how long you may cache what their API gives you: latitude/longitude values from geocoding may be cached for up to 30 consecutive calendar days and must then be deleted, place IDs may be kept indefinitely, and bulk download, scraping, pre-fetching and indexing of Google Maps content are all prohibited outright. Google's terms also prohibit using Google Maps content to train, test, validate or fine-tune a machine learning or AI model.

MapMap's [agent data rights manifest](/agents#data-rights) commits to the opposite for responses we have already sent you: no caching time limit, no storage ban, no indexing ban and no training ban, ever, for calls already delivered under a given version of that manifest. The one boundary that does not move is upstream: content derived from OpenStreetMap stays under ODbL, which is OpenStreetMap's licence, not ours to waive, so public redistribution still needs attribution.

## What does not port: no substitute exists

Do not promise parity here; there is nothing to point at.

- **Places richness.** Reviews, ratings, photos, opening hours and place details are a Google Places product. Our geocoding resolves names and addresses from OpenStreetMap; it is not a business-data product.
- **Street View.** Nothing comparable.
- **Traffic-aware routing.** Google's traffic-aware travel modes use live probe data. MapMap routes on the road network without live traffic.
- **Global coverage on the hosted gateway.** Google is worldwide by default. Our hosted coverage is territory-based (`GET /territories` lists what is live); self-hosting covers anywhere you build a territory for.

If your product leans on any of these, keep that piece on Google and migrate the rest. The two platforms coexist fine side by side.

## Map rendering: `google.maps.Map` to MapLibre

```diff
- const map = new google.maps.Map(document.getElementById("map"), {
- center: { lat: 52.6, lng: -1.5 },
- zoom: 6,
- });
- new google.maps.marker.AdvancedMarkerElement({ map, position: { lat: 51.5, lng: -0.13 } });
+ import maplibregl from "maplibre-gl";
+ const map = new maplibregl.Map({
+ container: "map",
+ style: "https://api.mapmap.ai/tiles/uk/style.json?api_key=snk_…",
+ center: [-1.5, 52.6], // lon, lat
+ zoom: 6,
+ });
+ new maplibregl.Marker().setLngLat([-0.13, 51.5]).addTo(map);
```

The container needs an explicit CSS height, same as Google's renderer required. `google.maps.InfoWindow` becomes `maplibregl.Popup`. Google's cloud-based map styling becomes a MapMap theme (seventeen palette slots plus per-layer overrides), built in [Studio](/studio) or through the MCP style tools. Attribution changes shape too: Google's logo and terms requirements go away, and "© OpenStreetMap contributors" (linked to openstreetmap.org/copyright) takes their place on anything you render or republish; compiled MapMap styles carry it structurally.

## What it costs

A Google API key needs a Cloud project, a billing account with a card on file, and per-API enablement. A MapMap `snk_` key is one call, card-free:

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

That key covers every endpoint; there is no per-API enablement to repeat. Standard calls (car, bicycle, pedestrian routing, matrix, isochrone, map matching, geocoding, tiles) start at 0.05p; truck and ADR calls start at 1p and draw 20 included calls from the free tier. Fifty thousand calls a month are free once the key's email is verified, commercial use included, and everything runs on prepaid credit rather than a card on file: no credit means a `402` or `429`, never a surprise invoice. Machine-readable pricing: [`/pricing.json`](/pricing.json).

## Try it

Full endpoint reference: [API reference](/docs/api-reference) (append `.md` for the raw file an agent can read directly). Coordinate order, units and the error model: [conventions](/docs/conventions). If you use a coding agent to do the port, the `mapmap-migrate-from-google-maps` skill in [`mapmap-agent-skills`](https://github.com/Mapmapai/mapmap-agent-skills) carries this same mapping as machine-readable knowledge it can act on directly.