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

MethodEndpointDescription
GET/time-off/v1/requestsGet 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) and offset
  • Filter by employee with userIds
  • Filter by status with statuses (approved, pending, denied)
  • When statuses is omitted, only approved requests are returned
  • Each item in data.requests[] uses the same shape as the Create time off request (POST) response
👍

Reuse your POST parser

Each 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

startDate and endDate are required. endDate must be on or after startDate. The range may not exceed 365 days.


Authentication

  • API Key (X-API-KEY header), or
  • OAuth 2.0 with scope time_off.read

Query Parameters

ParameterTypeRequiredDefaultDescription
startDatestring (YYYY-MM-DD)YesStart of the date range, inclusive. Any request overlapping [startDate, endDate] is returned.
endDatestring (YYYY-MM-DD)YesEnd of the date range, inclusive. Must be on or after startDate. Range may not exceed 365 days.
userIdsarray of integersNoall usersFilter by one or more employee IDs. Omit to include all employees.
statusesarray of enumNo["approved"]Filter by status. Allowed: approved, pending, denied. Repeat the param for multiple values.
limitintegerNo10Max results to return. Range 1–100.
offsetintegerNo0Number 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

FieldTypeDescription
requestIdstringUnique identifier for this API request.
data.requestsarrayList of time-off requests matching the filters.
data.requests[].idstringUnique identifier of the time-off request.
data.requests[].userIdintegerID of the employee the request belongs to.
data.requests[].policyTypeIdstringID of the time-off policy type.
data.requests[].statusenumRequest status: approved, pending, or denied.
data.requests[].isAllDaybooleantrue if the request covers full days; false if it uses specific times.
data.requests[].startDatestringStart date of the time off (YYYY-MM-DD).
data.requests[].endDatestringEnd date of the time off (YYYY-MM-DD).
data.requests[].startTimestringStart time of the time off (HH:MM:SS).
data.requests[].endTimestringEnd time of the time off (HH:MM:SS).
data.requests[].timezonestringTimezone of the request, e.g. America/New_York.
data.requests[].employeeNotestringNote added by the employee. Empty string when none.
data.requests[].managerNotestringNote added by the manager. Empty string when none.
data.requests[].timeClockIdintegerID of the time clock where the time off appears on the timesheet. Omitted when not linked.
paging.offsetintegerPosition after the last returned record; pass as offset to fetch the next page.
paging.totalintegerTotal number of requests matching the filters, ignoring pagination.

Pagination

Use limit and offset to walk through large result sets:

  1. Call with offset=0 and your chosen limit.
  2. Read paging.total to know how many matching requests exist.
  3. If more results remain, set offset to the paging.offset value 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 StatusDescription
400endDate earlier than startDate, date range exceeds 365 days, or a userId does not exist.
401Missing or invalid authentication token.
403Token lacks the time_off.read scope, or the plan does not include the Time Off API.
429Rate 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 statuses returns approved requests only. Pass statuses=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.

API Reference