TravelTime Data API
A geospatial data layer for what's actually around a place.
TravelTime Data API stitches three independent points-of-interest sources, public-transport timetables, and named land-cover polygons into a per-country bundle you query through a spatial HTTP API. The primitives are points (POIs and transport stops), hex cells, and polygons — what you build on top of them is your decision.
Data API is a TravelTime product. Its sibling, the TravelTime Routing API, owns isochrones, routes, and the h3-fast endpoint; both live on the same TravelTime account and are metered separately.
These docs walk through two things, in order of how you'll need them:
- How the data is built — where POIs come from, how transport is modelled, what counts as green space, and how often any of it changes.
- The full API reference — every endpoint, every parameter, regenerated from the live schema on every build.
Start here
Pick the path that matches what you're trying to do.
| If you want to… | Start at |
|---|---|
| Make your first call in 60 seconds | Quickstart |
| Query by coordinate, H3 cell, or geohash | Coordinates, H3 & geohash |
| Understand what Data API is and whether to use it | How POIs are built |
| Build against it | API reference |
| Sanity-check what we ingest | Freshness & versions |
Base URL
Everywhere these docs show a curl example, the host is
https://beta.poi.traveltimeapp.com. Set two environment variables in your shell and the
examples become copy-paste:
export POI_BASE="https://beta.poi.traveltimeapp.com"
export POI_API_KEY="poi_live_..." # the key emailed to you when you signed up
Authentication
Get a key at /signup: enter your email and an API key starting with poi_live_
arrives in your inbox — it's shown nowhere else. Store it somewhere you can find it again
(a password manager, a secret store) — we hash it at rest and can't recover the plaintext.
Send the key as a bearer token on every request:
curl -H "Authorization: Bearer $POI_API_KEY" \
"$POI_BASE/v1/stats?country=GB"
A missing, malformed, or revoked key returns 401 Unauthorized from any /v1/* endpoint.
Conventions that apply everywhere
Country scoping
Every spatial endpoint accepts a country query parameter (default GB). It's an
ISO 3166-1 alpha-2 code, validated before any service call. Cross-country requests — asking
for POIs in Australia when the default is GB — are rejected with 400 rather than silently
falling back. Always send country explicitly in a multi-country client; defaults can
change.
Source-count buckets
Every POI carries a source_count (datasets that contributed) and an
independent_source_count, which discounts Overture echoes. The independent count is
the canonical confidence signal — the default floor and min_sources key on it.
| Filter | Includes | Rough precision |
|---|---|---|
| unset (default) | independent_source_count >= 2, or a register-verified / authoritative row | between 2 and 3 |
min_sources=1 | All POIs, including single-source rows — wider than the default | ~41% |
min_sources=2 | Cross-validated by ≥ 2 independent sources | ~45% on this bucket |
min_sources=3 | All-three consensus | ~75% |
The default is not 1. Sending min_sources=1 widens results by dropping the
authority-aware bar; 2 and 3 tighten it. See
How POIs are built for what "agreement" actually means in our
pipeline.
Errors
Domain exceptions are returned in a JSON envelope under a top-level error key:
{
"error": {
"code": "anchor_required",
"message": "one of (lat+lng) or poi_id is required",
"details": {}
}
}
Request-validation errors come back as standard 422 with a detail array. The
Errors page enumerates every domain code we raise, the HTTP statuses you
can see, and a sketch of a single client-side handler that covers all three buckets.