Overview
Errors and pagination
What a non-2xx response carries, which status means what, and how cursors work.
The error body
Every non-2xx response, on every route of your server, carries one shape. Branch on code; show message to people.
application/json
{
"error": {
"code": "risk_rejected",
"message": "10000 contracts is over the 100 limit",
"details": { "rule": "max_order_contracts", "venue": "kalshi", "retryable": false }
}
}| Field | Meaning |
|---|---|
code | Machine-readable, snake_case. The library's exception in snake case (market_not_found, insufficient_funds, order_rejected, risk_rejected, rate_limit_exceeded), or unauthorized, forbidden, not_found, validation_error, unknown_venue |
message | What happened, with the venue's own reason where there is one |
details.venue | The venue that answered, or null when your server refused on its own |
details.retryable | True when the request never got a verdict: a timeout, a rate limit, a venue down. Wait and retry those; do not retry the rest |
details.rule | On risk_rejected: the rule that refused, such as max_order_contracts or kill_switch |
details.errors | On validation_error: one entry per field, {field, message} |
details.retry_after | On a rate limit, when known: seconds to wait. Also sent as the Retry-After header |
Status codes
| Status | Meaning |
|---|---|
400 | Bad parameters: an unknown venue, a status outside the four, an id from another venue. On an order, also the venue's refusal, such as insufficient balance, with its reason |
401 | No key, or an unknown one (trading API) |
403 | The key lacks the permission this route needs |
404 | No such market, order or series |
409 | Refused before sending, by a risk rule or a halt; the message names the rule. Also a market the venue has halted |
422 | The request body does not match the schema: a missing field or a value of the wrong type |
429 | Slow down. The venue's limit, passed through; retryable is true |
501 | The venue has no such capability. Read has on GET /venues first |
502 | The venue failed the request |
504 | The venue did not answer |
Pagination
Lists page by cursor, never by offset: the underlying set changes between calls, and an offset would skip or repeat rows. Pass next_cursor back as cursor, unchanged, with the same filters. null means the venue returned no continuation.
curl "https://api.synpath.dev/venues/kalshi/markets?limit=100"
# { "data": [...], "next_cursor": "eyJjIjoiQ0RJIiwibyI6NX0", "count": 100 }
curl "https://api.synpath.dev/venues/kalshi/markets?limit=100&cursor=eyJjIjoiQ0RJIiwibyI6NX0"Search results are ranked by relevance, so paging deep into a large result set is best effort. To enumerate a whole catalog, walk without query.

