Give your AI agent live availability for more than 290,000 hotels, apartments and other accommodations across Germany, Austria, Switzerland and the United States: search them, read real rooms, EUR prices, cancellation terms where the property publishes them and room photos, then book the room without the guest ever leaving the conversation.

Setup

Add it to your AI

One URL is the whole integration: no key, no sign-up, no sign-in. Click the one you use to see its installation steps.

Questions we get

Do I need an API key?
No. Every tool on this endpoint is public, and it needs no auth header.
What is covered?
More than 290,000 properties, across Germany, Austria and Switzerland as well as ten United States cities including New York, Los Angeles, Chicago, Miami and others.
Are the prices real, or cached?
Read live from each property’s own booking page while the call runs.
Which currency, and which language?
EUR only. The description we write on the results card comes back in German or English; the name of a room, and of each price option within it, stay exactly as the property writes them.
Does calling hotel_reserve book a real room?
Yes. A real reservation at a real property, every time.
Does my agent ever touch card details?
Never. Where a hotel has to be paid for, the guest pays on the checkout page and their card is held, not charged, until the hotel booking is actually confirmed. You handle your card, not your agent.
Who does the guest contact afterwards?
The hotel for the room itself, using the hotel’s confirmation number. The Bluerails reference is what reaches us.
These docs describe something my agent doesn’t do yet. Why?
Clients read the tool list when the connector is added and mostly keep that copy, so an update we ship afterwards may not reach an agent that already has it. Remove the connector and add it again — Customize → Connectors in Claude, Plugins in ChatGPT on the web — and the next query runs against a freshly read list.
My agent answered a hotel question without using Bluerails. Why?
Name the connector in the question. Most clients hold several, and an assistant that is not pointed at Bluerails will often answer a hotel question from memory or from web search instead, which reads like a failed install when it is only an unasked tool. “Using Bluerails, find me a hotel in …” is enough. If you gave it a different name when you added it, say that one. Naming it is a request, not a switch: the assistant decides which tools to call, so it may still answer from elsewhere.
Something’s wrong. Who do I reach out to?
Please reach out to us at support@bluerails.com.

That is the whole setup. Usage, below, shows what to ask it once it is in.

Usage

What you can ask it

The guest asks in a sentence. The nights, the party and the price come out of it, and what comes back is real rooms at real prices.

  • “Using Bluerails, find me a hotel in Berlin for two people, 12 to 14 November, nothing over €200 a night.”
  • “Using Bluerails, we want a family hotel near Zell am See from 18 to 21 December, two adults and our two kids, aged 6 and 9, up to €400 a night.”
  • “Using Bluerails, find me a 4-star hotel in Vienna for 15 to 17 October, two of us, nothing over €250 a night.”
The results card for the Berlin question: Hotel Kastanienhof, a 3-star in Berlin 1.2 km out, with four rooms priced for the two nights from 238 to 289 euro, each carrying photos and marked bookable.
What the Berlin question returns, trimmed to the first hotel. Read live on 29 September 2026; rates and availability move.

How a booking gets made

Two calls, and the handle between them is what ties one to the other. There is no session to open and nothing to store on your side.

  1. 1. list_hotels with the guest’s dates and party

    Every room we could price comes back with an offerId beside its EUR price and its cancellation terms. Hotels in unpriced have no rooms and no handle, so there is nothing to reserve there. Leave the dates out and nothing is priced and no handle is minted.

  2. 2. Show the rooms and let the guest choose one

    Show them the price and the cancellation terms for the room they are picking. Where cancelTerms is null, say we hold none and the hotel’s own terms apply. That is not a reason to refuse the booking.

  3. 3. hotel_reserve with that room’s offerId, unedited

    The hotel, room, rate, stay, party and price all travel inside the handle, which is why none of them is an argument. You pass the guest’s own name, email, phone and billing address, and acceptedTerms: true once they have agreed to that rate. You cannot book a combination the guest was never shown.

  4. 4. Read which of the two endings came back

    confirmationNumber means the room is booked and the guest pays the hotel at the desk, and nothing has been charged. paymentUrl means the room is not held yet: the guest pays on our page, their card is held rather than charged, and we place the booking once they have. Both endings also carry a bookingReference, which is what the guest quotes to reach us.

An offerId is good for 30 minutes. If the guest takes longer, call list_hotels with that same offerId and nothing else. That re-prices just that one room and mints a fresh handle, rather than showing the guest the whole property a second time. If a call comes back ambiguous, do not call again: a reservation may already exist.

More info

Authentication

None. Every tool is public, so the connector above works immediately with no API key and no sign-in prompt, which on this endpoint means a reservation can be placed with nothing but the URL, so treat it as you would anything that takes a booking. The other verticals (publishers, ecommerce stores, SaaS companies) are served over the REST API rather than as MCP tools; see openapi.json.

Tool reference

list_hotels searches properties and, when you pass a stay, prices them in the same call. hotel_reserve books one of the rooms it priced. Calling it places a real reservation at a real property.

list_hotels

Search hotel properties and price them for a stay in the same call. What comes back is two groups: priced, the hotels whose live rates were read, and unpriced, the rest.

Signature
list_hotels(
  city?: string, radius?: { value: number, unit: "km" | "mi" },
  query?: string, country?: "de" | "at" | "ch" | "us",
  starRating?: "1-star" | "2-star" | "3-star" | "4-star" | "5-star",
  style?: "boutique",           // independent of starRating: pass both to ask for both
  // REQUIRED: ask the guest for these before you search; a call
  // missing any of them runs no search and asks for them back
  checkIn: string, checkOut: string, adults: number,
  maxPricePerNight: number, currency: string,
                                 // currency is ISO 4217 for maxPricePerNight ONLY
                                 // ("EUR" most of the time). Use the one the guest
                                 // already named; ask only for a bare number you
                                 // cannot place. Prices always come back in EUR;
                                 // send their own figure, never one you converted
  childrenAges?: (number|null)[], // one entry per child: their age, or null until
                                 // you ask. A null is refused, not priced; a child
                                 // left out is priced as though not coming
  language?: string,
  limit?: number,                // 1-20, default 10. There is no paging
  offerId?: string,              // re-price ONE room instead of searching: pass a handle
                                 // from an earlier answer and every field above is ignored
) → {
  unpriced: Hotel[],             // the only field always present
  priced?: PricedHotel[], aboveBudget?: PricedHotel[],
  place?: string, radiusUsedKm?: number, stay?: Stay,
  language?: string, hint?: string,

  // when nothing was found, or the input was rejected
  placeNotRecognised?: string, nearestBeyondRadiusKm?: number | null,
  nearestSearchedToKm?: number, requestedRadiusKm?: number,
  maxRadiusKm?: number, error?: string,
}

// limit out of range is a JSON-RPC -32602 error, never clamped.
// radius is two checks: value must be 1-100 whatever the unit, and the
// converted distance must be <= 100 km. The first is a -32602; the second
// is NOT: 63 mi is a normal 200 carrying error, requestedRadiusKm and
// maxRadiusKm, so handle it as data, not as a thrown exception.
Example call
list_hotels({
    city: "Bremen",
    radius: { value: 3, unit: "km" },
    checkIn: "2026-09-20",
    checkOut: "2026-09-22",
    adults: 2,
    limit: 20,
  })
Example response
// A real response, trimmed: one of the 9 priced hotels with one of its 4 rooms, and 2 of
  // the 5 unpriced ones. An unpriced hotel carries its own website and no booking link. unpriced
  // never holds more than 5, whatever limit asks for; priced is not capped.
  {
    "place": "Bremen",
    "radiusUsedKm": 3,
    "stay": {
      "checkIn": "2026-09-20",
      "checkOut": "2026-09-22",
      "adults": 2,
      "children": 0
    },
    "priced": [
      {
        "slug": "hotel-edel-weiss",
        "name": "Hotel Edel Weiss",
        "shortDescription": "Hotel Edel Weiss is a 4-star hotel in bremen, DE.",
        "tags": ["de", "bremen", "Heinz Vesecky", "wifi", "4-star", "mid-range"],
        "websiteUrl": "https://402.bluerails.com/go/dUKHiE78uA1G52v_",
        "listing": {
          "starRating": "4-star",
          "guestRating": 4.5,
          "address": {
            "street": "Bahnhofsplatz  8",
            "postalCode": "28195",
            "city": "Bremen",
            "countryCode": "de"
          },
          "city": "Bremen",
          "country": "de",
          "amenities": ["wifi"]
        },
        "distanceKm": 0.7753051258616253,
        "distanceApproximate": false,
        "rooms": [
          {
            "name": "Economy Doppelzimmer ",
            "price": 191.2,
            "cancelTerms": "100% (191.2 EUR)",
            "soldOut": false,
            "ratePlanName": "nicht kostenfrei Stornierbar",
            "breakfastIncluded": false,
            "bookingUrl": "https://402.bluerails.com/go/PAW3xVPfgBK4A9AL",
            "imageUrls": [
              "https://cdn.dirs21.de/v1/cdn/images/81426/1000041578.jpg?h=500",
              "https://cdn.dirs21.de/v1/cdn/images/81426/1000041579.jpg?h=500"
            ],
            "ratePlans": [
              {
                "ratePlanName": "Standardrate",
                "price": 201,
                "cancelTerms": "100% (201 EUR)",
                "soldOut": false,
                "breakfastIncluded": false,
                "bookingUrl": "https://402.bluerails.com/go/P7LJOKLBwSbUT7oc"
              },
              {
                "ratePlanName": "Experience Bremen: a 3-day mini-break",
                "price": 222.03,
                "cancelTerms": "100% (222.03 EUR)",
                "soldOut": false,
                "breakfastIncluded": true,
                "bookingUrl": "https://402.bluerails.com/go/-1HTu1Z5F5Jdl7Jj"
              }
            ]
          }
        ],
        "stayTotal": 191.2,
        "pricePerNight": 96,
        "bookingUrl": "https://402.bluerails.com/go/XGG8-qKrft7n0GYX",
        "datesApplied": true,
        "partyApplied": true
      }
    ],
    "unpriced": [
      {
        "slug": "hotel-classico-Bremen",
        "name": "Hotel Classico",
        "shortDescription": "Hotel Classico is a 4-star boutique hotel in Bremen, DE.",
        "websiteUrl": "https://402.bluerails.com/go/ttR9Is3Zd8r_OD-p",
        "tags": ["de", "Bremen", "boutique", "independent", "business", "4-star", "mid-range"],
        "distanceKm": 0.07897485301717645,
        "distanceApproximate": false,
        "listing": {
          "starRating": "4-star",
          "style": "boutique",
          "guestRating": 4.5,
          "address": {
            "street": "Hinter dem Schütting  1 A",
            "postalCode": "28195",
            "city": "Bremen",
            "countryCode": "de"
          },
          "city": "Bremen",
          "country": "de",
          "propertyType": "business",
          "chain": "independent"
        }
      },
      {
        "slug": "hotel-hanseatic",
        "name": "Hotel Hanseatic",
        "shortDescription": "Hotel Hanseatic is a 3-star hotel in bremen, DE.",
        "websiteUrl": "https://402.bluerails.com/go/UfGTZkFVVWnzFIuL",
        "tags": ["de", "bremen", "3-star", "independent", "business", "mid-range"],
        "distanceKm": 0.33778274072368647,
        "distanceApproximate": false,
        "listing": {
          "starRating": "3-star",
          "city": "bremen",
          "country": "de",
          "propertyType": "business",
          "chain": "independent"
        }
      }
    ]
  }

hotel_reserve

Book a room the guest chose, from any offer list_hotels returned an offerId for. It either books the room outright and the guest pays at the desk, or returns our checkout link and the room is held once they have paid.

Signature
hotel_reserve(
      offerId: string,          // from list_hotels, unedited. Valid 30 minutes.
      guestGivenName: string,
      guestLastName: string,
      guestEmail: string,       // the guest's own; the hotel's confirmation goes here
      guestPhone: string,       // any format the guest gives it in
      guestStreet: string,
      guestPostal: string,
      guestCity: string,
      guestCountry: string,     // 2 letters, where the guest LIVES, not where they travel
      guestStreetNo?: string,   // when the guest gives it separately from the street
      guestSalutation?: string, // only what the guest told you; never guess from a name
      acceptedTerms: boolean,   // true only after the guest agreed to THIS rate's terms
    ) → {
      // booked outright: the room is held and the guest pays at the desk
      confirmationNumber?: string, hotel?: string,
      checkIn?: string, checkOut?: string, payment?: string,

      // paid-for booking: the room is NOT held until the guest pays
      paymentUrl?: string,      // OUR checkout. The card is HELD, not charged. The only
                                // link this tool returns, never the hotel's own page.

      // TWO NUMBERS, FOR TWO DIFFERENT PEOPLE, and both endings carry this one.
      bookingReference?: string, // OURS: the only thing that lets the guest reach US
                                 // about a booking they made through us. Always present
                                 // on paymentUrl; on confirmationNumber it is absent only
                                 // when the booking could not be recorded, which changes
                                 // nothing else the guest is told.

      // on a refusal, and never alongside either ending
      error?: string, reason?: string,

      // on either: wording meant for the guest rather than for you
      tellTheGuest?: string,
    }

    // The hotel, room, rate, stay, party and price all come out of the signed
    // offerId, which is why none of them is a separate argument. You cannot
    // book a combination you were never shown, or different nights from the
    // ones you searched.
    //
    // Holding an offerId is the whole qualification: there is no second field
    // to check before calling, and no class of offer that is refused for what
    // it is rather than for what the engine says when asked.
Example call
hotel_reserve({
      offerId: "ofr3_4KQ2-9XPD-7M3F",
      guestGivenName: "Anna",
      guestLastName: "Weber",
      guestEmail: "anna.weber@example.com",
      guestPhone: "+49 421 555 0134",
      guestStreet: "Sögestraße",
      guestStreetNo: "12",
      guestPostal: "28195",
      guestCity: "Bremen",
      guestCountry: "DE",
      acceptedTerms: true,
    })
Example response
// Ending one: booked. The confirmation number is the hotel's own reference.
    {
      "confirmationNumber": "…",   // the HOTEL's. What the guest quotes AT the property.
      "bookingReference": "…",     // OURS. What they quote to reach US.
      "hotel": "ACHAT Hotel Bremen City",
      "checkIn": "2026-11-20",
      "checkOut": "2026-11-23",
      "payment": "Nothing has been charged. The guest pays the hotel directly at the desk.",
      "tellTheGuest": "Give them the confirmation number and tell them to keep it — the hotel will also email them. To change or cancel the room, they contact the hotel directly."
    }

    // Ending two: the rate has to be paid for. NOTHING is reserved yet: the
    // room is placed after the guest pays, and their card is held, not charged.
    {
      "paymentUrl": "https://402.bluerails.com/book/…",
      "bookingReference": "…",     // always here, and it OUTLIVES the link
      "hotel": "ACHAT Hotel Bremen City",
      "checkIn": "2026-11-20",
      "checkOut": "2026-11-23",
      "payment": "Nothing has been charged yet. The page holds the card; we take payment only once the hotel confirms the room.",
      "tellTheGuest": "Give them this link to pay. Their card is HELD, not charged — we take the money only once the hotel confirms the room, and release the hold if it cannot. The room is NOT reserved until they have paid, so price and availability can still change. The link expires, so if they do not use it soon, call list_hotels with that same offerId to re-price their room, then reserve again for a new link. Their booking reference is … — that is what they quote if they need to reach us, and it outlives the link."
    }

    // A refusal. This one is real, returned on 25 September 2026 for a call
    // that passed acceptedTerms: false. No room is touched before it fires.
    {
      "error": "The guest has not agreed to book this rate. Show them its cancellation terms where `list_hotels` gave any — where it gave none, tell them we hold none and that the hotel’s own terms apply — then call again once they have agreed.",
      "reason": "terms-not-accepted"
    }