Get Time Off Requests
Retrieve time-off requests for your company within a date range. Use this endpoint to sync approved leave into payroll systems, surface pending approvals in your HRIS, or audit denied requests — without workarounds through other APIs.
Endpoint
| Method | Endpoint | Description |
|---|---|---|
| GET | /time-off/v1/requests | Get a paginated list of time-off requests |
Overview
Returns every time-off request whose date range overlaps the window you pass in startDate and endDate (both inclusive). For example, a request from June 10–12 is returned when you query June 1–30, and also when you query June 11–11 alone.
- Results are paginated with
limit(1–100, default 10) andoffset - Filter by employee with
userIds - Filter by status with
statuses(approved,pending,denied) - When
statusesis omitted, onlyapprovedrequests are returned - Approved requests include a
durationfield with the amount deducted from the employee's balance
duration field for payrollApproved requests include a
durationobject (units+amount) reflecting exactly how much balance was deducted when the request was approved — in the policy's native unit (hours or days). This value is frozen at approval time, so it remains accurate even if the policy schedule changes later. It is omitted forpendinganddeniedrequests.
Date range limits
startDateandendDateare required.endDatemust be on or afterstartDate. The range may not exceed 365 days.
Authentication
- API Key (
X-API-KEYheader), or - OAuth 2.0 with scope
time_off.read
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| startDate | string (YYYY-MM-DD) | Yes | — | Start of the date range, inclusive. Any request overlapping [startDate, endDate] is returned. |
| endDate | string (YYYY-MM-DD) | Yes | — | End of the date range, inclusive. Must be on or after startDate. Range may not exceed 365 days. |
| userIds | array of integers | No | all users | Filter by one or more employee IDs. Omit to include all employees. |
| statuses | array of enum | No | ["approved"] | Filter by status. Allowed: approved, pending, denied. Repeat the param for multiple values. |
| limit | integer | No | 10 | Max results to return. Range 1–100. |
| offset | integer | No | 0 | Number of results to skip for pagination. Min 0. |
Example Request — Approved requests for a pay period
curl --request GET \
--url 'https://api.connecteam.com/time-off/v1/requests?startDate=2026-06-01&endDate=2026-06-30&limit=25&offset=0' \
--header 'X-API-KEY: YOUR_API_KEY'Example Request — Pending requests for specific employees
curl --request GET \
--url 'https://api.connecteam.com/time-off/v1/requests?startDate=2026-06-01&endDate=2026-06-30&userIds=12345&userIds=67890&statuses=pending&statuses=approved' \
--header 'X-API-KEY: YOUR_API_KEY'Response Structure
{
"requestId": "0b1c2d3e-4f56-7890-abcd-ef0123456789",
"data": {
"requests": [
{
"id": "665f1a2b3c4d5e6f7a8b9c0d",
"userId": 12345,
"policyTypeId": "63a1b2c3d4e5f60718293a4b",
"status": "approved",
"isAllDay": true,
"startDate": "2026-06-10",
"endDate": "2026-06-12",
"startTime": "00:00:00",
"endTime": "23:59:59",
"timezone": "America/New_York",
"duration": {
"units": "hours",
"amount": 22.0
},
"employeeNote": "Family trip",
"managerNote": "Approved, enjoy!",
"timeClockId": 4815162342
},
{
"id": "665f1a2b3c4d5e6f7a8b9c0e",
"userId": 67890,
"policyTypeId": "63a1b2c3d4e5f60718293a4b",
"status": "pending",
"isAllDay": false,
"startDate": "2026-06-15",
"endDate": "2026-06-15",
"startTime": "09:00:00",
"endTime": "13:00:00",
"timezone": "America/New_York",
"employeeNote": "Doctor appointment",
"managerNote": ""
}
]
},
"paging": {
"offset": 2,
"total": 2
}
}duration is present only on approved requests (see the first item above). timeClockId is omitted when the request is not linked to a time clock (see the second item above).
Response Fields
| Field | Type | Description |
|---|---|---|
| requestId | string | Unique identifier for this API request. |
| data.requests | array | List of time-off requests matching the filters. |
| data.requests[].id | string | Unique identifier of the time-off request. |
| data.requests[].userId | integer | ID of the employee the request belongs to. |
| data.requests[].policyTypeId | string | ID of the time-off policy type. |
| data.requests[].status | enum | Request status: approved, pending, or denied. |
| data.requests[].isAllDay | boolean | true if the request covers full days; false if it uses specific times. |
| data.requests[].startDate | string | Start date of the time off (YYYY-MM-DD). |
| data.requests[].endDate | string | End date of the time off (YYYY-MM-DD). |
| data.requests[].startTime | string | Start time of the time off (HH:MM:SS). |
| data.requests[].endTime | string | End time of the time off (HH:MM:SS). |
| data.requests[].timezone | string | Timezone of the request, e.g. America/New_York. |
| data.requests[].duration | object | Amount deducted from the employee's balance when the request was approved. Present only for approved requests; omitted otherwise. |
| data.requests[].duration.units | enum | Unit the amount is expressed in: hours or days, matching the policy type. |
| data.requests[].duration.amount | number | Amount of time deducted. For all-day requests this reflects the policy work schedule per calendar day, not the start/end time span. Frozen at approval time. |
| data.requests[].employeeNote | string | Note added by the employee. Empty string when none. |
| data.requests[].managerNote | string | Note added by the manager. Empty string when none. |
| data.requests[].timeClockId | integer | ID of the time clock where the time off appears on the timesheet. Omitted when not linked. |
| paging.offset | integer | Position after the last returned record; pass as offset to fetch the next page. |
| paging.total | integer | Total number of requests matching the filters, ignoring pagination. |
Pagination
Use limit and offset to walk through large result sets:
- Call with
offset=0and your chosenlimit. - Read
paging.totalto know how many matching requests exist. - If more results remain, set
offsetto thepaging.offsetvalue from the previous response and call again.
async function fetchAllApprovedTimeOff(startDate, endDate) {
const all = [];
let offset = 0;
const limit = 100;
while (true) {
const params = new URLSearchParams({
startDate,
endDate,
limit: String(limit),
offset: String(offset),
});
const res = await fetch(
`https://api.connecteam.com/time-off/v1/requests?${params}`,
{ headers: { 'X-API-KEY': 'YOUR_API_KEY' } }
);
if (!res.ok) throw new Error(await res.text());
const body = await res.json();
all.push(...body.data.requests);
if (body.paging.offset >= body.paging.total) break;
offset = body.paging.offset;
}
return all;
}Error Codes
| HTTP Status | Description |
|---|---|
400 | endDate earlier than startDate, date range exceeds 365 days, or a userId does not exist. |
401 | Missing or invalid authentication token. |
403 | Token lacks the time_off.read scope, or the plan does not include the Time Off API. |
429 | Rate limit exceeded. |
Integration Example — Sync approved leave to payroll
async function syncApprovedLeaveForPeriod(startDate, endDate) {
const params = new URLSearchParams({
startDate,
endDate,
statuses: 'approved',
limit: '100',
offset: '0',
});
const res = await fetch(
`https://api.connecteam.com/time-off/v1/requests?${params}`,
{ headers: { 'X-API-KEY': 'YOUR_API_KEY' } }
);
if (!res.ok) throw new Error(await res.text());
const { data, paging } = await res.json();
for (const request of data.requests) {
await pushToPayroll({
employeeId: request.userId,
startDate: request.startDate,
endDate: request.endDate,
isAllDay: request.isAllDay,
startTime: request.startTime,
endTime: request.endTime,
policyTypeId: request.policyTypeId,
// duration is present for all approved requests
durationAmount: request.duration.amount,
durationUnits: request.duration.units,
note: request.employeeNote,
});
}
return paging.total;
}Notes
Important Considerations
- Overlap, not containment: A request is returned if any part of its date range falls inside your query window — not only when it is fully contained.
- Default status filter: Omitting
statusesreturns approved requests only. Passstatuses=pending(or repeat the param for multiple values) to include other statuses.durationis frozen at approval: The amount and units reflect the policy schedule at the moment the request was approved. Policy schedule changes after approval do not affect the stored value.durationabsent for non-approved:pendinganddeniedrequests do not includeduration. Checkstatus === "approved"before reading it.- Permissions: Results respect the authenticated user's Time Off permissions. Users without broad visibility may see a subset of company requests.
- Related endpoints: Use Create and update time off requests (POST/PUT) to write requests, and Policy Types & Balances to manage balances.
