Home / Alps2Alps Public API — Developer Documentation

Alps2Alps Public API — Developer Documentation

The Alps2Alps Public API is a free, key-less HTTP API that lets AI agents and developer integrations discover Alpine transfer routes, retrieve real-time prices from the same booking engine that powers the website, look up verified accommodation entries, and generate checkout links that land the end user directly on the booking form with everything pre-filled. The entire flow works with GET requests only — no POST needed — so even chat agents that can only open URLs can drive the booking funnel.

No authentication is required. All endpoints are rate-limited per IP. All responses are application/json.

Base URL: https://booking.alps2alps.com/api/public/v1/
The full OpenAPI 3.1 spec (v1.4.0) is available at https://booking.alps2alps.com/openapi/public-v1.json. Import it directly into Postman, Insomnia, or any AI agent framework that accepts an OpenAPI URL (OpenAI function calling, Anthropic tool use, LangChain, AutoGen, and others).

TL;DR for AI agents

Alps2Alps is a private Alpine transfer service (airport / train station ↔ ski resort). This API lets you take a user from “I want to go from Geneva to Chamonix on the 22nd” to a ready-to-pay booking page — without any auth and without leaving GET requests if that’s all your runtime supports.

Minimum end-to-end flow (works with GET only):

  1. GET /api/public/v1/locations/search?q=Geneva → resolve names to codes (airport-1, resort-11, train_station-7, city-48).
  2. GET /api/public/v1/transfer-options?from=airport-1&to=resort-11&date=2026-07-22&time=14:30&adults=2 → live prices per vehicle. The response includes ready-made links — no need to construct URLs manually:
    • response.quote_url — pre-filled step-2 link (vehicle picker). Use this when the user hasn’t chosen a vehicle yet.
    • response.outbound.vehicles[i].booking_url — per-vehicle step-3 link (Your Details, vehicle pre-selected). Use this after the user picks a vehicle.

If you can POST, use POST /api/public/v1/checkout-link when you also need to pre-fill accommodation, flight numbers, or free-text driver notes. That returns a stable tokenised URL valid for 24 hours.

Adding any return_* parameter flips the request to a return trip. Omitted return fields automatically mirror the matching outbound value, so the simplest return URL is just …&return_date=…&return_time=….

Machine-readable discovery

Everything you need to integrate is linked from these discovery files:

  1. Search locations — call /locations/search to resolve the user’s pickup and drop-off names into airport-… / resort-… / city-… codes.
  2. (Optional) Search accommodations — call /accommodations?resort_id=…&q=… with at least 2 characters of the hotel name. Pick the matching entry and confirm the exact name with the user before using its id.
  3. Quote the trip — call /transfer-options with the location codes plus passenger, luggage, and date inputs. The response includes passengers (confirming the passenger counts used), luggage per direction, and max_passengers / max_luggage per vehicle so you can tell users which vehicles fit their group. For return trips, each vehicle also carries a total_price for the combined journey. Present prices, vehicle types, travel time, and any promo discount. Always confirm the returned pick_up_date_time / return.pick_up_date_time back to the user — those are the actual times the driver will use.
  4. Send the user to checkout — once the user picks a vehicle, use the booking_url field on that vehicle to send them directly to step 3 (Your Details) with everything pre-filled. If you need to pre-fill an accommodation, flight number, or driver notes as well, use POST /checkout-link instead.
  5. The user pays — the checkout URL lands on booking step 3. They only need to enter contact details and complete payment.

Lightweight agents (no POST capability)

For chat agents that can only fetch URLs, the same end-to-end flow is possible without ever issuing a POST:

  1. (Optional) GET /locations/search?q=… to resolve names to codes.
  2. GET /api/public/v1/transfer-options?from=…&to=…&date=…&time=…&adults=… to fetch live prices. The JSON response already contains the links — just use them directly:
    • response.quote_url — lands the user on step 2 (vehicle picker).
    • response.outbound.vehicles[i].booking_url — lands the user on step 3 with that vehicle pre-selected.
    • You can still construct these URLs manually using the deep-link aliases if needed (see Deep links section).

Endpoints

Search for supported pickup and drop-off locations: resorts, airports, train stations, and cities.

Query parameters

ParameterRequiredNotes
qYesSearch term. Strings shorter than 2 characters return an empty array.

Response

A flat array of matching locations. Resort codes follow the pattern resort-{id}; transport hub codes follow {type}-{id} (e.g. airport-1, train_station-7, city-48).

[
  { "code": "airport-1",  "name": "Geneva Airport", "type": "airport", "country": "Switzerland" },
  { "code": "resort-11",  "name": "Chamonix",        "type": "resort",  "country": "France" }
]

Errors

  • 400 INVALID_PARAMSq parameter missing entirely.

Example

curl "https://booking.alps2alps.com/api/public/v1/locations/search?q=cham"

2. GET /api/public/v1/accommodations

Typed hotel lookup against the verified accommodation catalogue used by the booking funnel’s step-3 autocomplete. Results are scoped to a 10 km radius around the supplied resort. The intended flow is a typed lookup — the user names their hotel, the agent passes it as q scoped to the resort, and picks the matching id to send to /checkout-link.

Query parameters

ParameterRequiredDefaultNotes
resort_idYesNumeric resort id from a resort-{id} location code.
qYesSubstring of the accommodation name (accent-insensitive). Min 2, max 100 characters.
limitNo20Hard-capped at 50.

Response

{
  "data": [
    {
      "id": 38959,
      "name": "Clos 66",
      "address": "66 Clos des Charmilles, Chamonix",
      "latitude": 45.9123,
      "longitude": 6.8694
    }
  ],
  "total": 4
}

total is the count before limit is applied, so the caller can tell when results were truncated.

Important: Only id values returned by this endpoint may be passed into /checkout-link as accommodation_id. Free-typed hotel names are not accepted, ensuring every order receives a verified name and address pair.

Errors

  • 400 INVALID_PARAMSresort_id missing or not an integer, or q missing or shorter than 2 characters.
  • 404 RESORT_NOT_FOUND — resort id is unknown, inactive, or has no coordinates.

Example

curl "https://booking.alps2alps.com/api/public/v1/accommodations?resort_id=11&q=clos"

3. GET / POST /api/public/v1/transfer-options

Returns real-time prices for every bookable vehicle type on the requested route, in the requested currency, with the requested passenger and luggage configuration. Nothing is persisted — this is a quote-only endpoint. Two request flavours share the same response envelope:

  • POST — canonical JSON body using book\forms\Transfer field names. Recommended for programmatic clients with full control over headers and body.
  • GET — friendly-alias query string (from, to, date, time, …). For AI agents and chat tools that construct URLs by hand. See the Deep links section for the full alias table.

Important — time vs pickup time: By default, time and return_time are interpreted as flight times (is_flight and return_is_flight default to 1). The system calculates the actual pickup time from the flight time — for a return leg, the resort pickup will be earlier than the stated flight time.

If the user states a desired pickup time rather than a flight time (e.g. “pick us up at 14:30”, “leave the resort at 18:00”), set is_flight=0 and/or return_is_flight=0. Otherwise the quoted pickup time will be wrong.

Always confirm the returned pick_up_date_time / return.pick_up_date_time back to the user — those are the actual times the driver will use.

Example: A request with return_time=18:00 and the default return_is_flight=1 (resort → airport) returns "return.pick_up_date_time": "2026-07-26 14:20:00" — the system worked back from an 18:00 flight. To get an 18:00 pickup, send &return_is_flight=0.

Request body (POST)

Accepts application/json or application/x-www-form-urlencoded.

FieldTypeRequiredNotes
route_from_codestringYese.g. airport-1
route_to_codestringYese.g. resort-11
outbound_datestringYesYYYY-MM-DD
outbound_time_hoursintYes0–23
outbound_time_minutesintYes0–59
outbound_selected_time_is_flightint (0/1)No (default 1)1 = time is a flight arrival time, pickup calculated. 0 = time is the literal pickup time.
is_returnint (0/1)No (default 0)When 1, all return_* fields are required
adult_countintYes≥ 1
children_countintNo
infant_countintNo
children_age_seat_countintNoNumber of child-seat-aged children
children_age_booster_countintNoNumber of booster-seat-aged children
luggage_countintNo
skibag_countintNo
has_ski_equipmentint (0/1)No
currency_codestringNo (default EUR)ISO 4217. Unknown/inactive codes return 400 CURRENCY_NOT_SUPPORTED.
promo_codestringNoSilently ignored if invalid

For return trips, mirror the outbound fields with a return_ prefix: return_route_to_code, return_date, return_time_hours, return_time_minutes, return_selected_time_is_flight (default 1 — see time note above), return_adult_count, return_children_count, return_infant_count, return_children_age_seat_count, return_children_age_booster_count, return_luggage_count, return_skibag_count, return_has_ski_equipment.

GET query string — additional parameters

All outbound and return aliases are documented in the Deep links section. The following extra parameters are only relevant to GET requests and are not part of the POST body:

ParameterTypeRequiredDescription
child_agesstringNoComma-separated ages of children in the group, e.g. 8,12. Informational only — not used for pricing or vehicle selection. The value is echoed back in passengers.child_ages and preserved in the generated quote_url and booking_url.
return_child_agesstringNoSame as child_ages for the return leg. Preserved in generated URLs only.

The booking system selects vehicles based on adults, children, and child_seats/boosters counts — not individual child ages. The child_ages parameter exists so that AI assistants and integrations can confirm back to the user that their input was received. If specific seating requirements apply (child seat vs booster), use the child_seats and boosters parameters.

GET examples

One-way, 2 adults, 2 bags:

curl "https://booking.alps2alps.com/api/public/v1/transfer-options?from=airport-1&to=resort-11&date=2026-07-22&time=14:30&adults=2&luggage=2"

Return trip, 2 adults + 1 child + 3 bags (realistic AI-chat scenario):

curl "https://booking.alps2alps.com/api/public/v1/transfer-options?from=airport-1&to=resort-11&date=2026-07-22&time=14:30&adults=2&children=1&luggage=3&return_date=2026-07-29&return_time=10:00"

Response

Example GET response (all fields populated, one-way trip):

{
  "disclaimer": "Prices shown are real-time and calculated using the same engine as the booking website.",
  "quote_url": "https://booking.alps2alps.com/book/quick-search?from=airport-1&to=resort-11&date=2026-07-22&time=14%3A30&adults=2",
  "route": {
    "from": "Geneva Airport",
    "from_code": "airport-1",
    "to": "Chamonix",
    "to_code": "resort-11",
    "distance_km": 99,
    "duration_minutes": 100,
    "travel_time": "01:40:00"
  },
  "currency": "EUR",
  "passengers": {
    "adults": 4,
    "children": 2,
    "child_ages": [8, 12],
    "infants": 0,
    "child_seats": 0,
    "boosters": 0
  },
  "outbound": {
    "pick_up_date_time": "2026-07-22 14:30:00",
    "luggage": {
      "normal_suitcases": 2,
      "ski_equipment": 0
    },
    "vehicles": [
      {
        "vehicle_type_id": 10,
        "name": "Standard minivan",
        "price": 247,
        "max_passengers": 8,
        "max_luggage": 8,
        "offer_code": "requested",
        "total_price": null,
        "booking_url": "https://booking.alps2alps.com/book/quick-checkout?from=airport-1&to=resort-11&date=2026-07-22&time=14%3A30&adults=4&children=2&vehicle=10"
      },
      {
        "vehicle_type_id": 4,
        "name": "VIP",
        "price": 418.50,
        "max_passengers": 7,
        "max_luggage": 7,
        "offer_code": "requested",
        "total_price": null,
        "booking_url": "https://booking.alps2alps.com/book/quick-checkout?from=airport-1&to=resort-11&date=2026-07-22&time=14%3A30&adults=4&children=2&vehicle=4"
      }
    ]
  },
  "return": null,
  "promo_code_applied": false
}

For a return trip, each vehicle in both the outbound and return blocks additionally carries a total_price:

{
  "vehicle_type_id": 11,
  "name": "Standard XL minivan",
  "price": 257.21,
  "max_passengers": 8,
  "max_luggage": 8,
  "offer_code": "requested",
  "total_price": 473.81,
  "booking_url": "https://booking.alps2alps.com/book/quick-checkout?from=airport-1&to=resort-11&..."
}

GET-only fields (null when using POST):

  • quote_url — ready-made step-2 link pre-filled with all the current search parameters. Use this when the user hasn’t chosen a vehicle yet.
  • vehicles[].booking_url — ready-made step-3 checkout link for each vehicle, with that vehicle pre-selected. After the user picks a vehicle, pass this URL directly to them.

Note on URL encoding: The quote_url and booking_url values in the JSON response use a literal & character to separate query parameters (e.g. …children=2&currency=EUR), which is correct for URL strings inside JSON. Some tools — including certain browser DevTools panels and AI chat interfaces — may visually render &currency as ¤cy because &curren is an HTML entity (the ¤ symbol). This is a display-only rendering artefact. The actual URL returned by the API is correct. Copy the raw value from the JSON response, not from a rendered HTML view.

Fields present on both GET and POST:

  • route.from_code / route.to_code — the location codes echoed from the request.
  • passengers — the full passenger configuration after defaults are applied. Includes child_ages (array of integers, or null if not provided or on POST).
  • outbound.luggage / return.luggage — luggage used when pricing each leg (normal_suitcases, ski_equipment). Confirms what the engine used.
  • vehicles[].max_passengers — maximum passengers this vehicle can carry. All returned vehicles already passed the capacity filter, so they are confirmed suitable for the group.
  • vehicles[].max_luggage — maximum suitcases this vehicle can carry.
  • vehicles[].total_pricereturn trips only. The combined price for this vehicle type across both legs, rounded to 2 decimal places. null on one-way trips. Do not attempt to sum leg prices manually — use total_price when displaying the combined journey cost.

Promo code response fields:

When a valid promo was applied:

"promo_code": "AMI10",
"promo_code_applied": true,
"promo_code_discount": { "type": "percent", "value": 5 }

When a promo code was submitted but is not valid for this route, date, or group:

"promo_code": "AMI10",
"promo_code_applied": false,
"promo_code_message": "Promo code is not valid for this route, date, or group."

Errors

  • 400 INVALID_PARAMS — body empty, validation failure, or malformed date.
  • 400 CURRENCY_NOT_SUPPORTED — currency not in the active currency list (GET and POST).
  • 404 ROUTE_NOT_FOUND — location code combination does not map to an active route.
  • 422 UNSUPPORTED_ROUTE — station-to-station, resort-to-resort, or other funnel-rejected combinations.
  • 422 MANUAL_BOOKING_REQUIRED — route requires manual handling.
  • 429 RATE_LIMITED — too many requests from this IP. Back off and retry.
  • 500 INTERNAL_ERROR — pricing engine threw an unrecognised exception.

POST example

One-way:

curl -X POST -H "Content-Type: application/json" \
  -d '{
        "route_from_code": "airport-1",
        "route_to_code": "resort-11",
        "outbound_date": "2026-07-22",
        "outbound_time_hours": 14,
        "outbound_time_minutes": 30,
        "outbound_selected_time_is_flight": 1,
        "is_return": 0,
        "adult_count": 2,
        "luggage_count": 2,
        "currency_code": "EUR"
      }' \
  "https://booking.alps2alps.com/api/public/v1/transfer-options"

Return trip, 2 adults + 1 child + 3 bags (realistic AI-chat scenario):

curl -X POST -H "Content-Type: application/json" \
  -d '{
        "route_from_code": "airport-1",
        "route_to_code": "resort-11",
        "outbound_date": "2026-07-22",
        "outbound_time_hours": 14,
        "outbound_time_minutes": 30,
        "outbound_selected_time_is_flight": 1,
        "is_return": 1,
        "adult_count": 2,
        "children_count": 1,
        "luggage_count": 3,
        "currency_code": "EUR",
        "return_date": "2026-07-29",
        "return_time_hours": 10,
        "return_time_minutes": 0,
        "return_selected_time_is_flight": 0,
        "return_adult_count": 2,
        "return_children_count": 1,
        "return_luggage_count": 3
      }' \
  "https://booking.alps2alps.com/api/public/v1/transfer-options"

Accepts the same body as /transfer-options plus a few optional pre-fill fields. Returns a one-shot URL valid for 24 hours. Use this endpoint when you need to pre-fill an accommodation, flight number, or driver notes alongside the booking — for everything else, the booking_url field in the /transfer-options GET response is simpler.

Additional body fields

FieldTypeNotes
outbound_vehicle_type_idintOptional. Defaults to the cheapest available outbound vehicle.
return_vehicle_type_idintOptional. Only used when is_return = 1.
outbound_flight_numberstringOptional. Alphanumeric + hyphen, max 16 chars.
return_flight_numberstringOptional. Only used when is_return = 1.
accommodation_idintOptional. Must be an id returned by /accommodations and within 10 km of the route’s resort.
additional_infostringOptional. Free-text driver note. Max 1000 chars.
return_additional_infostringOptional. Driver note for the return leg. Only used when is_return = 1.

Response

{
  "checkout_url": "https://alps2alps.com/book/public-checkout/8f1c2c…",
  "expires_at":   "2026-05-28 09:14:22",
  "currency":     "EUR",
  "promo_code_applied": true
}

Errors

All errors from /transfer-options, plus:

  • 400 INVALID_PARAMSaccommodation_id is not a positive integer.
  • 400 VALIDATION_ERROR — accommodation id unknown, outside the route’s resort radius, or route has no resort side.
  • 422 NO_BOOKABLE_VEHICLE — no vehicle had a usable price on this route.
  • 429 RATE_LIMITED — too many requests from this IP. Back off and retry.
  • 500 INTERNAL_ERROR — could not persist the checkout link.

Example

curl -X POST -H "Content-Type: application/json" \
  -d '{
        "route_from_code": "airport-1",
        "route_to_code": "resort-11",
        "outbound_date": "2026-07-22",
        "outbound_time_hours": 14,
        "outbound_time_minutes": 30,
        "outbound_selected_time_is_flight": 1,
        "is_return": 0,
        "adult_count": 2,
        "luggage_count": 2,
        "currency_code": "EUR",
        "outbound_vehicle_type_id": 4,
        "outbound_flight_number": "BA123",
        "accommodation_id": 38959,
        "additional_info": "Please call on arrival."
      }' \
  "https://booking.alps2alps.com/api/public/v1/checkout-link"

A stateless family of GET endpoints that lets simple AI agents and chat tools drive the booking flow with plain URLs — no POST needed, no DB row, no expiry. In most cases the quote_url and booking_url fields returned by GET /transfer-options are already constructed for you — the manual URL building below is for cases where you need full control over parameters or cannot make a prior API call.

The trade-off vs POST /checkout-link: deep links cannot carry an accommodation id, a flight number, or free-text driver notes (those require server-side validation). Use POST /checkout-link when you need those extras.

Friendly alias → canonical field mapping

All three deep-link endpoints translate the same compact query-string aliases. The minimum viable URL is just from + to + date + time.

Outbound / shared:

AliasCanonical fieldDefaultNotes
fromroute_from_codeRequired. e.g. airport-1
toroute_to_codeRequired. e.g. resort-11
dateoutbound_dateRequired. YYYY-MM-DD
timeoutbound_time_hours + _minutesRequired. HH:MM. Interpreted as flight arrival time by default (is_flight=1). Set is_flight=0 for literal pickup time.
is_flightoutbound_selected_time_is_flight11 = time is flight arrival, pickup calculated. 0 = time is literal pickup time.
adultsadult_count2
childrenchildren_count0
child_ages— (GET only)Comma-separated child ages, e.g. 8,12. Echoed in passengers.child_ages and preserved in generated URLs. Informational only.
infantsinfant_count0
child_seatschildren_age_seat_count0
boosterschildren_age_booster_count0
luggageluggage_count2
ski_bagsskibag_count0
skihas_ski_equipment0
currencycurrency_codeEURUnknown/inactive codes return 400 CURRENCY_NOT_SUPPORTED on the JSON endpoints. The browser deep links (quick-search, quick-checkout) silently fall back to EUR so a stale value never breaks a user landing in the funnel.
promopromo_codeSame silent-on-failure behaviour as POST

Return leg — any return_* parameter (or return=1) implicitly enables is_return=1. Omitted return fields auto-mirror the matching outbound value, so the simplest return URL is just …&return_date=…&return_time=…:

AliasCanonical fieldMirror defaultNotes
return_toreturn_route_to_codeoutbound from
return_datereturn_date
return_timereturn_time_hours + _minutesInterpreted as flight departure time by default (return_is_flight=1). The calculated resort pickup will be earlier. Set return_is_flight=0 for literal resort pickup time.
return_is_flightreturn_selected_time_is_flightoutbound is_flight1 = return_time is the flight departure time, pickup calculated automatically. 0 = return_time is the literal resort pickup time.
return_adultsreturn_adult_countoutbound adults
return_childrenreturn_children_countoutbound children
return_child_ages— (GET only)outbound child_agesComma-separated child ages for the return leg. Preserved in generated URLs only.
return_infantsreturn_infant_countoutbound infants
return_child_seatsreturn_children_age_seat_countoutbound child_seats
return_boostersreturn_children_age_booster_countoutbound boosters
return_luggagereturn_luggage_countoutbound luggage
return_ski_bagsreturn_skibag_countoutbound ski_bags
return_skireturn_has_ski_equipmentoutbound ski

Unknown query keys (e.g. utm_source=…) are silently ignored.

3a. GET /api/public/v1/transfer-options (quote)

Same response shape as the POST flavour. Returns JSON, never persists anything. The response includes quote_url and per-vehicle booking_url fields ready to use.

One-way:

curl "https://booking.alps2alps.com/api/public/v1/transfer-options?from=airport-1&to=resort-11&date=2026-07-22&time=14:30&adults=2&luggage=2&currency=EUR"

Return trip, 2 adults + 1 child + 3 bags — because return fields auto-mirror outbound, only return_date and return_time need to be added:

curl "https://booking.alps2alps.com/api/public/v1/transfer-options?from=airport-1&to=resort-11&date=2026-07-22&time=14:30&adults=2&children=1&luggage=3&return_date=2026-07-29&return_time=10:00"

3b. GET /book/quick-search (land on step 2)

Stateless deep link that lands the browser on step 2 of the booking funnel (vehicle selection) with prices already calculated. Equivalent to the quote_url returned by GET /transfer-options. Useful when you want to construct the link manually without a prior API call.

One-way:

Return trip, 2 adults + 1 child + 3 bags:

Failures (validation error, unsupported route, route not found, malformed date) redirect to /book/?step=1 so the user lands on the clean booking form — they never see an error page.

3c. GET /book/quick-checkout (land on step 3)

Stateless equivalent of POST /checkout-link. Lands the browser on step 3 (Your Details) with the funnel hydrated from the query string. Equivalent to the booking_url field on each vehicle in the GET /transfer-options response. The vehicle parameter pre-picks the vehicle type.

One-way, pre-picked vehicle:

Return trip, 2 adults + 1 child + 3 bags, pre-picked vehicle:

Additional quick-checkout parameters:

AliasNotes
vehicleOutbound vehicle_type_id from /transfer-options. If omitted, the funnel auto-picks the cheapest vehicle.
return_vehicleReturn leg vehicle type. Defaults to vehicle for return trips.

accommodation_id, flight numbers, and driver notes are not supported on the GET flow. Use POST /checkout-link for those.

POST vs GET — which to use

NeedUse
JSON-only, full control, machine-to-machinePOST /api/public/v1/transfer-options
Quote in a URL (chat, email, blog)GET /api/public/v1/transfer-options
Hand user to vehicle picker (step 2) — use the ready-made quote_url from the response, or build manuallyGET /book/quick-search
Hand user to step 3 with a pre-picked vehicle — use the ready-made booking_url from the response, or build manuallyGET /book/quick-checkout
Hand user to step 3 with accommodation / driver notes / flight numbers / 24h-expiring linkPOST /api/public/v1/checkout-link

Error envelope & error codes

All errors share the same JSON envelope:

{ "error": { "code": "UNSUPPORTED_ROUTE", "message": "The routes from station to station is not supported" } }
HTTPCodeWhen
400INVALID_PARAMSMissing or invalid request fields, malformed dates, or validation failures.
400CURRENCY_NOT_SUPPORTEDUnknown or inactive currency code on a JSON endpoint (GET or POST).
400VALIDATION_ERRORaccommodation_id not found, not in range, or not applicable to this route.
404RESORT_NOT_FOUND/accommodations could not resolve the resort.
404ROUTE_NOT_FOUNDLocation code combination does not map to an active route.
422UNSUPPORTED_ROUTEFunnel rejects this destination-type combination (e.g. station-to-station).
422MANUAL_BOOKING_REQUIREDRoute requires manual handling; not bookable online.
422NO_BOOKABLE_VEHICLENo vehicle had a usable price on this route.
429RATE_LIMITEDPer-IP request limit exceeded. Back off and retry with exponential backoff.
500INTERNAL_ERRORCatch-all for all other unhandled exceptions.

Rate limits

The API requires no authentication and is rate limited per client IP. When the limit is exceeded the API returns HTTP 429 with the standard error envelope:

{ "error": { "code": "RATE_LIMITED", "message": "Too many requests. Please slow down and retry." } }
  • Pace your requests. A normal end-to-end flow is 3–4 calls: locations/searchtransfer-options → optionally checkout-link. The pricing endpoints have a lower allowance than the lookup endpoints.
  • On a 429, back off and retry with exponential backoff rather than retrying immediately.
  • Shared IPs — users behind a shared/office IP share the same budget and can hit the limit sooner.
  • Need a higher allowance? Contact us — a per-key allowance can be arranged for production integrations.

Exact per-minute numbers are intentionally not published so they can be tuned without changing the contract. Treat 429 as “slow down”, not a permanent failure.


Currencies

Pass any active ISO 4217 currency code in currency_code (POST) or currency (GET alias) — e.g. EUR, GBP, CHF, USD. The default is EUR.

  • JSON endpoints (GET and POST /transfer-options, POST /checkout-link) — an unknown or inactive currency code returns 400 CURRENCY_NOT_SUPPORTED. Agents calling these endpoints should handle that error rather than assuming an EUR fallback.
  • Browser deep links (/book/quick-search, /book/quick-checkout) — an invalid currency= value silently falls back to EUR so a stale parameter never breaks a user landing in the funnel.

For non-EUR responses the disclaimer field in /transfer-options notes that the exact checkout total may shift slightly due to live FX rates (sourced from the ECB, same as the booking website).


Promo codes

Pass promo_code (POST) or promo (GET alias) on either /transfer-options or /checkout-link. The code is validated server-side. If validation fails or the promo service is temporarily unreachable, the code is silently ignored and promo_code_applied returns false — the request itself still succeeds.

When a code is submitted but not applied, the response includes promo_code (echo of the submitted code) and promo_code_message explaining why it was not applied (for example, the code is expired or not valid for the chosen route). promo_code_applied remains false. The response itself still succeeds — prices are returned at full rate.

When a promo is applied on /transfer-options, the response includes promo_code_discount with a type (e.g. "percent") and value. See the Response section above for both promo code scenarios.


CORS & HTTP methods

The API is browser-callable with no additional configuration:

  • Access-Control-Allow-Origin: *
  • Access-Control-Allow-Headers: Content-Type, Accept
  • Access-Control-Allow-Methods: GET, POST, OPTIONS
  • OPTIONS preflight returns HTTP 204 with no body.
  • CSRF validation is disabled on all public API endpoints.