API Overview
Integrate the public B2B API to surface tickadoo experiences in your product.
The public B2B API gives partners bookable experiences with deterministic checkout URLs that carry your attribution. Query either by destination (city=<slug> — full popularity-ordered catalogue, cursor-paginated) or by coordinates (lat+lng+radiusKm — distance-ordered near a known point). No keys, no SDK — point at the endpoint with your partner slug and you’re done.
Your partner slug
Every request identifies you by your partner slug. You can find yours on the Dashboard — look for the “Your partner slug” card at the top.
Note
Older slugs continue to resolve to your account as aliases, so any links you’ve already shipped to customers keep working after a rename.
Quick start
One request, no auth. Pick the mode that fits your product.
Near a known location (hotel concierge, venue, map pin):
curl 'https://concierge.tickadoo.com/connect/v1/experiences?partner=<your-slug>&lat=51.5074&lng=-0.1278&radiusKm=5'
Full destination catalogue (city page, browse view):
curl 'https://concierge.tickadoo.com/connect/v1/experiences?partner=<your-slug>&city=london&limit=50'
Authentication
None. Your partner slug identifies you. Embed it client-side; treat it as public information.
Endpoint reference
GET /connect/v1/experiences
Two mutually-exclusive query modes: city mode (city=<slug>) returns the popularity-ordered full catalogue for a destination and supports cursor pagination; geo mode (lat+lng+radiusKm) returns distance-ordered results near a point and is capped by limit.
| Parameter | Required | Description | Default |
|---|---|---|---|
partner | Yes | Your partner slug. | — |
city | City mode | City slug (e.g. london, new-york, paris). When set, lat/lng/radiusKm are ignored. | — |
lat | Geo mode | Latitude in degrees (WGS84). Required when city is not set. | — |
lng | Geo mode | Longitude in degrees (WGS84). Required when city is not set. | — |
radiusKm | No | Search radius in kilometres (geo mode only). Clamped to [0.1, 50]. | 5 |
category | No | Filter by canonical category (single value). See Categories for the allowed list. | — |
cursor | No | Opaque pagination token from a previous response’s pagination.nextCursor. City mode only. | — |
limit | No | Maximum results per page. Clamped to [1, 100]. In city mode, page through the rest with cursor. | 50 |
locale | No | BCP-47 locale code. Translatable fields overlay when present, otherwise fall back to English. | en |
context | No | Opaque text up to 128 chars. Echoed back on every experience’s checkoutUrl for booking attribution. | — |
Response shape
Each experience carries a checkoutUrl, hero image, short description, classification, and — in geo mode — a distance from the requested point. The pagination envelope returns a nextCursor in city mode when the page is full. Worked example:
{
"experiences": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"slug": "sunset-rooftop-yoga-soho",
"title": "Sunset rooftop yoga in Soho",
"shortDescription": "60-min flow with skyline views, all levels welcome.",
"heroImageUrl": "https://cdn.tickadoo.com/exp/.../hero.webp",
"currency": "GBP",
"fromPrice": 28,
"distanceKm": 0.42,
"productType": "attraction",
"showtimes": null,
"checkoutUrl": "https://tickadoo.page/sunset-rooftop-yoga-soho?partner=<your-slug>&context="
}
],
"pagination": {
"limit": 50,
"count": 50,
"nextCursor": "eyJyYyI6ODUwMCwiaWQiOiJ1dWlkLXJvdzIifQ=="
}
}
Notable fields:
id— opaque listing UUID. Stable across re-imports; use it as a primary key. Useslugfor human-readable references and URLs.productType— internal classification (live_show,attraction,tour,transport,food_drink, …). Cleaner signal than the rawcategoriesarray — prefer this when grouping listings client-side.showtimes— weekly schedule for theatre / show-type listings: seven lines, one per day Mon–Sun, each carrying showtime(s) (e.g.19:30or14:30, 19:30) orClosed. Best-effort — many shows arenullwhile we backfill from supplier detail feeds.pagination.nextCursor— present in city mode when the page is full.nullin geo mode or when the result set is exhausted.
The full schema lives in the OpenAPI spec linked below.
Categories
category= filters either query mode to a single canonical bucket. Accepted values:
theatre, musicals, plays, concerts, opera, ballet, comedy, tours, guided-tours, walking-tours, day-trips, food-tours, attractions, museums, sightseeing, outdoor, cruises, transfers, food-and-drink, family.
Matching is done server-side by productType (clean signal) with fallbacks onto the underlying categories overlap and title heuristics, so partners see a stable enum regardless of how supplier categorisation evolves. Unknown or unsupported values return 400 InvalidRequest with the allowed list in the message.
Pagination (city mode)
City mode walks the entire popularity-ordered catalogue. After each response, take pagination.nextCursor and pass it back as cursor= to fetch the next page. Stop when nextCursor is null.
# Page 1
curl 'https://concierge.tickadoo.com/connect/v1/experiences?partner=<slug>&city=london&limit=100'
# Page 2 — pass back the nextCursor from page 1's response
curl 'https://concierge.tickadoo.com/connect/v1/experiences?partner=<slug>&city=london&limit=100&cursor=eyJyYyI6...'
The cursor is opaque (base64-encoded order-key boundary) — don’t construct it yourself; it may change format. Passing cursor= in geo mode returns 400 InvalidRequest.
Attribution & checkout
checkoutUrlis the link to deep-link or iframe. The user lands on tickadoo’s booking flow and partner attribution flows through to the resulting order.- The
contextparameter you pass on the request is echoed verbatim into everycheckoutUrl. Pass e.g.room-301to attribute a booking to a specific guest or touchpoint.
Locale support
Pass locale=ja, locale=fr, locale=pt-br, etc. Translatable fields (title, shortDescription, description, cancellationPolicy, tags) overlay from the per-locale translation set when present; untranslated fields fall back to English (never blanked).
Rate limits
1000 requests per minute per (partner, IP). On overflow the API responds with HTTP 429 and a Retry-After: 60 header. The IP component means a single scraper eats its own IP budget, not your partner-wide quota.
Errors
The API uses standard HTTP status codes:
- 400 — missing or malformed query parameters.
- 404 — unknown partner slug.
- 429 — rate limit exceeded; retry after the
Retry-Afterwindow. - 5xx — transient server error; retry with backoff.
Errors return a JSON body of this shape:
{
"error": "NotFound",
"message": "Partner not found or inactive"
}
OpenAPI spec
The full machine-readable spec is published with CORS open, so partner tooling (codegen, Postman, Insomnia) can fetch it directly: