Fetching Data
Learn how to filter and paginate the Kombo GET
endpoints. This will help you understand how to implement syncs and more.
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.
- This will occur for remote systems that follow a sequential ID pattern. For example, both the first employee and department have the 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.
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 thechanged_at
value is after the given timestamp. This can be used to sync 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 aremote_deleted_at
value are not returned. If you want to include them, set this totrue
. 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.
Rate limiting
The rate limiter restricts the number of API requests you can send to Kombo. Currently, we permit 300 requests over 60 seconds. After the time has passed, the limit is reset.
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 a 429
(“Too Many Requests”) status code:
{
"status": "error",
"error": {
"message": "Rate limit exceeded. Maximum requests are 300 every 60 seconds"
}
}
Handling rate limiting
The simplest way of handling rate limits is to watch for 429 status codes. If you receive one, you can implement retry logic that sends the same API request with an exponentially increasing wait time between each retry.
For a more sophisticated approach, you could parse the headers mentioned above and use the information about remaining requests and reset time to ensure you never encounter the rate limit. This could be useful if, for example, you never want a POST
request to fail; you could globally keep track of the ratelimit-remaining
value and keep a certain buffer of requests available to only be used by POST
requests.