> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kombo.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Querying the API

> Learn how to filter and paginate the Kombo `GET` endpoints. This will help you implement incremental fetching and more.

## SDKs

You can use our officially supported SDKs to interact with our API. For more information, see the [Libraries and SDKs](/libraries-and-sdks) section.

## General Response Format

All *GET* endpoints of Kombo follow the same principle. All of them return a list
of results and a next cursor. All results follow the same basic structure with the
following properties:

* `id`: An ID generated by Kombo, also referred to throughout the documentation as "Kombo ID".
* `remote_id`: The ID of the object in the remote system. This can be null for some objects.
* `changed_at`: Part of Kombo's change tracking — this is the timestamp of the last change that was detected by Kombo.
* `remote_deleted_at`: Part of Kombo's change tracking — this is the timestamp where the object was not found anymore in the remote system.

*Other properties are model-specific.*

### A note on Kombo IDs and their uniqueness

We generate the Kombo ID by referencing the remote ID and the integration ID.

This results in the following statements being true:

* A Kombo ID is unique across all integrations for *the same model*.
  * This is because we reference the integration ID itself when generating the Kombo ID.
* A Kombo ID is *not* unique across different models for the same integration.
  * This will occur for remote systems that follow a sequential ID pattern. For example, both the first employee and department have the ID `1`. As the integration ID is the same, so will the Kombo ID.

## Pagination

Each request returns either a value on the `next` field or `null`. The
value will be a cursor which you can use to get the next page of results. Use
the `cursor` query parameter to request the next page. (Please note that you
still need to add the relevant filters while paginating.)

In addition, you can use the `page_size` parameter to specify the number of results per page.

```bash theme={null}
curl --request GET \
  --url 'https://api.kombo.dev/v1/hris/employees?cursor=eyJwYWdlIjoxMiwibm90ZSI6InRoaXMgaXMganVzdCBhbiBleGFtcGxlIGFuZCBub3QgcmVwcmVzZW50YXRpdmUgZm9yIGEgcmVhbCBjdXJzb3IhIn0&page_size=100' \
  --header 'Authorization: Bearer <YOUR_API_KEY>'
```

## Filtering

All models provide the same filters in addition to some model-specific filters.
The following filters apply to all models:

* `updated_after`: Only return objects where the `changed_at` value is after
  the given timestamp. This can be used to fetch data incrementally.
  This filter also includes all expanded relations — so if only they have
  changed (but none of the primary attributes of the object), this will still
  count as the object having changed and it will still be returned.
* `include_deleted`: By default, all values with a `remote_deleted_at` value are
  not returned. If you want to include them, set this to `true`. This is helpful
  if you want to remove them from your database.
* `ids`: List of comma-separated ids. Only return objects with these IDs.
* `remote_ids`: List of comma-separated remote ids. Only return objects with
  these remote IDs.

## Error Codes

When the Kombo API encounters an error, it returns a structured error response with a specific `code` for programmatic handling.
For a complete list of error codes and detailed explanations see the [Error Handling guide](/guides/errors).

```json theme={null}
{
  "status": "error",
  "error": {
    "code": "REMOTE.INPUT_INVALID",
    "title": "The remote system returned validation errors.",
    "message": "Candidate field 'email' must be a valid email address for Greenhouse"
  }
}
```

## Rate limiting

The rate limiter restricts the number of API requests you can send to Kombo. Currently, we permit 300 requests over 60 seconds **per environment** (i.e., PROD or DEV environment). After the time window has passed, the limit is reset. If you need a higher quota, reach out to the Kombo team.

Independent of this cap, [concurrency limiting](../guides/concurrency-limiting) limits how many unified actions can run at the same time per integration.

This information is exposed as headers on the response of every authenticated API request:

| Header              | Sample Value | Description                                           |
| ------------------- | ------------ | ----------------------------------------------------- |
| ratelimit-limit     | 300          | The maximum number of requests permitted              |
| ratelimit-remaining | 298          | The remaining number of requests permitted            |
| ratelimit-reset     | 57           | The remaining seconds until the rate limiter is reset |

When exceeded, you will receive a failed response with the [error `code`](/guides/errors) of `PLATFORM.RATE_LIMIT_EXCEEDED`:

```json theme={null}
{
  "status": "error",
  "error": {
    "code": "PLATFORM.RATE_LIMIT_EXCEEDED",
    "title": "Rate limit exceeded.",
    "message": "Maximum requests are 300 every 60 seconds. Try again in 57 seconds.",
    "log_url": null
  }
}
```

### Concurrency limiting

In addition to rate limiting, Kombo applies [concurrency limiting](../guides/concurrency-limiting) to unified actions. While rate limiting caps total requests over a time window, concurrency limiting caps simultaneous in-flight unified actions per integration to prevent backpressure buildup on downstream tools. Endpoints for reading synced model data are not affected by concurrency limiting.

### Handling 429 responses

Both rate limiting and concurrency limiting return HTTP `429`, but they have different error codes and require different responses:

* **`PLATFORM.RATE_LIMIT_EXCEEDED`** -- You've sent too many requests in the current time window. This applies to all endpoints. Wait for `ratelimit-reset` seconds or retry with exponential backoff.
* **`PLATFORM.CONCURRENCY_LIMIT_EXCEEDED`** -- Too many unified actions are in flight for this integration. The underlying tool can't keep up. Retry the failed request after a short backoff (e.g., 1s, 2s, 4s). You likely still have rate limit quota remaining, so waiting for `ratelimit-reset` is unnecessary. See the [concurrency limiting guide](../guides/concurrency-limiting) for details.
