Requests and responses
Every endpoint answers in the same shape, which means your integration can have one place that unwraps a response and one place that handles a failure.
The envelope
Section titled “The envelope”A successful call:
{ "success": true, "data": {}, "request_id": "req_abc123", "timestamp": "2026-09-07T12:00:00Z"}Read success first, then data. A failure carries an error object instead — see errors.
Keep the request_id. It identifies that one call in the server’s own logs, and it is the first thing support will ask for. Store it against whatever record your system created, alongside the ID that came back.
Database IDs come back as JSON numbers, not strings — "id": 501, not "id": "501". Your own external IDs stay strings. A language that guesses types will get this right; a schema that expects strings will not.
Pagination
Section titled “Pagination”List and lookup endpoints add a pagination block outside data:
{ "success": true, "data": [], "pagination": { "index": 0, "limit": 50, "returned": 2, "total": 2, "has_more": false }, "request_id": "req_abc123", "timestamp": "2026-09-07T12:00:00Z"}Paging is optional — ask for nothing and you get the first 50.
| Parameter | Default | Allowed |
|---|---|---|
| index | 0 | A zero-based record offset, not a page number. Non-negative. |
| limit | 50 | 1 to 200. |
GET {BASE_URL}/countries?index=0&limit=50GET {BASE_URL}/countries?index=50&limit=50index counts records, not pages. The second call above starts at the 51st record; it is not “page 50”. This is the mistake worth checking for in your code, because passing a page number quietly returns the wrong slice rather than an error.
Use has_more to decide whether to ask again, rather than comparing counts yourself. total is how many records match altogether, not how many came back in this response — that is returned.
Dates and times
Section titled “Dates and times”- Every datetime you send must be ISO-8601 with an offset or
Z—2026-09-10T10:00:00+05:30or2026-09-10T04:30:00Z. A datetime with no zone is rejected rather than guessed at. - Every datetime you receive is UTC, ending in
Z.
So a job scheduled for 10 a.m. in Mumbai is sent as +05:30 and comes back as 04:30:00Z. Both are the same moment; convert for display rather than storing what you were shown and treating it as local.
Rate limits
Section titled “Rate limits”Fixed for every credential:
- 60 requests per minute
- 1000 requests per hour
Every authenticated response tells you where you stand:
X-RateLimit-Minute-Limit: 60X-RateLimit-Minute-Remaining: 59X-RateLimit-Minute-Reset: 1788789600X-RateLimit-Hour-Limit: 1000X-RateLimit-Hour-Remaining: 999X-RateLimit-Hour-Reset: 1788789600Exceed either and the answer is 429, with a Retry-After header in seconds:
Retry-After: 42Wait that long and retry with the same idempotency key — see idempotency and retries. Reading the remaining counts and slowing down before you hit the wall is better than being told to stop, particularly on a nightly batch that pushes a day’s work in one go.