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 - Each item in
data.requests[]uses the same shape as the Create time off request (POST) response
Reuse your POST parserEach object in
data.requests[]matches the Create time off request (POST) response. If you already parse POST responses in your integration, reuse that model for GET results.
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",
"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
}
}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[].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,
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.- 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.
Updated 1 day ago
