Draft API. Endpoints may change before general availability.

API basics

Eight rules that apply to every request. Read this once and the rest of the API will feel predictable.

1. Making a request

Send everything to https://api.eventtickets.dev/v1. Put your secret key in the Authorization header, and send JSON bodies with Content-Type: application/json. Every response is JSON.

Every request

curl https://api.eventtickets.dev/v1/events \
  -H "Authorization: Bearer sk_test_..."

Keep keys on your server. Anyone with your key can act as your account, so never put it in a web page, an app, or a public code repository. If one leaks, revoke it in Developers and make a new one.

2. Test mode and live mode

Your account has two separate sets of data. A sk_test_ key only sees test events, orders and tickets; a sk_live_ key only sees live ones. Every object has "livemode": true or false so you can tell them apart. Build and try everything with a test key, then swap the key.

3. Money and dates

Amounts are whole numbers in the smallest unit of the currency. For pounds that's pence: 1200 is £12.00 and 50 is 50p. There are no decimals anywhere.

Dates are ISO 8601 with a timezone, like 2026-11-14T22:00:00Z (the Z means UTC). We always reply in UTC.

4. Key permissions

A key can have full access, or only the permissions you tick when you create it. Give your website the least it needs: a site that only sells tickets needs events:read and checkout:write. A :write permission includes reading the same things.

events:read / write
Events and their days, ticket types, prices, add-ons and series.
checkout:write
Start checkouts and look them up.
orders:read / write
Orders. Write lets the key refund and resend tickets.
tickets:read / write
Tickets. Write lets the key issue and cancel guest-list tickets.
coupons:read / write
Discount codes.
webhooks:read / write
Webhook endpoints.
branding:read / write
Your branding: colours and wording on checkout and confirmation, contact email and ticket email style.

5. When something goes wrong

Errors always have the same shape: a short code for your program, a message written for people, and param when one field is the problem. The message is safe to log, and usually tells you how to fix it.

400 Bad Request

{
  "error": {
    "code": "invalid_request",
    "message": "Choose between 1 and 10 tickets.",
    "param": "quantity"
  }
}
400 invalid_request
Something in the request is missing or wrong. Check param and message.
401 unauthorized
No key, a mistyped key, or a revoked key.
403 forbidden
The key doesn't have the permission this needs.
404 resource_missing
No such object, or it belongs to the other mode (test vs live).
409 conflict
Allowed in general, but not right now. For example, refunding an unpaid order.
422 idempotency_key_reused
You reused an Idempotency-Key with a different request.
429 rate_limited
Too many requests. Wait for Retry-After seconds.
500 api_error
Our fault. Retry with the same Idempotency-Key.

6. Safe retries

Networks fail. If a request times out you can't tell whether it worked, and sending it again could, say, refund twice. To make a retry safe, add an Idempotency-Key header to POST requests: any unique string, such as your own order number.

If we've seen that key in the last 24 hours, we don't do the work again. We send back the first answer, with the header Idempotent-Replayed: true.

Retrying is now harmless

curl https://api.eventtickets.dev/v1/orders/ord_9c1e4b7a2d3f5e60/refund \
  -X POST \
  -H "Authorization: Bearer $EVENTTICKETS_KEY" \
  -H "Idempotency-Key: refund-ord_9c1e4b7a2d3f5e60"

7. Lists and paging

Anything that returns many objects gives you a page, newest first. Ask for up to 100 with limit (default 20). If has_more is true, ask again with starting_after set to the id of the last object you got.

Fetch every order, a page at a time (Node.js)

let after;
do {
  const url = new URL("https://api.eventtickets.dev/v1/orders");
  url.searchParams.set("limit", "100");
  if (after) url.searchParams.set("starting_after", after);

  const page = await fetch(url, { headers: { Authorization: `Bearer ${key}` } }).then((r) => r.json());
  for (const order of page.data) console.log(order.id, order.amount_total);

  after = page.has_more ? page.data.at(-1).id : undefined;
} while (after);

8. Rate limits

Each live key can make 300 requests a minute, and each test key 100. Every response tells you where you are with X-RateLimit-Remaining and X-RateLimit-Reset. Go over and you get a 429 with a Retry-After header saying how many seconds to wait.

Tip: you rarely need to poll the API. Webhooks tell you the moment an order is paid or a ticket is scanned. See Webhooks.