Skip to content

Best practices ​

A one-page checklist for a fast, complete and fair-use integration. Each point links to the page with the details.

Offer the whole category, not one ticket ​

A category (a section of the venue) is shared by all suppliers. One event usually has several tickets in the same category, each from a different supplier, with its own price and stock. If you link a product on your side to a single ticket_id, you only see that one supplier. When it sells out or changes price, your product shows "sold out" while the same seats are still available from other suppliers.

Do this instead:

  1. Map your product to event_id + category_id + sub_category, never to a ticket_id.

  2. Fetch all available tickets of that combination, from all suppliers:

    /v1/tickets?event_id=<event_id>&category_id=<category_id>&ticket_status=available&stock=gt:0
  3. Pick the ticket to sell at checkout time, for example the cheapest one with enough stock for the requested quantity (check the ticket flags too).

  4. Make the reservation with the ticket_id you picked.

Only the ticket_id is supplier-specific. Do not store it as a long-lived reference: new tickets for the same category appear all the time.

See Find events & tickets for how to group tickets.

Fetch many items in one request with in ​

Use the in operator instead of one request per item. This is faster for you and keeps you far away from the rate limit.

/v1/tickets?event_id=in:[<event_id_1>,<event_id_2>,<event_id_3>]&ticket_status=available&stock=gt:0
/v1/categories?category_id=in:[<category_id_1>,<category_id_2>]
/v1/events?sport_type=in:[soccer,formula1]&date_stop=ge:2026-10-21

Rules:

  • Separate values with a comma. Spaces around values are ignored.
  • One filter value can be at most 2000 characters. With our identifiers that is about 50 IDs per in list. Split longer lists over several requests.
  • The full URL must stay under 8 KB.
  • not_in is available too, see Filtering & sorting.

Avoid the "N+1" pattern, for example one /v1/categories/{id} call per ticket. Fetch the categories of the whole event with /v1/categories?event_id=<event_id> in one call.

Filter as much as you can on our side ​

  • Always add a date filter, for example date_stop=ge:2026-10-21 on events and tournaments. Otherwise you also get historical data.
  • On tickets, filter on ticket_status=available and stock=gt:0 unless you really need the unavailable ones.
  • Only request the sports, tournaments or events you sell.

Use pagination ​

List endpoints return 50 items per page by default. Increase page_size to reduce the number of requests.

Do not build the next page URL yourself. The pagination.next_page field holds the complete URL of the next page, with all your filters, page_size and page already in it. Call that URL until next_page is empty (""):

json
{
  "pagination": {
    "total_size": 120,
    "page_size": 50,
    "page_number": 1,
    "next_page": "https://api.xs2event.com/v1/tickets?event_id=<event_id>&ticket_status=available&page_size=50&page=2",
    "previous_page": ""
  }
}

See Formats & conventions.

Cache what is stable, never cache ticket availability ​

Our responses do not carry HTTP cache headers (Cache-Control, ETag), so cache on your side.

ResourceCache locallyRefresh
Sports, tournaments, teams, venuesYesMonthly
CategoriesYesWeekly
EventsYesDaily
Tickets (price, stock, status)NoFetch when needed
  • Always fetch the current tickets right before you show prices or availability in checkout, and before you make a reservation.

  • For a daily sync, fetch only what changed with the updated filter. Use the date of your last sync in YYYY-MM-DD format (a time is not supported):

    /v1/events?updated=ge:<last_sync_date>
    /v1/categories?updated=ge:<last_sync_date>

More details are on Rate limits & caching.

Use webhooks instead of polling orders ​

Subscribe to webhooks to be notified of order and fulfilment changes. If you poll, filter on the status you need (for example logistic_status=completed, see E-tickets & e-invoices) instead of fetching all orders again.

Handle errors and limits ​

  • A 429 means you exceed the rate limit: wait and retry with an increasing delay. Do not retry in a tight loop.
  • A 400 means the request does not match the schema: an unknown query parameter, a wrong type or enum value, or a missing body field. The errors object in the response names each field. Fix the request, do not retry it unchanged.
  • A 422 means the request is well-formed but cannot be processed. This is either a filter value we cannot parse (for example a value longer than 2000 characters, or a date that is not YYYY-MM-DD), or a business rule, such as out-of-stock, pairs-only, invalid-guest-data or reservation-expired. Business rule errors carry a type field: use it to decide what to do, for example pick another ticket on out-of-stock. See Errors & validation.
  • Make the reservation before the booking, and handle an expired reservation by making a new one. See Booking flow.

Next steps ​