Irmu Booking API

Searching Booking.com and reading its property pages, through the same crawler.

Operations 2

GET /booking/search Search Booking.com #
GET /booking/hotel Read a Booking.com property #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/irmu-com:irmu-com-booking-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

irmu-com-booking-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Irmu Com Booking API
  version: 1.0.0
  description: 'Operations tagged Booking across 2 of this provider''s published API definitions: app.irmu.com.yaml, irmu-com-openapi.yaml. Each path carries the servers of the definition it was published in.'
servers:
- url: https://app.irmu.com/api
  description: Production
tags:
- name: Booking
  description: Searching Booking.com and reading its property pages, through the same crawler.
paths:
  /booking/search:
    get:
      tags:
      - Booking
      summary: Search Booking.com
      operationId: bookingSearch
      description: 'Searches Booking.com for places to stay and returns the properties on

        the first page of results, one row per property card.


        You name a destination and, optionally, a stay; the search url is built

        from them. Results are always read from the English site, because the

        recipe that reads them reads English labels.


        Every field of a row is text as Booking prints it -- `price` is

        `"€ 120"`, `reviewScore` is `"8.7"` -- and a field the card does not

        show is absent from its row.


        **30 credits, flat.** `premium`, `country` and `city` choose where the

        crawl exits, not what it costs. A search that finds nothing is still

        charged for: it is Booking''s answer, and an empty `hotels` with `status`

        `done` is how it reads.


        The key goes in the `Authorization` header, or in `?key=`.'
      security:
      - apiKeyHeader: []
      - apiKeyQuery: []
      parameters:
      - name: destination
        in: query
        required: true
        description: 'Where to stay, as you would type it into Booking''s search box -- a

          city, a region, a landmark. Up to 200 characters.

          '
        schema:
          type: string
          maxLength: 200
        example: Rome
      - name: checkin
        in: query
        required: false
        description: 'The first night, as `YYYY-MM-DD`, today or later. **Requires

          `checkout`.**


          Optional: without dates Booking still shows the page, it just prices

          nothing.

          '
        schema:
          type: string
          format: date
        example: '2026-11-11'
      - name: checkout
        in: query
        required: false
        description: The day you leave, as `YYYY-MM-DD`, after `checkin`. **Requires `checkin`.**
        schema:
          type: string
          format: date
        example: '2026-11-12'
      - name: adults
        in: query
        required: false
        description: How many adults are staying.
        schema:
          type: integer
          minimum: 1
          maximum: 30
          default: 2
      - name: rooms
        in: query
        required: false
        description: How many rooms they need.
        schema:
          type: integer
          minimum: 1
          maximum: 30
          default: 1
      - name: currency
        in: query
        required: false
        description: 'The currency to price in, as a three-letter ISO 4217 code. Left to

          Booking when absent, which usually means the currency of the country

          the crawl exits from.

          '
        schema:
          type: string
          minLength: 3
          maxLength: 3
          pattern: ^[A-Za-z]{3}$
        example: EUR
      - name: premium
        in: query
        required: false
        description: 'Go through the premium proxy pool rather than the standard one. **On

          by default**, because the standard pool is the one Booking turns away;

          send `false` to use the standard pool. Free either way, unlike on

          `/crawl` -- the price is flat.


          `true`, `false`, `1`, `0`, `yes`, `no`, `on`, `off`, in any case.

          Anything else is refused rather than read as `false`.

          '
        schema:
          type: string
          default: 'true'
        example: 'true'
      - name: country
        in: query
        required: false
        description: 'The country to appear to be browsing from, as a two-letter ISO 3166-1

          alpha-2 code. Free. Booking prices and ranks by where you are, so this

          can change the answer.


          Required to use `city`.

          '
        schema:
          type: string
          minLength: 2
          maxLength: 2
          pattern: ^[A-Za-z]{2}$
        example: it
      - name: city
        in: query
        required: false
        description: 'The city inside that country, as the pool spells it. **Requires

          `country`.** Free here, where on `/crawl` it doubles the price.

          '
        schema:
          type: string
          pattern: ^[A-Za-z][A-Za-z0-9_-]{0,63}$
        example: rome
      - name: key
        in: query
        required: false
        description: 'The API key, as an alternative to the `Authorization` header. Ignored

          when a header is present.

          '
        schema:
          type: string
      responses:
        '200':
          description: The properties the search found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BookingSearchResult'
              example:
                url: https://www.booking.com/searchresults.en-gb.html?ss=Rome&checkin=2026-11-11&checkout=2026-11-12&group_adults=2&no_rooms=1&group_children=0
                destination: Rome
                checkin: '2026-11-11'
                checkout: '2026-11-12'
                adults: 2
                rooms: 1
                currency: null
                hotels:
                - name: Hotel Artemide
                  url: https://www.booking.com/hotel/it/artemide.en-gb.html
                  image: https://cf.bstatic.com/xdata/images/hotel/square240/1.jpg
                  price: € 212
                  priceInfo: 1 night, 2 adults
                  taxes: +€ 12 taxes and charges
                  address: Termini, Rome
                  distance: 1.2 km from centre
                  reviewScore: '9.1'
                  reviewWord: Wonderful
                  reviewCount: 3,214 reviews
                  stars: 4 out of 5 Property rating
                credits_charged: 30
                credits_remaining: 970
                job_id: 0198a9b6-1f4c-7c3e-9a2b-5d8e6f0a1b23_0198a9b6-2a71-7f0d-8c44-9e1b3c5d7f02
                status: done
                position: null
                debug: null
                error: null
        '202':
          description: 'Paid for and still going. Collect it from `/crawl/{job}`, free, as a

            slow crawl is collected. That endpoint answers the crawl envelope, so

            what is lifted into `hotels` here arrives under `data` there.

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BookingSearchResult'
        '401':
          description: 'No key, an unknown key, or a caller that is not an organization.

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                message: This endpoint is reached with an organization API key.
        '402':
          description: 'Not enough credits, or the daily limit is reached. Nothing is charged

            and no attempt is recorded.

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: 'A malformed request -- no destination, one date without the other, dates in the past, or a flag that is not a yes or a no.

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationError'
        '500':
          description: 'The crawl failed outright. A search that ran and found nothing is not

            this: it is a `200` with an empty `hotels`.


            **The credits are refunded**, so `credits_charged` is `0`. The attempt

            is still recorded against `www.booking.com`.

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BookingSearchResult'
              example:
                url: https://www.booking.com/searchresults.en-gb.html?ss=Rome&group_adults=2&no_rooms=1&group_children=0
                destination: Rome
                checkin: null
                checkout: null
                adults: 2
                rooms: 1
                currency: null
                hotels: []
                credits_charged: 0
                credits_remaining: 1000
                job_id: 0198a9b6-1f4c-7c3e-9a2b-5d8e6f0a1b23_0198a9b6-2a71-7f0d-8c44-9e1b3c5d7f02
                status: failed
                position: null
                debug: null
                error: Navigation timeout of 60000 ms exceeded
        '503':
          description: 'The crawl could not be routed through a proxy pool. Nothing is

            charged.

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                message: The crawler cannot route this crawl right now. Nothing was charged.
    servers:
    - url: https://app.irmu.com/api
      description: Production
  /booking/hotel:
    get:
      tags:
      - Booking
      summary: Read a Booking.com property
      operationId: bookingHotel
      description: 'Reads one Booking.com property page into a single object, with the parts

        Booking prints as blobs resolved into values: `price` and each room''s

        prices as `{ amount, currency, original, discount }`, `rating` as

        `{ score, word, count }`, `facilities` grouped as the page groups them.

        Beside those come the lists the page carries as text -- points of

        interest, review subscores, featured reviews, questions, sections.


        Room prices only exist for a stay, so send `checkin` and `checkout` to

        get them. The url''s own query string is ignored: the stay goes in this

        endpoint''s parameters, and the page is always read in English.


        **30 credits, flat.** A page that could not be read -- usually Booking''s

        challenge page -- is not charged for.


        The key goes in the `Authorization` header, or in `?key=`.'
      security:
      - apiKeyHeader: []
      - apiKeyQuery: []
      parameters:
      - name: url
        in: query
        required: true
        description: 'A Booking.com property page, `https://www.booking.com/hotel/<country>/<name>.html`,

          in any language. Anything else is refused.

          '
        schema:
          type: string
          format: uri
          maxLength: 2048
        example: https://www.booking.com/hotel/it/relais-la-maison-de-luxe-b-b.html
      - name: checkin
        in: query
        required: false
        description: 'The first night, as `YYYY-MM-DD`, today or later. **Requires

          `checkout`.**


          Optional: without dates Booking still shows the page, it just prices

          nothing.

          '
        schema:
          type: string
          format: date
        example: '2026-11-11'
      - name: checkout
        in: query
        required: false
        description: The day you leave, as `YYYY-MM-DD`, after `checkin`. **Requires `checkin`.**
        schema:
          type: string
          format: date
        example: '2026-11-12'
      - name: adults
        in: query
        required: false
        description: How many adults are staying.
        schema:
          type: integer
          minimum: 1
          maximum: 30
          default: 2
      - name: rooms
        in: query
        required: false
        description: How many rooms they need.
        schema:
          type: integer
          minimum: 1
          maximum: 30
          default: 1
      - name: currency
        in: query
        required: false
        description: 'The currency to price in, as a three-letter ISO 4217 code. Left to

          Booking when absent, which usually means the currency of the country

          the crawl exits from.

          '
        schema:
          type: string
          minLength: 3
          maxLength: 3
          pattern: ^[A-Za-z]{3}$
        example: EUR
      - name: premium
        in: query
        required: false
        description: 'Go through the premium proxy pool rather than the standard one. **On

          by default**, because the standard pool is the one Booking turns away;

          send `false` to use the standard pool. Free either way, unlike on

          `/crawl` -- the price is flat.


          `true`, `false`, `1`, `0`, `yes`, `no`, `on`, `off`, in any case.

          Anything else is refused rather than read as `false`.

          '
        schema:
          type: string
          default: 'true'
        example: 'true'
      - name: country
        in: query
        required: false
        description: 'The country to appear to be browsing from, as a two-letter ISO 3166-1

          alpha-2 code. Free. Booking prices and ranks by where you are, so this

          can change the answer.


          Required to use `city`.

          '
        schema:
          type: string
          minLength: 2
          maxLength: 2
          pattern: ^[A-Za-z]{2}$
        example: it
      - name: city
        in: query
        required: false
        description: 'The city inside that country, as the pool spells it. **Requires

          `country`.** Free here, where on `/crawl` it doubles the price.

          '
        schema:
          type: string
          pattern: ^[A-Za-z][A-Za-z0-9_-]{0,63}$
        example: rome
      - name: key
        in: query
        required: false
        description: 'The API key, as an alternative to the `Authorization` header. Ignored

          when a header is present.

          '
        schema:
          type: string
      responses:
        '200':
          description: The property.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BookingHotelResult'
              example:
                url: https://www.booking.com/hotel/it/relais-la-maison-de-luxe-b-b.en-gb.html?checkin=2026-11-11&checkout=2026-11-12&group_adults=2&no_rooms=1&group_children=0
                checkin: '2026-11-11'
                checkout: '2026-11-12'
                adults: 2
                rooms: 1
                currency: null
                hotel:
                  name: Relais La Maison de Luxe B&B
                  address:
                    '@type': PostalAddress
                    streetAddress: Via Nazionale 10
                    addressLocality: Rome
                    addressCountry: IT
                  description: Set in Rome, 600 metres from Termini...
                  rating:
                    score: 8.7
                    word: Fabulous
                    count: 426
                  price:
                    amount: 112
                    currency: EUR
                    original: 140
                    discount: 28
                  rooms:
                  - name: Double Room
                    prices:
                    - amount: 112
                      currency: EUR
                      original: 140
                      discount: 28
                  facilities:
                  - group: Internet
                    items:
                    - Free WiFi
                  images:
                  - https://cf.bstatic.com/xdata/images/hotel/max1024x768/1.jpg
                  pointsOfInterest:
                  - Termini 600 m
                  reviewSubscores:
                  - Staff 9.2
                  reviews:
                  - Great location and very clean.
                  questions:
                  - Is breakfast included?
                  sections:
                  - Property surroundings...
                  url: https://www.booking.com/hotel/it/relais-la-maison-de-luxe-b-b.en-gb.html?checkin=2026-11-11&checkout=2026-11-12&group_adults=2&no_rooms=1&group_children=0
                credits_charged: 30
                credits_remaining: 970
                job_id: 0198a9b6-1f4c-7c3e-9a2b-5d8e6f0a1b23_0198a9b6-2a71-7f0d-8c44-9e1b3c5d7f02
                status: done
                position: null
                debug: null
                error: null
        '202':
          description: 'Paid for and still going. Collect it from `/crawl/{job}`, free, as a

            slow crawl is collected. That endpoint answers the crawl envelope, so

            what is lifted into `hotel` here arrives under `data` there.

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BookingHotelResult'
        '401':
          description: 'No key, an unknown key, or a caller that is not an organization.

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                message: This endpoint is reached with an organization API key.
        '402':
          description: 'Not enough credits, or the daily limit is reached. Nothing is charged

            and no attempt is recorded.

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: 'A malformed request -- no url, a url that is not a Booking.com property page, one date without the other, dates in the past, or a flag that is not a yes or a no.

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationError'
        '500':
          description: 'Nothing came back: the crawl failed outright, or every block ran and

            found nothing -- Booking''s challenge page, or a layout the recipe no

            longer matches.


            **The credits are refunded either way**, so `credits_charged` is `0`.

            The attempt is still recorded against `www.booking.com`.

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BookingHotelResult'
              example:
                url: https://www.booking.com/hotel/it/relais-la-maison-de-luxe-b-b.en-gb.html?group_adults=2&no_rooms=1&group_children=0
                checkin: null
                checkout: null
                adults: 2
                rooms: 1
                currency: null
                hotel: null
                credits_charged: 0
                credits_remaining: 1000
                job_id: 0198a9b6-1f4c-7c3e-9a2b-5d8e6f0a1b23_0198a9b6-2a71-7f0d-8c44-9e1b3c5d7f02
                status: done
                position: null
                debug: null
                error: The crawler could not read that property page. Nothing was charged.
        '503':
          description: 'The crawl could not be routed through a proxy pool. Nothing is

            charged.

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                message: The crawler cannot route this crawl right now. Nothing was charged.
    servers:
    - url: https://app.irmu.com/api
      description: Production
components:
  schemas:
    BookingSearchResult:
      type: object
      description: 'What `/booking/search` answers. `hotels` is empty while the search is

        still running, when it failed and when it found nothing; `status` tells

        those apart.

        '
      required:
      - destination
      - hotels
      - url
      - checkin
      - checkout
      - adults
      - rooms
      - currency
      - credits_charged
      - credits_remaining
      - job_id
      - status
      - position
      - error
      - debug
      properties:
        url:
          type: string
          description: The Booking.com page that was read, as built from the request.
        checkin:
          type:
          - string
          - 'null'
          format: date
        checkout:
          type:
          - string
          - 'null'
          format: date
        adults:
          type: integer
        rooms:
          type: integer
        currency:
          type:
          - string
          - 'null'
        destination:
          type: string
        hotels:
          type: array
          description: 'One row per property card, each field as text the way Booking prints

            it. A field the card does not show is absent.

            '
          items:
            type: object
            properties:
              name:
                type: string
              url:
                type: string
              image:
                type: string
              price:
                type: string
              priceInfo:
                type: string
              taxes:
                type: string
              address:
                type: string
              distance:
                type: string
              reviewScore:
                type: string
              reviewWord:
                type: string
              reviewCount:
                type: string
              stars:
                type: string
        credits_charged:
          type: integer
          description: 30, or 0 when nothing came back.
        credits_remaining:
          type: integer
        job_id:
          type: string
          description: What this ran under. Collect a `202` from `/crawl/{job}` with it.
        status:
          type: string
          enum:
          - done
          - queued
          - running
          - failed
        position:
          type:
          - integer
          - 'null'
          description: Where it sits in the crawler's queue while `queued`.
        error:
          type:
          - string
          - 'null'
          description: Why nothing came back. Null otherwise.
        debug:
          description: 'Where the crawl had got to, when the crawler stopped waiting and

            looked. Only sent when the request carries `debuga=1` -- a screenshot

            and the page source run to a megabyte -- and null otherwise.

            '
          oneOf:
          - $ref: '#/components/schemas/CrawlSnapshot'
          - type: 'null'
    CrawlSnapshot:
      type: object
      description: 'Where a crawl had got to, for one that is still running or failed. Only

        sent when the request carries `debuga=1`.


        The crawler stops waiting long before it stops working, and when it stops

        waiting it looks at the tab: the url the browser is on, a picture of what

        is on screen, and the page source as it stands. That is what this is --

        a diagnosis for a request that timed out, not a result. Nothing is

        charged for it.


        Null on anything that is not still running. A finished or failed crawl has

        no tab left to look at, and a crawl still in the queue has not opened one

        yet.


        Two things to know when reading one. The image is the **viewport**, not

        the whole page, so it is not the picture `/screenshot` sells -- it is a

        thumbnail at quality 60, meant to be looked at rather than kept. And

        either artefact may be missing: they are captured independently, because a

        navigation can destroy one without touching the other, so half a snapshot

        is ordinary. The missing half reads as null rather than going absent.


        A blank image beside a near-empty `html` is not a broken capture -- it

        means the crawl stalled *inside* a navigation, with the old document

        already replaced. That is itself the answer.

        '
      required:
      - url
      - screenshot
      - html
      properties:
        url:
          type:
          - string
          - 'null'
          format: uri
          description: 'The url the tab was on, which a redirect may have moved away from the

            one you asked for.

            '
          example: https://shop.example/p/1
        screenshot:
          type:
          - string
          - 'null'
          format: byte
          description: 'A base64 JPEG of the viewport, with no `data:` prefix -- decode it as

            it stands. Null when the capture raced a navigation and lost.

            '
          example: /9j/4AAQSkZJRgABAQ...
        html:
          type:
          - string
          - 'null'
          description: 'The page source at that moment, half-loaded and all. Null when the

            capture raced a navigation and lost.

            '
          example: <!doctype html>...
    ValidationError:
      type: object
      required:
      - message
      - errors
      properties:
        message:
          type: string
        errors:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
    BookingHotelResult:
      type: object
      description: 'What `/booking/hotel` answers. `hotel` is null while the page is still

        being read and when it could not be; `status` and `error` tell those

        apart.

        '
      required:
      - hotel
      - url
      - checkin
      - checkout
      - adults
      - rooms
      - currency
      - credits_charged
      - credits_remaining
      - job_id
      - status
      - position
      - error
      - debug
      properties:
        url:
          type: string
          description: The Booking.com page that was read, as built from the request.
        checkin:
          type:
          - string
          - 'null'
          format: date
        checkout:
          type:
          - string
          - 'null'
          format: date
        adults:
          type: integer
        rooms:
          type: integer
        currency:
          type:
          - string
          - 'null'
        hotel:
          oneOf:
          - type: object
            additionalProperties: true
            description: 'The property. `name`, `address`, `description`, `rating`, `price`,

              `rooms`, `facilities` and `images` are resolved by the crawler;

              `pointsOfInterest`, `reviewSubscores`, `reviews`, `questions` and

              `sections` are lists of text. `price` is the cheapest of the room

              prices, and absent without a stay.

              '
          - type: 'null'
        credits_charged:
          type: integer
          description: 30, or 0 when nothing came back.
        credits_remaining:
          type: integer
        job_id:
          type: string
          description: What this ran under. Collect a `202` from `/crawl/{job}` with it.
        status:
          type: string
          enum:
          - done
          - queued
          - running
          - failed
        position:
          type:
          - integer
          - 'null'
          description: Where it sits in the crawler's queue while `queued`.
        error:
          type:
          - string
          - 'null'
          description: Why nothing came back. Null otherwise.
        debug:
          description: 'Where the crawl had got to, when the crawler stopped waiting and

            looked. Only sent when the request carries `debuga=1` -- a screenshot

            and the page source run to a megabyte -- and null otherwise.

            '
          oneOf:
          - $ref: '#/components/schemas/CrawlSnapshot'
          - type: 'null'
    Error:
      type: object
      required:
      - message
      properties:
        message:
          type: string
  securitySchemes:
    apiKeyHeader:
      type: http
      scheme: bearer
      description: '`Authorization: Bearer <key>`. The usual way in.

        '
    apiKeyQuery:
      type: apiKey
      in: query
      name: key
      description: '`?key=<key>`. Accepted on the crawl route only, and only when no

        `Authorization` header was sent.

        '
x-refined-from:
- app.irmu.com.yaml
- irmu-com-openapi.yaml