Reference
The on-device Timeline.json format: a field reference
Since the 2024 migration, the only Google Maps location history you can export yourself comes off the handset as a file called Timeline.json. It is not the format Google Takeout used to hand out, it is not documented by Google, and it is not the same on Android as it is on iOS. This page is the field reference for it.
What this does not cover, and where to go instead. The reference for the older Google Takeout exports — Records.json, Settings.json, Timeline Edits.json and the Semantic Location History — is locationhistoryformat.com, and it is more thorough on those than this page will ever be. Its last update is dated March 2024, which is before the on-device export existed, so the shapes below are the part it leaves open. Use both.
Three shapes, not one
A parser that has to accept whatever a user drops on it is dealing with three distinct documents. The first is the legacy Takeout export, which is still what you get for pre-migration history. The other two are the on-device export, which differs by platform.
| Shape | Root | Where it comes from |
|---|---|---|
| Legacy Takeout | Object with timelineObjects | takeout.google.com, pre-migration history only |
| Android on-device | Object with semanticSegments | Settings → Location → Location services → Timeline → Export Timeline data |
| iOS on-device | A bare JSON array | Google Maps app → profile picture → Settings → Location & Privacy |
Telling them apart
Detect on the root, not on a field. Android gives you a key to look for; iOS gives you nothing — the array is the whole document, so there is no wrapper to test. The check that works is: does the root carry semanticSegments, or is the root an array whose elements look like segments?
"Look like segments" needs a real test, because a bare array is also what a plain row table looks like — and a row table is flat, every value a scalar, which is the cheapest way to rule it out. What marks a segment is a string startTime together with at least one of visit, activity or timelinePath. Note that timelinePath counts: a file can open on breadcrumbs before any trip appears, and a test that insists on a visit or an activity rejects it. Sample the first few elements rather than the first one.
One archive, many shapes. A Takeout ZIP holds files from every product you selected, and the first JSON in the archive says nothing about the hundreds behind it. Sniff each file, and when you report on a format, report the shape of the largest file rather than the first — we had a bug report carrying the shape of one file out of 997.
The same record, written two ways
Both on-device shapes agree on the outline: an array of segments, each with startTime, endTime, and either a visit or an activity, each of those holding a topCandidate. The differences are all in the leaves, and they are the reason a parser written against one platform returns nothing on the other.
| What | Android | iOS |
|---|---|---|
| Place identifier | placeId | placeID |
| Visit coordinates | placeLocation.latLng | placeLocation, a string |
| Coordinate encoding | "45.4642°, 9.1900°" | "geo:45.4642,9.1900" |
| Trip endpoints | activity.start.latLng | activity.start, a string |
distanceMeters | Number: 3850 | String: "3850" |
| Breadcrumb timing | time, absolute ISO 8601 | durationMinutesOffsetFromStartTime, a string of minutes |
| Also seen | editConfirmationStatus, isTimelessVisit | hierarchyLevel, probability repeated outside topCandidate |
The placeId / placeID split is the one that costs the most time, because no other field in the record changes case between platforms, so nothing else primes you to look for it.
Coordinates: three encodings, one rule
Across the three shapes a coordinate pair arrives in one of three ways:
| Shape | Encoding | Example |
|---|---|---|
| Legacy Takeout | Two integers, scaled by 107 | "latitudeE7": 454225900 |
| Android on-device | Decimal pair with degree signs | "latLng": "45.4642°, 9.1900°" |
| iOS on-device | geo: URI | "geo:45.4642,9.1900" |
One reader covers both on-device forms: accept either a string or an object carrying latLng, strip a leading geo:, split on the comma, strip the degree sign, and parse each half as a float. Reject the pair unless both halves come back finite — a half-parsed coordinate is worse than none, because it lands somewhere plausible.
The legacy form needs its own path: divide each E7 integer by 107. Two things bite here. First, a place visit writes the pair as centerLatE7 and centerLngE7 rather than latitudeE7 and longitudeE7, so a reader that only knows the latter silently loses every visit. Second, and worse:
Takeout sometimes ships a negative E7 coordinate wrapped as an unsigned 32-bit integer, so it arrives as the true value plus 232. Every southern latitude and western longitude in the file can land on the wrong continent, and the number looks perfectly well-formed.
A wrap is therefore always a large positive number, never a large negative one, and the only ceiling that detects it is the coordinate's own: 90° and 180°, which in E7 are 900,000,000 and 1,800,000,000. Above the ceiling, subtract 232. Judge each axis on its own value — a wrapped longitude says nothing about whether the latitude wrapped, and treating the pair as one unit corrupts the half that was fine.
timelinePath is not extra distance
Both on-device shapes carry timelinePath, an array of raw breadcrumbs. On a real export it is the bulk of the file by volume, and it is tempting to treat it as more trips.
It is not. A timelinePath retraces the same ground the activity segments already describe, so anything that sums both double-counts every kilometre. Take distance from the activity segments; treat the path as detail for drawing a line on a map. If you are counting records to detect a format change — which is worth doing — count paths separately, or they drown the counter.
A segment can also carry a timelinePath with no activity beside it. Those are breadcrumbs Google never resolved into a trip: real movement, no claimable distance, and no start or end place.
Where the legacy distance hides
For the legacy Takeout shape, an activitySegment may carry its distance in any of four fields, and usually only one of them is present:
| Field | Typically when |
|---|---|
waypointPath.distanceMeters | A route Google reconstructed through waypoints |
simplifiedRawPath.distanceMeters | A simplified trace of the raw points |
transitPath.distanceMeters | Public transport |
distance | A plain total, no path attached |
Reading only the first of these loses a substantial share of trips, silently, and the loss is not evenly spread — it follows how each trip happened to be recorded.
Other things worth knowing before you start
- Confidence changes type. The legacy shape writes
confidenceas a word —"HIGH"— andvisitConfidenceas a number out of 100. The on-device shapes writeprobabilityas a fraction between 0 and 1. Three scales, one concept. - Timestamps move. The legacy shape nests them in
duration.startTimestampandduration.endTimestamp; the on-device shapes putstartTimeandendTimeon the segment itself. - Coerce every number, and fail loudly if you cannot. The iOS export writes numbers as strings —
"distanceMeters": "27267.0". A string that reaches arithmetic untouched concatenates instead of adding, so two trips become"38503850"kilometres rather than 7,700. Worse, a string probability compared against a numeric threshold is always false, so a confidence filter written for Android silently discards every iOS trip. - Zero is a valid coordinate. The equator and the Greenwich meridian are both 0, so test for the presence of the field, not for its truthiness. A falsy check drops the Gulf of Guinea and, more often, hides a genuine parsing bug behind what looks like missing data.
- A file that parses cleanly into zero trips is not a success. An export can be structurally valid and hold nothing claimable — all visits and no activities, for instance. Treat it as its own outcome, or you will be told the tool "did nothing" and have no idea why.
- Addresses are not in the file. The on-device export carries coordinates and place identifiers, not street addresses. Turning one into the other takes a geocoding step, which is the single biggest reason a converted spreadsheet still is not usable for expenses.
- No format is final. Google has changed this data three times in three years. Anything here is what the exports look like as of August 2026.
Time-mile reads all three shapes in the browser — the Takeout ZIP, the Android Timeline.json, the iOS array — resolves the coordinates to street addresses, and exports a mileage log. Your file never leaves your machine.
Frequently asked questions
How do I tell an Android Timeline.json from an iPhone one?
By the root of the document, not by any field inside it. Android wraps the segments in an object under a semanticSegments key. iOS ships the array on its own, with no wrapper key at all, so there is no key to look for — you have to test whether the root is an array and whether its elements look like segments.
Why does my parser find no coordinates in an iPhone export?
Because iOS puts the coordinate string directly on placeLocation, while Android nests it one level deeper under a latLng key. Reading placeLocation.latLng returns undefined on every iOS record. The encodings differ too: iOS writes a geo: URI, Android writes a decimal pair carrying degree signs.
Is placeId or placeID the correct spelling?
Both, depending on the platform. Android writes placeId with a lowercase d, iOS writes placeID with a capital D. The same is true of no other field in the record, which is what makes it easy to miss.
Can I add up the distances in timelinePath to get a total?
No. A timelinePath is the raw breadcrumb trail underneath the trips, so it retraces the same ground the activity segments already cover. Summing both double-counts every kilometre. Use the activity segments for distance and treat timelinePath as detail.
Does locationhistoryformat.com cover this format?
No. It is the reference for the Google Takeout exports — Records.json, Settings.json, Timeline Edits.json and Semantic Location History — and its last update is dated March 2024, before the on-device export existed. It remains the better reference for anything that came out of Takeout.
Where does the distance live in a legacy Takeout activitySegment?
In one of four places, and usually only one of them is present: waypointPath.distanceMeters, simplifiedRawPath.distanceMeters, transitPath.distanceMeters, or a plain distance field. Which one appears depends on how the trip was recorded.