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

# Pagination, errors & rate limits

> The list envelope, the error envelope, and how rate limiting behaves.

<Note>
  This page documents the **wire contract** - what the REST API itself returns. You'll mostly meet it translated: the **SDKs** turn the envelope into typed errors and auto-paginate for you ([SDK errors & pagination](/developers/sdk/errors-pagination)), and the **CLI** maps it onto exit codes and JSON output ([machine contract](/developers/cli/machine-contract)). Read this page to understand what those layers are built on - and because `errorCode` values and rate-limit behavior are identical everywhere.
</Note>

## Pagination

List endpoints take two query parameters and return a consistent envelope:

| Param | Meaning | Default |
| - | - | - |
| `offset` | Zero-based index of the first item to return | `0` |
| `top` | Maximum number of items to return | `100` (connections list: `25`) |

```json theme={null}
{
  "items": [ ... ],
  "totalCount": 137,
  "offset": 0,
  "top": 100
}
```

All four fields are always present. Page until `offset + items.length >= totalCount`. Both SDKs auto-paginate internally - their `list`-style methods return the complete array.

## Error envelope

**Every** non-2xx response carries the same JSON body - including credential rejections and unmatched routes:

```json theme={null}
{
  "errorCode": "VALIDATION_ERROR",
  "message": "Human-readable error message.",
  "requestId": "0HN0...",
  "timestamp": "2026-08-12T10:00:00Z",
  "details": [
    { "field": "fields.name", "issue": "Required field is missing." }
  ]
}
```

`details` appears only on field-level validation failures (`400`, `errorCode: "VALIDATION_ERROR"`).

| Status | Meaning |
| - | - |
| 400 | Bad request - malformed body or invalid parameters |
| 401 | Unauthorized - missing or invalid credentials |
| 403 | Forbidden - the caller lacks access to the resource |
| 404 | Not found - the resource does not exist |
| 409 | Conflict - e.g. no default connection configured for the tool's application |
| 429 | Too many requests - rate limit exceeded |
| 500 | Internal server error - include `requestId` when reporting |

## Error codes

Branch on `errorCode` rather than parsing `message` - messages may be reworded, codes are contract. The ones you're most likely to handle:

| `errorCode` | Status | Meaning |
| - | - | - |
| `VALIDATION_ERROR` | 400 | Request failed validation - see `details[]` for the offending fields |
| `MALFORMED_BODY` | 400 | Body missing or unparseable |
| `WRONG_APPLICATION` | 400 | The connection you passed belongs to a different application than the tool |
| `NOT_FOUND` | 404 | The connection, tool, or toolset doesn't exist (or isn't yours) |
| `NO_DEFAULT_CONNECTION` | 409 | No default connection for the tool's application - pass `?connectionId=` or set a default |
| `NO_TOOLSET_CONNECTION` | 409 | The toolset has no connection for the tool's application |
| `TOOL_NOT_IN_TOOLSET` | 403 | The tool isn't in the toolset's allow-list |
| `CONNECTION_NOT_IN_TOOLSET` | 403 | The connection isn't part of the toolset you scoped to |
| `ACCOUNT_USER_INACTIVE` | 403 | The acting user is no longer active on the account |
| `CONNECTION_BLOCKED` | 403 | The resolved connection is blocked (no licence, or licence expired); the tool was not executed |
| `UNAUTHORIZED` | 401 | Missing or invalid credential |
| `RATE_LIMIT_EXCEEDED` | 429 | Rate limit tripped - honor `Retry-After` |
| `UNSUPPORTED_MEDIA_TYPE` | 415 | Request `Content-Type` isn't supported |

New codes may be added over time - treat an unrecognized `errorCode` as a generic failure of its HTTP status rather than erroring out.

<Note>
  Every response also carries an **`x-request-id`** header, echoed in the body's `requestId`. Quote it when contacting support - it's the fastest way for us to find your exact request.
</Note>

<Note>
  `POST /v1/tools/{toolSlug}/execute` is special: **tool-runtime** failures (the downstream API errored) return `200` with `isSuccess: false` and an `errorMessage` - not the error envelope. Branch on `isSuccess`, not just the HTTP status.
</Note>

## Rate limits

Limits are enforced **per account** with two budgets that must both pass: a per-second burst and a sustained hourly window. Search-backed discovery calls (`GET /v1/tools` or `GET /v1/applications` with a non-empty `?search=`) have their own, stricter budget because they trigger semantic search.

On `429`, honor the `Retry-After` response header (seconds): `1` when the burst window tripped, `3600` when the hourly window tripped. Back off and retry - don't hammer.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.