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 Authorization header, 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.

StatusMeaningWhat to do
200Segments are ready and included in the response.Use them.
202Segments are being generated for this timesheet.Repeat the same request after 5 to 10 seconds.
429Your 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 time

If you need segments for many timesheets, request them one after another: wait for a 200 on one before moving to the next. Requesting several at once only returns 429 for 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_duration and duration_in_seconds are in seconds.
  • driving_distance and mileage are always in miles. A driving segment's distance uses your company's unit (kilometers or miles), and distance_in_km and distance_in_miles give both.

Fields summary

FieldTypeDescription
timesheet_idintegerThe timesheet the segments belong to.
job_namestringName of the timesheet's job. Empty when the timesheet has no job.
clock_in_timestringLocal clock-in time.
clock_out_timestringLocal clock-out time.
total_durationintegerLength of the shift, in seconds.
duration_humanstringLength of the shift for display, for example 02h 05m, or 45m 10s for shifts under an hour.
total_break_durationstring or 0Total break time for display, for example 15m. 0 when no breaks were taken.
driving_distancenumberTotal distance driven, in miles.
mileagenumberSame value as driving_distance.
segmentsarrayThe segments, in the order they happened.

Segment categories

categoryWhat it means
ClockInWhere and when the shift started.
DrivingThe employee was moving. Includes the distance driven.
OnsiteThe employee was stopped at a job site.
IdlingThe employee was stopped somewhere other than a job site.
OnbreakThe employee was on a break.
ClockOutWhere 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.

FieldTypeDescription
categorystringOne of the categories above.
start_timestring or nullLocal start time. null for ClockOut.
end_timestring or nullLocal end time. null for ClockIn.
duration_in_secondsinteger or nullLength of the segment. null for ClockIn and ClockOut.
duration_humanstring or nullLength of the segment for display, for example 12m 30s.
timezonestring or nullTimezone abbreviation, for example EST. Only on ClockIn and ClockOut, and may be null.
addressstring or nullStreet address. Only on ClockIn and ClockOut.
locationobject or nullThe recorded point. Only on ClockIn and ClockOut. See Location.
siteobject, string or nullThe job site for Onsite and Idling segments. See Site. For an Idling segment with no matching site, this is the address as text.
distancenumber or nullDistance driven, in your company's unit. Only on Driving.
distance_in_kmnumber or nullDistance driven, in kilometers. Only on Driving.
distance_in_milesnumber or nullDistance driven, in miles. Only on Driving.
breaksarrayBreaks taken during the segment. See Breaks.

Breaks

FieldTypeDescription
namestringName of the break.
start_timestringLocal start time of the part of the break inside this segment.
end_timestringLocal end time of the part of the break inside this segment.
durationstringLength for display, for example 30 m or 01 h:15 m.
fullbooleantrue when the whole break falls inside this segment. false when it continues into a neighbouring segment.
paidbooleanWhether the break is paid.

Location

FieldTypeDescription
latitudenumberLatitude of the point.
longitudenumberLongitude of the point.
location_timestringWhen the point was recorded.
speednumberSpeed at the point, in your company's unit (km/h or mph).
actual_speednumberSpeed as reported by the device, in meters per second.

Site

FieldTypeDescription
idintegerThe job's ID.
namestringThe job's name.
radiusnumberGeofence radius of the site.
locationobjectCenter of the site. See Location.
has_custom_geofencebooleanWhether the site uses a custom geofence shape instead of a radius.
geofence_coordinatesarray or nullThe 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.

StatusMessageCause
401Invalid api key.The Authorization header is missing or the key is wrong.
404Timesheet not found.No timesheet with this ID belongs to your company.
404No segments found for this timesheet.The timesheet has no recorded locations, so it has no segments.
404The route … could not be found.timesheetId isn't a whole number of up to 18 digits.
422Segmented tracking is not enabled for this company.Your company doesn't have the Segmented Tracking add-on.
422Segments are available once the timesheet is clocked out.The timesheet is still in progress.
429Segments for another timesheet are being generated. Retry shortly.Your company is already generating another timesheet's segments.
500Segment generation failed for this timesheet. Try again later.Generation failed. Retry after an hour.