Timesheet segments
Segmented Tracking breaks a shift into segments: where the employee clocked in, when they were driving, stopped at a job site, idle somewhere else or on a break, and where they clocked out. The segments endpoint returns those segments for one timesheet, along with a summary of the shift.
Before you start
- Your company needs the Segmented Tracking add-on.
- Send your API key in the
Authorizationheader, as with every Public API request.- The timesheet must be clocked out. Segments for a shift still in progress aren't available.
Request
curl --request GET \
--url https://api.timeero.app/api/public/segments/3018106 \
--header 'Authorization: YOUR_API_KEY' \
--header 'Accept: application/json'timesheetId is the id of a timesheet, as returned by the timesheets endpoints.
How the request flow works
Segments are generated once and then stored. Most clocked-out timesheets already have them, so the first request usually returns them. When they haven't been generated yet, the API starts generating them in the background and asks you to come back.
| Status | Meaning | What to do |
|---|---|---|
200 | Segments are ready and included in the response. | Use them. |
202 | Segments are being generated for this timesheet. | Repeat the same request after 5 to 10 seconds. |
429 | Your company is already generating segments for a different timesheet. | Wait and retry. Each company generates one timesheet at a time. |
500 with "Segment generation failed" | Generation failed for this timesheet. | Retry after an hour. Until then the same error is returned. |
One timesheet at a timeIf you need segments for many timesheets, request them one after another: wait for a
200on one before moving to the next. Requesting several at once only returns429for all but the first.
Polling example
async function getSegments(timesheetId, apiKey) {
const url = `https://api.timeero.app/api/public/segments/${timesheetId}`;
for (let attempt = 0; attempt < 30; attempt++) {
const response = await fetch(url, {
headers: { Authorization: apiKey, Accept: 'application/json' },
});
if (response.status === 200) {
return (await response.json()).items;
}
// 202: still generating. 429: another timesheet is generating.
if (response.status === 202 || response.status === 429) {
await new Promise((resolve) => setTimeout(resolve, 10000));
continue;
}
const body = await response.json();
throw new Error(`${response.status}: ${body.message ?? body.error_message}`);
}
throw new Error('Segments were not ready in time. Try again later.');
}Reading the response
A 200 response wraps the segments in items:
{
"items": {
"timesheet_id": 3018106,
"job_name": "Riverside Office",
"clock_in_time": "2026-10-27T08:00:12",
"clock_out_time": "2026-10-27T10:05:40",
"total_duration": 7528,
"duration_human": "02h 05m",
"total_break_duration": "15m",
"driving_distance": 12.43,
"mileage": 12.43,
"segments": [ ... ]
},
"message": "Segments loaded successfully."
}
Times and units
- Times are the employee's local time, in the format
YYYY-MM-DDTHH:mm:ss, with no UTC offset.total_durationandduration_in_secondsare in seconds.driving_distanceandmileageare always in miles. A driving segment'sdistanceuses your company's unit (kilometers or miles), anddistance_in_kmanddistance_in_milesgive both.
Fields summary
| Field | Type | Description |
|---|---|---|
timesheet_id | integer | The timesheet the segments belong to. |
job_name | string | Name of the timesheet's job. Empty when the timesheet has no job. |
clock_in_time | string | Local clock-in time. |
clock_out_time | string | Local clock-out time. |
total_duration | integer | Length of the shift, in seconds. |
duration_human | string | Length of the shift for display, for example 02h 05m, or 45m 10s for shifts under an hour. |
total_break_duration | string or 0 | Total break time for display, for example 15m. 0 when no breaks were taken. |
driving_distance | number | Total distance driven, in miles. |
mileage | number | Same value as driving_distance. |
segments | array | The segments, in the order they happened. |
Segment categories
category | What it means |
|---|---|
ClockIn | Where and when the shift started. |
Driving | The employee was moving. Includes the distance driven. |
Onsite | The employee was stopped at a job site. |
Idling | The employee was stopped somewhere other than a job site. |
Onbreak | The employee was on a break. |
ClockOut | Where and when the shift ended. |
Segment fields
Every segment has the same fields. Fields that don't apply to a category are null, or an empty array for breaks.
| Field | Type | Description |
|---|---|---|
category | string | One of the categories above. |
start_time | string or null | Local start time. null for ClockOut. |
end_time | string or null | Local end time. null for ClockIn. |
duration_in_seconds | integer or null | Length of the segment. null for ClockIn and ClockOut. |
duration_human | string or null | Length of the segment for display, for example 12m 30s. |
timezone | string or null | Timezone abbreviation, for example EST. Only on ClockIn and ClockOut, and may be null. |
address | string or null | Street address. Only on ClockIn and ClockOut. |
location | object or null | The recorded point. Only on ClockIn and ClockOut. See Location. |
site | object, string or null | The job site for Onsite and Idling segments. See Site. For an Idling segment with no matching site, this is the address as text. |
distance | number or null | Distance driven, in your company's unit. Only on Driving. |
distance_in_km | number or null | Distance driven, in kilometers. Only on Driving. |
distance_in_miles | number or null | Distance driven, in miles. Only on Driving. |
breaks | array | Breaks taken during the segment. See Breaks. |
Breaks
| Field | Type | Description |
|---|---|---|
name | string | Name of the break. |
start_time | string | Local start time of the part of the break inside this segment. |
end_time | string | Local end time of the part of the break inside this segment. |
duration | string | Length for display, for example 30 m or 01 h:15 m. |
full | boolean | true when the whole break falls inside this segment. false when it continues into a neighbouring segment. |
paid | boolean | Whether the break is paid. |
Location
| Field | Type | Description |
|---|---|---|
latitude | number | Latitude of the point. |
longitude | number | Longitude of the point. |
location_time | string | When the point was recorded. |
speed | number | Speed at the point, in your company's unit (km/h or mph). |
actual_speed | number | Speed as reported by the device, in meters per second. |
Site
| Field | Type | Description |
|---|---|---|
id | integer | The job's ID. |
name | string | The job's name. |
radius | number | Geofence radius of the site. |
location | object | Center of the site. See Location. |
has_custom_geofence | boolean | Whether the site uses a custom geofence shape instead of a radius. |
geofence_coordinates | array or null | The custom geofence as [latitude, longitude] pairs. null when there's no custom geofence. |
Segments generated a long time ago may include fewer site fields.
Errors
Errors return an error_code and a message. Authentication errors use error_message instead of message.
| Status | Message | Cause |
|---|---|---|
401 | Invalid api key. | The Authorization header is missing or the key is wrong. |
404 | Timesheet not found. | No timesheet with this ID belongs to your company. |
404 | No segments found for this timesheet. | The timesheet has no recorded locations, so it has no segments. |
404 | The route … could not be found. | timesheetId isn't a whole number of up to 18 digits. |
422 | Segmented tracking is not enabled for this company. | Your company doesn't have the Segmented Tracking add-on. |
422 | Segments are available once the timesheet is clocked out. | The timesheet is still in progress. |
429 | Segments for another timesheet are being generated. Retry shortly. | Your company is already generating another timesheet's segments. |
500 | Segment generation failed for this timesheet. Try again later. | Generation failed. Retry after an hour. |