Business Platform
TravelShop

API Reference

TravelShop Booking API

Search 10,000+ tours, pull live pricing and availability, and create bookings from your own platform. This reference is public — read it before you apply.

Getting started

The API is a plain REST API over HTTPS that returns JSON, authenticated with a single API key you generate yourself once your access is approved. The examples below use {BASE_URL} as a placeholder — your base URL is shown next to your key in the developer dashboard.

  1. 1

    Apply

    Tell us about your platform. We review every request by hand, usually within 1–2 business days.

  2. 2

    Generate your key

    Once approved, create your API key in the developer dashboard. It is shown only once.

  3. 3

    Call the API

    Send the key as an X-API-Key header on every request. That is the whole handshake.

Authentication

Send your key in the X-API-Key header on every request. There is no OAuth flow, no token exchange and no expiry — the key stays valid until you regenerate or revoke it.

curl
curl -X GET '{BASE_URL}/b2b/api/apiv2/b2c/tours/search?term=cappadocia&page_size=5' \
  -H 'X-API-Key: YOUR_API_KEY' \
  -H 'Accept: application/json'
node
const res = await fetch(
  '{BASE_URL}/b2b/api/apiv2/b2c/tours/search',
  {
    method: 'POST',
    headers: {
      'X-API-Key': process.env.TSB_API_KEY,
      'Content-Type': 'application/json',
      Accept: 'application/json',
    },
    body: JSON.stringify({ term: 'cappadocia', page_size: 5 }),
  },
)

const data = await res.json()
Call the API from your server, never from browser code — a key shipped to the browser is a public key. We store only a hashed copy of your key, so it is shown exactly once, when you create it. Lost it? Generate a new one; the old one dies immediately.

Rate limits & quotas

Two independent limits apply, both counted per partner — never shared with anyone else.

240 requests per minute

A burst limit. Every response carries X-RateLimit-Limit and X-RateLimit-Remaining so you can pace yourself.

20000 requests per day

The default daily quota on approval. It resets at midnight UTC, and we raise it on request — tell us your expected volume and we will match it.

Exceeding either limit returns 429. Your current usage, broken down per endpoint, is visible in your developer dashboard.

Content rules

New tours are held back for 30 days. A tour added to our catalogue does not appear in partner search results or tour detail responses until it is 30 days old. This gives the tour time to be indexed on our own site first, so search engines see the original source. Everything older than that is fully available to you.

Pricing, dates and content are revalidated daily. Cache responses if you like, but refresh prices and availability at least once a day — and always re-check availability immediately before creating a booking.

Pricing & availability

Live prices and departure availability. Every path exists in a b2c and a b2b variant: b2c returns retail prices, b2b returns your net (operator) price.

GET/b2b/api/apiv2/b2c/availability/{slug}

Availability & price for a tour

Departure availability and pricing for one tour. Use /b2b/api/apiv2/b2b/availability/{slug} for net pricing.

ParameterTypeDescription
roomTypestringsng, dbl or trp. Defaults to dbl.
dateYYYY-MM-DDA single departure date.
startDateYYYY-MM-DDStart of a date range.
endDateYYYY-MM-DDEnd of a date range.
paxintegerNumber of travellers.
service_typestringService level. Defaults to regular.
roomsarrayRoom breakdown, as [{ id, pax, count }].
include_pricesyes | noInclude the price breakdown.
include_roomsyes | noInclude room options.
POST/b2b/api/apiv2/b2c/prices/{type}

Batch prices

One price per tour for a list of ids — what listing and category pages need. Use the b2b path for net prices.

ParameterTypeDescription
idsrequiredinteger[]Tour ids to price.
roomTypestringsng, dbl or trp. Defaults to dbl.
startDateYYYY-MM-DDDefaults to today.
endDateYYYY-MM-DDOptional range end.
GET/b2b/api/apiv2/b2c/pricemonths/{type}/{id}

Monthly price calendar

Cheapest price per month for a tour — for a "from €X in May" calendar. A b2b variant is available for net prices.

Destinations & taxonomy

The reference data behind filters, menus and search boxes.

GET/b2b/api/apiv1/b2c/countries/list

Countries

All countries with their id, name, slug and phone code.

GET/b2b/api/apiv1/b2c/quicksearch/locations

Location search

Search destinations by name — cities, regions and countries.

ParameterTypeDescription
qstringSearch term.
idsstringComma-separated ids, to resolve known locations instead of searching.
limitintegerMax results, 1–50. Defaults to 20.
GET/b2b/api/apiv1/location-parents/{id}

Location ancestors

The parent chain of a location (city → region → country), for breadcrumbs.

GET/b2b/api/apiv1/b2c/quicksearch/tour-categories

Tour categories

Category pages matching a term, with their titles and URLs.

ParameterTypeDescription
termstringSearch term.
GET/b2b/api/apiv1/b2c/quicksearch/activities

Activities

Activity list (hiking, diving, …) with id, name and slug — the values the activities filter accepts.

ParameterTypeDescription
termstringSearch term.
GET/b2b/api/apiv1/b2c/tours/available-destinations

Available destinations

Destinations that currently have bookable tours.

Enquiries & bookings

Create demand on our side from your platform. Everything you create here is attributed to your partner account and appears in your dashboard.

POST/b2b/api/apiv1/bookings/new-enquiry

Create an enquiry

A traveller question about a tour. Creates an enquiry record and notifies the operator.

ParameterTypeDescription
tour_idrequiredintegerThe tour being asked about.
contact_namerequiredstringThe traveller's name.
contact_emailrequiredstringThe traveller's email.
messagerequiredstringThe question, up to 5000 characters.
phone_codeintegerLocation id of the phone country code.
contact_phonestringPhone number without the country code.
buyer_currencyUSD | EURCurrency to quote in. Defaults to EUR.

No captcha is required on API-key requests — your key, quota and rate limit are the gate.

POST/b2b/api/apiv1/bookings/new-booking

Create a booking

A full booking with travellers and rooms. Returns the booking with its reference_id and token, which you then use for payment.

ParameterTypeDescription
tour_idrequiredintegerThe tour being booked.
paxrequiredintegerNumber of travellers.
daterequiredYYYY-MM-DDDeparture date, today or later.
service_typerequiredstringService level, as returned by availability.
seller_currencyrequiredstringCurrency the tour is sold in.
buyer_currencyrequiredstringCurrency the traveller pays in.
roomsarrayRequired for tours with accommodation: [{ id: "dbl", pax, count }].
customersrequiredarrayTravellers. The first is the lead and needs title, first_name, last_name, email, phone_code and phone.
GET/b2b/api/apiv1/bookings/{referenceId}

Booking detail

Read one booking. Pass the booking token as ?token= — the token scopes access to that single booking.

ParameterTypeDescription
tokenrequiredstringThe token returned when the booking was created.
POST/b2b/api/apiv1/bookings/{referenceId}/payment-request

Open a payment request

Start a collection against a booking, for a payment page hosted on your own front end. The amount is calculated from the booking — the client cannot set it.

ParameterTypeDescription
tokenrequiredstringThe booking token.
payment_modeltotal | depositCollect the full remaining amount or just the deposit.
GET/b2b/api/apiv1/bookings/{referenceId}/payment-request/{id}

Payment request status

Poll the state of a payment request. Requires the same token.

ParameterTypeDescription
tokenrequiredstringThe booking token.
POST/b2b/api/apiv1/bookings/abandoned-cart

Report an abandoned cart

Tell us a traveller started but did not finish a booking, so our team can follow up. Creates no booking record.

ParameterTypeDescription
sourcerequiredtsb | tstWhich storefront the cart came from.
tourrequiredstringTour slug.
namestringThe traveller's name.
emailstringThe traveller's email.
phone_codeintegerLocation id of the phone country code.
phonestringPhone number.
dateYYYY-MM-DDIntended departure date.
paxintegerNumber of travellers.

Errors

Errors use standard HTTP status codes, with a JSON body carrying a message and — for validation failures — an errors object keyed by field name.

CodeMeaningWhat to do
200OKThe request succeeded.
401UnauthorizedThe X-API-Key header is missing, or the key is not recognised. Check that you are sending the key you generated, not the one it replaced.
403ForbiddenYour API access is suspended or not yet approved. Contact us if you believe this is a mistake.
404Not foundThe tour, booking or location does not exist — or, for token-scoped endpoints, the token does not match.
422UnprocessableValidation failed. The body contains a message and an errors object keyed by field name.
429Too many requestsYou exceeded either the per-minute rate limit or your daily quota. Check the X-RateLimit-Remaining header and back off.
500Server errorSomething failed on our side. Retry with backoff; if it persists, contact us with the time of the request.
422 response
{
  "message": "The given data was invalid.",
  "errors": {
    "tour_id": ["The selected tour id is invalid."],
    "customers.0.email": ["The customers.0.email must be a valid email address."]
  }
}

Versioning

The version is part of the path: /b2b/api/apiv1/… and /b2b/api/apiv2/…. Both are live and maintained; which one an endpoint sits under is historical, not a quality difference.

Within a version we only make additive changes — new fields and new endpoints. We do not rename or remove fields you already rely on. Anything that would break an integration ships under a new version path, and we email every active partner before a version is retired.

Ready to build?

Apply for access and generate your key. Questions about which integration fits you? Our team is happy to help.