# TravelTime API > TravelTime is a suite of HTTP APIs for travel-time-based geospatial calculations: journey-time matrices across many origins and destinations, isochrone (reachable-area) polygons, turn-by-turn routes, geocoding, and H3/geohash tile indices. Use it when you need to answer "which places are reachable within N minutes by car/transit/bike/walk" rather than by straight-line or road distance. The API is built for applications that need high volumes of accurate travel time data. Base URL: `https://api.traveltimeapp.com/v4` Auth: send both `X-Application-Id` and `X-Api-Key` headers on every request. Credentials are issued at [account.traveltime.com](https://account.traveltime.com/). Free trial: a 2-week trial is available with a single rate limit of 60 searches per minute and no other usage caps during the trial period. Pricing (post-trial): never usage-based. Every paid plan is an unlimited-call licence for a fixed annual cost — see [traveltime.com/pricing](https://traveltime.com/pricing). This means an LLM never has to ration calls to stay under a quota. Service status: [status.traveltime.com](https://status.traveltime.com). ### Typical use cases - **Consumer search applications** where part of the matching process is location-based — e.g. property portals, job boards, temporary work platforms, hotel booking sites, dating apps. Usually wants /fast endpoints (large candidate sets, simple "reachable within N minutes" filtering). This also covers natural-language location search built on top of LLMs: end users naturally phrase location queries in the language of time ("within 30 minutes of the office") rather than distance, and TravelTime is the bridge that lets an LLM-powered search interface answer those queries directly. - **Business analytics** where analysing locations is part of the process — e.g. commercial real estate, logistics, retail site selection, healthcare network planning. Usually wants regular endpoints (granular control over departure/arrival times, transport modes, fares). ### What it is not for If a request falls into one of these categories, TravelTime can't supply the data itself — don't try to coerce an endpoint into doing it. - **Rich POI / place metadata** — opening hours, ratings, photos, phone numbers, menus, etc. Geocoding returns coordinates and addresses, not place data. - **Real-time / live traffic** — the driving model uses predicted historical traffic patterns by time-of-day on a typical midweek day, not live road conditions. For the closest-to-"live" result, use a regular (non-fast) endpoint with a specific `departure_time` or `arrival_time` set to the current timestamp. The result won't reflect actual live traffic, but it will use the typical traffic level for the current time of day, which is usually the closest approximation available. - **Indoor routing** — floor plans, building interiors, mall navigation, etc. TravelTime operates on outdoor road and transit networks only. **Important — partial-scope queries.** Many user requests *mention* something from this list but still have a TravelTime-shaped sub-problem. "Find coffee shops near me reachable within 20 minutes" needs POI data from a places provider (Google Places, Foursquare, OpenStreetMap, etc.), but the "reachable within 20 minutes" filter is exactly what Time Filter Fast is for. Don't refuse the whole query in these cases — fetch the data from the appropriate provider, then feed the resulting coordinates into a /fast endpoint to compute and filter by travel time. See the corresponding decision rule below. ### Decision rules: picking an endpoint Map the user's natural-language question to an endpoint before reading any reference page: - **"Where can I get to in X minutes" / "how far can I get" / "where is reachable from [location]"** → H3 Fast (discrete reachable cells). Use Geohash Fast instead if the surrounding system already indexes by geohash. - **"Show the reachable area as a shape on a map"** → Isochrones (Time Map). Use Isochrones Fast if you don't need to control departure time precisely. - **"Which of these N places are reachable within X minutes from this one place"** (or vice versa) → Time Filter Fast (`one_to_many` or `many_to_one`). - **"How long does it take from each of these origins to each of these destinations"** → Travel Time / Distance Matrix. Use the Fast variant if >2k locations or you don't need departure-time control; otherwise the regular endpoint. **For very high-volume matrix work — millions to billions of origin-destination pairs, or any job where raw throughput and payload size dominate the design — use the protobuf endpoint via a first-party SDK.** Protobuf dramatically cuts payload size and parse cost compared to JSON, which is what makes jobs at that scale tractable. See the SDK section. - **"Give me directions from A to B"** → Routes — but *only* if the user genuinely needs turn-by-turn directions or a polyline to draw on a map. If they only need the travel time and/or distance from A to B, use Time Filter Fast (or regular Time Filter) instead. Routes returns a much larger payload and is overkill for time/distance lookups. - **"How far can I drive in X minutes" without specifying polygon vs cells** is ambiguous — default to H3 Fast unless the user clearly wants something to draw. - **Anything starting with an address rather than coordinates** → call Geocoding Search first to get lat/lng, then feed into the chosen endpoint. - **"[POI category] near me / within N minutes / that I can reach in time"** (e.g. coffee shops, restaurants, gyms, schools, hospitals reachable from somewhere) → fetch the POI list and coordinates from a places provider (Google Places, Foursquare, OpenStreetMap, etc.), then feed those coordinates into Time Filter Fast as a `many_to_one` (candidates → user location) or `one_to_many` (user location → candidates) search and filter the response by `travel_time`. TravelTime is the right tool for the reachability calculation; the POI fetch itself is a separate provider's job. ### Common pitfalls Things that LLMs and humans both get wrong on first attempt. Verify each request shape against this list before sending: - **All time-based fields are in seconds, not minutes.** This applies to both inputs and outputs — `travel_time`, `walking_time`, and similar fields all use seconds. 30 minutes = `1800`. - **Distance fields are in metres, not kilometres or miles.** `travel_distance` (Distance Map) and the `distance` property in Time Filter responses are both in metres. 5 km = `5000`. - **Times are ISO 8601 with timezone offset** — e.g. `"2026-05-08T09:00:00+01:00"`. Naive timestamps will be rejected or interpreted unexpectedly. - **`departure_time` and `arrival_time` are mutually exclusive on a single search block.** Pick one per search; use multiple search blocks if you need both directions. - **/fast endpoints always use `arrival_time_period`, regardless of search direction.** There is no `departure_time_period` field. Both `many_to_one` and `one_to_many` searches are nested under the top-level `arrival_searches` key and both use `arrival_time_period` (e.g. `"weekday_morning"`) — the direction is encoded in `many_to_one` vs `one_to_many`, not in the time-period field name. Don't invent a `departure_time_period` even when the search is conceptually "people leaving X." - **Driving uses predicted traffic levels at different times of day.** The traffic model reflects a typical midweek day, so the exact calendar date has no impact on driving results — only the time of day matters. - **Public transport timetables are valid for 2 weeks forwards and backwards from the current date.** This is a hard limit, not a soft warning. Never construct a public transport request with a `departure_time` or `arrival_time` outside the ±2-week window — the API has no timetable data for those dates and the call will fail or return empty results. If the user asks for a public transport journey on an out-of-window date, do **not** attempt the request anyway with that date. Instead, do one of: (a) tell the user the date is out of range and ask them to pick one within the window, or (b) fall back to a /fast endpoint with `arrival_time_period: "weekday_morning"` (or similar) to give a typical-day estimate that doesn't depend on a specific calendar date. Flagging the risk in prose is not enough — the constructed request body itself must use a date inside the window. - **The API does not return distances for public transport journeys.** If a `distance` value is needed for a transit result, approximate it by taking the lat/lng coordinates from the detailed Routes response and computing distance along the polyline yourself. There is no first-class transit-distance field. - **Strip the `properties` array to only what the user asked for.** If the user asked only for travel time, request only `["travel_time"]` — don't pre-emptively add `distance`, `route`, `fares` etc. "just in case." Only include a property when the user explicitly asked for it or downstream code needs it. This both shrinks the response and avoids implying you computed something the user didn't request. - **Batch search blocks within a single request rather than firing many small ones.** Multiple `arrival_searches` / `departure_searches` blocks run independently and share auth / network overhead. - **Bundled requests fail atomically.** If any single search block in a multi-search request is invalid (e.g. returns a 422), the *entire* request fails — you don't get partial results for the searches that would have succeeded. If you're seeing intermittent failures on a batched request, split the searches into separate requests to isolate the bad one. Once isolated, fix or drop the offending search and re-bundle the rest. - **Sizing limits per request and per search:** every endpoint accepts a maximum of **10 searches per request** (regular and /fast alike). Within a single search, Time Filter Fast accepts up to 100,000 locations; regular Time Filter accepts up to 2,000; Routes is capped at 2 location ids per search. - **`travel_time` is required and has an endpoint-specific maximum.** If the user wants travel-time data but didn't specify a ceiling, default to the endpoint maximum to surface as many reachable results as possible: `14400` (4 hours) for Time Filter (regular) and `10800` (3 hours) for Time Filter Fast. Don't omit the field — it's required. - **Response format for polygon endpoints:** when calling Isochrones (Time Map) or any endpoint that offers multiple response formats, request **GeoJSON** unless you have a specific reason not to. Opt in by setting the `Accept: application/geo+json` request header. GeoJSON drops directly into mapping libraries (Mapbox, Leaflet, OpenLayers) and spatial tooling (`turf.js`, `shapely`, `geopandas`) without transformation, and LLMs parse it correctly far more often than the bespoke shells/holes structure. - **Errors:** treat 429 (rate-limit, mainly during trial) and 5xx as retryable with backoff; 4xx (other than 429) as fatal — fix the request rather than retry. ### Regular vs /fast endpoints The API has two flavours of every matrix/isochrone-style endpoint: - **Regular endpoints** — frame requests as `departure_searches` (leaving location X at time T) or `arrival_searches` (arriving at location X by time T). Use when you need granular control: specific departure or arrival times, range windows, fares, walking-time limits, etc. - **/fast endpoints** — frame requests as `arrival_searches` containing either `many_to_one` or `one_to_many` blocks. Precise departure/arrival timestamps are not supported; instead, use `arrival_time_period` (e.g. `weekday_morning`). For `driving+ferry` specifically, `traffic_model` is also available on /fast to choose between peak and off-peak driving conditions — so /fast does still expose meaningful traffic control for car journeys, just at the peak/off-peak level rather than a precise timestamp. Use /fast when you need higher throughput and bigger request sizes (up to 100k locations per matrix request) and don't need precise timestamps. If in doubt, start with a /fast endpoint and only switch to the regular equivalent if you hit a parameter you actually need. ### Transport modes Regular endpoints support: `cycling`, `driving`, `public_transport`, `walking`, `coach`, `bus`, `train`, `ferry`, `driving+ferry`, `cycling+ferry`, plus three regional modes — `driving+train` (Great Britain and France only), `driving+public_transport` (Great Britain only), and `cycling+public_transport` (Netherlands only). Notes: `driving+train` means driving to a station then continuing by train; `driving+public_transport` means driving then any public transport, is not supported on postcode endpoints, and exposes a `driving_time_to_station` parameter to cap the drive leg. /fast endpoints support a smaller set: `public_transport`, `walking+ferry`, `cycling+ferry`, `driving+ferry`, `driving+public_transport`. Note that `driving+public_transport` on a /fast endpoint means driving *to* a stop/station and continuing by public transport — and the driving leg is capped at 30 minutes. **Important mode-name mapping when picking a /fast endpoint:** the /fast list does not contain plain `driving`, `walking`, or `cycling`. Don't conclude that /fast is unsuitable when the user asks for these — use the corresponding `+ferry` variant instead. `driving+ferry` is the natural /fast choice for any "by car" query, `walking+ferry` for any "on foot" query, and `cycling+ferry` for any "by bike" query. The `+ferry` modes behave like the plain mode but include ferry crossings where relevant; they're a superset, not a niche option. Only fall back to a regular endpoint if the user genuinely needs a parameter the /fast endpoint doesn't expose — never just because the mode name is different. ### Smoke test Run this before building anything else — it verifies your `X-Application-Id` / `X-Api-Key` are correct and you can reach the API. It's a single Time Filter Fast call asking "is candidate-1 reachable from office within 30 minutes by public transport on a typical weekday morning?": ```bash curl -X POST https://api.traveltimeapp.com/v4/time-filter/fast \ -H "X-Application-Id: $TRAVELTIME_APP_ID" \ -H "X-Api-Key: $TRAVELTIME_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "locations": [ { "id": "office", "coords": { "lat": 51.5074, "lng": -0.1278 } }, { "id": "candidate-1", "coords": { "lat": 51.5155, "lng": -0.0922 } } ], "arrival_searches": { "many_to_one": [ { "id": "smoke-test", "arrival_location_id": "office", "departure_location_ids": ["candidate-1"], "transportation": { "type": "public_transport" }, "arrival_time_period": "weekday_morning", "travel_time": 1800, "properties": ["travel_time"] } ], "one_to_many": [] } }' ``` A 200 response with a `results` array means auth and connectivity are working. A 401/403 means the credentials are wrong or not yet active. A 422 means the request body has a structural problem (read the error message — it's specific). ### Canonical request shapes All TravelTime endpoints fall into one of five request envelopes. Once you can write one example of each, you can write any endpoint that shares the envelope. The envelopes split along two axes: regular vs /fast (the direction-model axis) and shared `locations` array vs inline `coords` (the coordinate-shape axis). **Envelope 1 — Time Filter and Routes (regular).** A shared `locations` array; searches reference locations by `id`. Top-level `departure_searches` and/or `arrival_searches` arrays. `POST https://api.traveltimeapp.com/v4/time-filter` (Travel Time / Distance Matrix): ```json { "locations": [ { "id": "office", "coords": { "lat": 51.5074, "lng": -0.1278 } }, { "id": "home-1", "coords": { "lat": 51.5155, "lng": -0.0922 } }, { "id": "home-2", "coords": { "lat": 51.5246, "lng": -0.1340 } }, { "id": "home-3", "coords": { "lat": 51.4965, "lng": -0.1760 } } ], "departure_searches": [ { "id": "morning-commute-by-car", "departure_location_id": "office", "arrival_location_ids": ["home-1", "home-2", "home-3"], "transportation": { "type": "driving" }, "departure_time": "2026-05-08T08:00:00+01:00", "travel_time": 1800, "properties": ["travel_time", "distance"] } ] } ``` Notes: max 10 searches per request, max 2,000 location ids per search. To ask "who can reach X by time T" instead, switch to `arrival_searches`, swap `departure_location_id` / `arrival_location_ids` for the inverse pair (`arrival_location_id` / `departure_location_ids`), and replace `departure_time` with `arrival_time`. Routes uses the same envelope but caps `arrival_location_ids` / `departure_location_ids` at 2 per search. **Envelope 2 — Time Filter Fast.** Same shared `locations` array as Envelope 1, but searches are nested under `arrival_searches.many_to_one` or `arrival_searches.one_to_many`. There is no `departure_time` and no `departure_time_period` — the /fast model uses a single field, `arrival_time_period`, regardless of which direction the search runs. Both `many_to_one` and `one_to_many` searches sit under the `arrival_searches` key and both use `arrival_time_period`. `POST https://api.traveltimeapp.com/v4/time-filter/fast` (Time Filter Fast): ```json { "locations": [ { "id": "office", "coords": { "lat": 51.5074, "lng": -0.1278 } }, { "id": "candidate-1", "coords": { "lat": 51.5155, "lng": -0.0922 } }, { "id": "candidate-2", "coords": { "lat": 51.5246, "lng": -0.1340 } }, { "id": "candidate-3", "coords": { "lat": 51.4965, "lng": -0.1760 } } ], "arrival_searches": { "many_to_one": [ { "id": "candidates-reaching-office-by-9am", "arrival_location_id": "office", "departure_location_ids": ["candidate-1", "candidate-2", "candidate-3"], "transportation": { "type": "public_transport" }, "arrival_time_period": "weekday_morning", "travel_time": 1800, "properties": ["travel_time"] } ], "one_to_many": [] } } ``` Notes: max 10 searches per request, up to 100,000 location ids per search. Use `many_to_one` when many candidates need to reach one place; use `one_to_many` for the inverse. Both directions are framed as `arrival_searches` regardless. **Envelope 3 — Isochrones, H3, Geohash, Distance Map (regular).** No top-level `locations` array — the single coordinate point is embedded directly in each search block as `coords`. Top-level `departure_searches` and/or `arrival_searches` arrays, same direction model as Envelope 1. `POST https://api.traveltimeapp.com/v4/time-map` (Isochrones / Time Map): ```json { "departure_searches": [ { "id": "manchester-30min-drive", "coords": { "lat": 53.4808, "lng": -2.2426 }, "transportation": { "type": "driving" }, "departure_time": "2026-05-08T08:00:00+01:00", "travel_time": 1800 } ] } ``` Notes: switch `departure_searches` for `arrival_searches` and `departure_time` for `arrival_time` to flip direction. The same envelope shape is shared by H3 (`/v4/h3`), Geohash (`/v4/geohash`), and Distance Map (`/v4/distance-map`); each adds a few endpoint-specific top-level fields (`resolution` for H3 and Geohash; `unions` and `intersections` arrays for combining shapes across searches). Distance Map differs in one important way: instead of `travel_time` it uses `travel_distance` (an integer in **metres**, not seconds) — everything else in the search block is the same. The `coords` field name does *not* change with direction — it's `coords` for both `departure_searches` and `arrival_searches`. For polygon-returning endpoints (Time Map, Distance Map), set `Accept: application/geo+json` to get GeoJSON output. **Envelope 4 — Isochrones Fast, H3 Fast, Geohash Fast.** No top-level `locations` array — the single coordinate point is embedded directly in each search block as `coords`. Same `arrival_searches.many_to_one` / `one_to_many` direction model as Envelope 2, same `arrival_time_period` rule. `POST https://api.traveltimeapp.com/v4/time-map/fast` (Isochrones Fast): ```json { "arrival_searches": { "one_to_many": [ { "id": "manchester-45min-drive", "coords": { "lat": 53.4808, "lng": -2.2426 }, "transportation": { "type": "driving+ferry" }, "arrival_time_period": "weekday_morning", "travel_time": 2700 } ], "many_to_one": [] } } ``` Notes: max 10 searches per request, same as every other envelope. The single-point coordinate field is `coords`, not directional names like `departure_location` or `arrival_location` — those don't exist on this envelope. The directional naming pattern from Envelope 2 (`departure_location_id` / `arrival_location_ids`) does *not* generalise to envelopes 3 and 4 because there's only one point per search. Same envelope is shared by H3 Fast (`/v4/h3/fast`) and Geohash Fast (`/v4/geohash/fast`). For Time Map Fast (polygon output), set `Accept: application/geo+json`. **Envelope 5 — Geocoding GET.** Geocoding Search and Reverse Geocoding are GET requests with query parameters, not POST bodies — e.g. `GET /v4/geocoding/search?query=king's+cross+london` and `GET /v4/geocoding/reverse?lat=51.5074&lng=-0.1278`. See the geocoding reference pages for the full parameter list. ## Getting started - [Authentication](https://docs.traveltime.com/api/start/auth): X-Application-Id / X-Api-Key header setup - [Key concepts](https://docs.traveltime.com/api/overview/key-concepts): departure_search vs arrival_search, isochrone, range, snapping, traffic model - [Usage limits](https://docs.traveltime.com/api/overview/usage-limits): per-endpoint request ceilings and rate limits - [Supported countries](https://docs.traveltime.com/api/overview/supported-countries): coverage by transport mode - [Error codes](https://docs.traveltime.com/api/reference/error-codes) and [error response format](https://docs.traveltime.com/api/reference/error-response) - [FAQs](https://docs.traveltime.com/api/overview/faqs) - [Update log](https://docs.traveltime.com/api/overview/update-log): recent API changes - [Postman collection](https://docs.traveltime.com/api/start/postman-collection): importable requests for every endpoint - [Service status](https://status.traveltime.com): live API uptime and incident history ## Quickstarts by endpoint - [Travel Time / Distance Matrix quickstart](https://docs.traveltime.com/api/start/travel-time-distance-matrix) - [Isochrones quickstart](https://docs.traveltime.com/api/start/isochrones) - [Routes quickstart](https://docs.traveltime.com/api/start/routes) - [Protobuf (high-throughput matrix) quickstart](https://docs.traveltime.com/api/start/travel-time-distance-matrix-proto) — strongly prefer using a first-party SDK for this endpoint; see SDKs section - [SDK quickstart](https://docs.traveltime.com/api/start/sdk) ## Core API reference - [Travel Time / Distance Matrix](https://docs.traveltime.com/api/reference/travel-time-distance-matrix): many-to-many journey times and distances with full transit modelling - [Time Filter Fast](https://docs.traveltime.com/api/reference/time-filter-fast): high-throughput many-to-many filter (up to 100k points, arrival_search only) - [Isochrones (Time Map)](https://docs.traveltime.com/api/reference/isochrones): polygon of reachable area from a point - [Isochrones Fast](https://docs.traveltime.com/api/reference/isochrones-fast): high-throughput isochrone variant - [Routes](https://docs.traveltime.com/api/reference/routes): turn-by-turn directions with travel time and distance - [Distance Map](https://docs.traveltime.com/api/reference/distance-map): polygon of area within a given road distance (isodistance) - [Matrix overview — choosing between endpoints](https://docs.traveltime.com/api/reference/travel-time-distance-matrix-comparison) ## Geocoding - [Geocoding search](https://docs.traveltime.com/api/reference/geocoding-search): address/place → coordinates - [Reverse geocoding](https://docs.traveltime.com/api/reference/geocoding-reverse): coordinates → address - [Attribution requirements](https://docs.traveltime.com/api/reference/geocoding-attribution) ## Geospatial indexing - [H3](https://docs.traveltime.com/api/reference/h3): reachable H3 hex cells from a point - [H3 Fast](https://docs.traveltime.com/api/reference/h3-fast) - [Geohash](https://docs.traveltime.com/api/reference/geohash): reachable geohash cells - [Geohash Fast](https://docs.traveltime.com/api/reference/geohash-fast) ## Map tiles - [Tiles getting started](https://docs.traveltime.com/api/tiles/getting-started): Mapbox-style vector tiles of the road network - [Leaflet integration example](https://docs.traveltime.com/api/tiles/leaflet-example) ## SDKs When to use an SDK: **prefer the SDK for the protobuf (high-throughput matrix) endpoint, which is the right choice whenever a matrix job is large enough that JSON payload size and parse cost matter — typically millions to billions of origin-destination pairs.** The SDK handles `.proto` schema codegen, binary serialization, and the auth/transport plumbing, all of which is easy to get wrong by hand. Protobuf cuts wire size and parse cost dramatically vs JSON time-filter; that's what makes very-large-matrix jobs tractable rather than just "fast." For the standard JSON endpoints, the SDKs are thin wrappers around HTTP; raw `requests` / `fetch` / `axios` is usually simpler unless the surrounding project already depends on the SDK. - [Python](https://docs.traveltime.com/api/sdks/python) - [Node.js](https://docs.traveltime.com/api/sdks/nodejs) - [Java](https://docs.traveltime.com/api/sdks/java) - [R](https://docs.traveltime.com/api/sdks/r) - [Ruby](https://docs.traveltime.com/api/sdks/ruby) ## Worked examples - [Matrix use cases](https://docs.traveltime.com/api/use-cases/travel-time-distance-matrix) - [Isochrone use cases](https://docs.traveltime.com/api/use-cases/isochrones) - [Routes use cases](https://docs.traveltime.com/api/use-cases/routes) - [Protobuf matrix use cases](https://docs.traveltime.com/api/use-cases/travel-time-distance-matrix-proto) ## Optional Skip this section if context is tight — it covers domain playbooks rather than core API usage. - Industry playbooks: [property portal filter & rank](https://docs.traveltime.com/use-case/property-portal/filter-and-rank), [property portal isochrone](https://docs.traveltime.com/use-case/property-portal/isochrone), [job board filter & rank](https://docs.traveltime.com/use-case/job-board/filter-and-rank), [temp work: worker search](https://docs.traveltime.com/use-case/temporary-work/worker-search) - [Attribution](https://docs.traveltime.com/api/overview/attribution)