Requests and Responses
These conventions apply to every endpoint. Read this page once and the individual endpoint guides become much shorter.
Base URL and versioning
All endpoints live under:
https://developer.seaty.co.uk/v1
The /v1 prefix is the version. Breaking changes would be introduced under a new version, never by changing /v1 underneath you.
Pagination
List endpoints are paginated with two query parameters:
| Parameter | Default | Notes |
|---|---|---|
page | 1 | The page number, starting at 1. |
per_page | 10 | Records per page. Maximum 100. |
curl "https://developer.seaty.co.uk/v1/events?page=2&per_page=50" \
-H "Authorization: Bearer sk_live_your_key_here"
Every list response uses the same envelope:
{
"data": [ ... your records ... ],
"links": {
"first": "https://developer.seaty.co.uk/v1/events?page=1&per_page=50",
"last": "https://developer.seaty.co.uk/v1/events?page=5&per_page=50",
"prev": null,
"next": "https://developer.seaty.co.uk/v1/events?page=3&per_page=50"
},
"meta": {
"current_page": 2,
"per_page": 50,
"from": 51,
"to": 100,
"total": 230,
"path": "/v1/events"
}
}
datais the array of records for this page.links.nextisnullon the last page;links.previsnullon the first. Follownextuntil it isnullto walk every page.meta.totalis the full count across all pages.
Single-record endpoints (for example "get one order") return the record directly, with no envelope.
Money is in minor units
All amounts are integers in the smallest currency unit (pence for GBP). There are no decimal money values anywhere in the API. So 1850 means £18.50. To display it, divide by 100. Each event also tells you its currency_symbol.
This avoids rounding errors and is consistent across every field: ticket prices, discounts, order totals, payments and refunds are all integer pence.
Totals are always broken into their parts
Wherever the API gives you a composite total, it also gives you the parts that make it up, so you never have to guess how a number was reached. For example an order exposes tickets_subtotal, discount_total and total, and separately amount_paid, amount_refunded and balance. You can always reconcile a headline figure against its components.
Dates and times
Dates and timestamps are ISO 8601 strings, for example 2026-06-04T19:30:00Z. Timestamps are in UTC. Where an event has a timezone it is given separately as time_zone_id so you can present local times correctly.
Errors
Errors use a consistent shape and standard HTTP status codes:
{
"error": {
"code": "not_found",
"message": "Order not found."
}
}
| Status | code | Meaning |
|---|---|---|
| 400 | bad_request | The request was malformed. |
| 401 | unauthorized | Missing or invalid API key. |
| 403 | forbidden | The key is missing a required scope (for example a read-only key trying to write). |
| 404 | not_found | No such record in your organisation (it may exist for another org; you can't see it). |
| 409 | conflict | A balance payment with that idempotency key is already being processed. |
| 422 | unprocessable_entity | The request was valid but could not be applied (for example a refund exceeding the amount paid). |
| 429 | rate_limited | You are sending requests too quickly (see below). |
| 502 | upstream_error | A temporary problem reaching a downstream service. Safe to retry shortly. |
The API never returns stack traces or internal details in error messages.
Rate limits
Requests are limited per key:
| Limit | Short burst | |
|---|---|---|
| Reads | 60 per minute | up to 30 back-to-back |
| Balance-payment writes | 30 per minute | up to 10 back-to-back |
Responses include X-RateLimit-Limit and X-RateLimit-Remaining headers. If you exceed the limit you get 429 rate_limited with a Retry-After header telling you how many seconds to wait. Build in a short pause and retry, and prefer a larger per_page over many small requests when reading lots of data.
Next
Need help? Email support@seaty.co.uk.