{
  "openapi": "3.1.0",
  "info": {
    "title": "Airbnb API",
    "version": "1.0.0",
    "description": "Search Airbnb listings for a place, read one listing in full with its host and per-category ratings, and read a listing's guest reviews. Reviews report their own completeness — every response carries the listing's full review count alongside how many came back, and says so in words, so a partial page is never mistaken for a complete one. Prices are structured objects with total, per-night, nights, taxes and fees, and any original pre-discount total, rather than a display string. Dates are optional throughout: search always prices and reports which stay window it priced, while listing-detail returns a null price until you pass dates. Availability is a separate route and a separate question: listing-availability returns the calendar night by night — open, closed, check-in and checkout flags, and the minimum and maximum stay length for each night — up to twelve months per call, without per-night pricing."
  },
  "servers": [
    {
      "url": "https://api.fetchlayer.dev/airbnb"
    }
  ],
  "security": [
    {
      "BearerAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API Key",
        "description": "Send your FetchLayer key as Authorization: Bearer ss-your-key."
      }
    }
  },
  "paths": {
    "/search-listings": {
      "post": {
        "operationId": "search-listings",
        "tags": [
          "Listings"
        ],
        "summary": "Search listings by location",
        "description": "Search Airbnb listings for a place. `location` is the only required field — a place name like \"Lisbon, Portugal\" or \"Austin, TX\" — and every result comes back with its price, rating, badges, coordinates and photos. **Dates are optional, and the two routes that take them behave differently on purpose.** `/search-listings` **always prices**: with no dates it prices every result for the source's own default stay window and reports that window back on `checkIn`/`checkOut` plus a note. `/listing-detail` **does not**: with no dates `listing.price` is **`null`** and a note says to pass `checkIn` and `checkOut` to price a stay. A search has to price to be a search; a detail read will not invent a stay you did not ask about. Read one route's behaviour onto the other and a `null` price looks like a broken listing when it is the documented answer. `price` is a structured object, not a number: `{total, perNight, nights, taxesAndFees, originalTotal, qualifier, note}`, where `total`, `perNight`, `taxesAndFees` and `originalTotal` are each `{amount, currency, formatted}`. `originalTotal` above `total` means a discount is applied — a measured example is `originalTotal: {amount: 1083.09}` against `total: {amount: 940.47}`. `taxesAndFees` is broken out rather than folded into the total, and `nights` is the stay length the figures cover. Any of the money fields can be `null` when the source did not publish it. **Dates here price a stay; they are not a calendar.** A stay that cannot be priced returns `price: null` with a note that it may be unavailable for those dates — which tells you about those dates and nothing else. For which nights are open, use [`/listing-availability`](/documentation/airbnb/endpoints/listing-availability), which returns the calendar night by night. Billing: 1 credit per page fetched, reported as `pagesFetched`. A page is up to 18 listings and costs the same whether it comes back full or half empty. Measured on 17 September 2026: `{\"location\":\"Lisbon, Portugal\"}` returned 200 in 1.6-1.9s across three runs, with 18 listings each time.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "location": {
                    "type": "string",
                    "description": "A place name, e.g. \"Austin, TX\" or \"Lisbon, Portugal\". The only required field."
                  },
                  "checkIn": {
                    "type": "string",
                    "description": "Stay start date, YYYY-MM-DD. Optional — without it, results are priced for the source's own default stay window, which comes back on `checkIn` in the response. Must be given together with `checkOut`: one without the other is a 400 saying \"checkIn and checkOut must be given together\"."
                  },
                  "checkOut": {
                    "type": "string",
                    "description": "Stay end date, YYYY-MM-DD. Required if `checkIn` is given, and vice versa."
                  },
                  "adults": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Adult guests, up to 50. Omitted means the source's own default, and the response echoes null rather than pretending to a number."
                  },
                  "children": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Children, up to 50."
                  },
                  "infants": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Infants, up to 50."
                  },
                  "pets": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Pets, up to 50."
                  },
                  "pages": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "How many pages of results to walk in one request, up to 15. Each page is up to 18 listings and 1 credit. Pass -1 for the maximum.",
                    "example": "1"
                  },
                  "cursor": {
                    "type": "string",
                    "description": "Continue from a previous response's `nextCursor`."
                  },
                  "format": {
                    "type": "string",
                    "description": "Envelope format: json or markdown.",
                    "example": "json"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "location"
                ]
              },
              "examples": {
                "default": {
                  "value": {
                    "location": "Lisbon, Portugal"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "transport": {
                      "type": "string",
                      "description": "The source. \"airbnb\"."
                    },
                    "notes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Caveats and completeness statements you can act on, in plain words. On /listing-reviews this is where the \"10 of the 52\" line lives; on the priced routes it is where the stay window used for pricing is stated; on /listing-availability it is where the booking-window warning lives — read it before reading a run of closed nights as a closure."
                    },
                    "pagesFetched": {
                      "type": "number",
                      "description": "Pages read, and what the request is billed at: 1 credit per page. A page costs the same whether it comes back full or half empty. /resolve-url reports 0 and is free."
                    },
                    "query": {
                      "type": "string",
                      "description": "The location that was searched, echoed back."
                    },
                    "listingCount": {
                      "type": "number",
                      "description": "How many listings came back in this response."
                    },
                    "hasMore": {
                      "type": "boolean",
                      "description": "Whether the source has further results beyond this page."
                    },
                    "nextCursor": {
                      "type": "string",
                      "description": "Pass back as `cursor` to continue. Null when there is nothing more."
                    },
                    "checkIn": {
                      "type": "string",
                      "description": "**The stay window the prices are actually for.** Your dates when you gave them; the source's own default window when you did not — so a price is never silently for dates you did not ask about. Read this before comparing prices across requests."
                    },
                    "checkOut": {
                      "type": "string",
                      "description": "The other end of that window, on the same terms."
                    },
                    "adults": {
                      "type": "number",
                      "description": "The guest count the prices are for, or null when you passed none and the source used its own default."
                    },
                    "children": {
                      "type": "number",
                      "description": "As `adults`."
                    },
                    "infants": {
                      "type": "number",
                      "description": "As `adults`."
                    },
                    "pets": {
                      "type": "number",
                      "description": "As `adults`."
                    },
                    "listings": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "description": "The listing's numeric id. Hand it straight to /listing-detail or /listing-reviews as `listing`."
                          },
                          "url": {
                            "type": "string",
                            "description": "The listing's airbnb.com/rooms/… address."
                          },
                          "name": {
                            "type": "string",
                            "description": "The row's headline as published, e.g. \"Apartment in Santa Maria Maior\" or a hotel name."
                          },
                          "roomName": {
                            "type": "string",
                            "description": "The specific room on a listing that has one, e.g. \"Casa Teva Lisboa Hotel, Deluxe double room\". Null on an ordinary whole-home listing."
                          },
                          "coordinates": {
                            "type": "object",
                            "additionalProperties": true,
                            "description": "`{latitude, longitude}` — approximate on search, as the source publishes them."
                          },
                          "rating": {
                            "type": "object",
                            "additionalProperties": true,
                            "description": "`{average, count}` — the aggregate score and how many reviews it rests on. The per-category breakdown is on /listing-detail."
                          },
                          "badges": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "description": "The source's own labels on the row. Measured values include \"Guest favorite\", \"Superhost\" and \"Featured hotel\"."
                          },
                          "photos": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "description": "Photo URLs for the row — six per listing in the searches measured."
                          },
                          "price": {
                            "type": "object",
                            "additionalProperties": true,
                            "description": "`price` is a structured object, not a number: `{total, perNight, nights, taxesAndFees, originalTotal, qualifier, note}`, where `total`, `perNight`, `taxesAndFees` and `originalTotal` are each `{amount, currency, formatted}`. `originalTotal` above `total` means a discount is applied — a measured example is `originalTotal: {amount: 1083.09}` against `total: {amount: 940.47}`. `taxesAndFees` is broken out rather than folded into the total, and `nights` is the stay length the figures cover. Any of the money fields can be `null` when the source did not publish it.",
                            "properties": {
                              "total": {
                                "type": "object",
                                "additionalProperties": true,
                                "description": "`{amount, currency, formatted}` — the whole stay, e.g. `{amount: 1245, currency: \"USD\", formatted: \"$1,245\"}`."
                              },
                              "perNight": {
                                "type": "object",
                                "additionalProperties": true,
                                "description": "`{amount, currency, formatted}` — the per-night figure, e.g. `{amount: 301.93, currency: \"USD\", formatted: \"$301.93\"}`."
                              },
                              "nights": {
                                "type": "number",
                                "description": "How many nights the figures cover."
                              },
                              "taxesAndFees": {
                                "type": "object",
                                "additionalProperties": true,
                                "description": "`{amount, currency, formatted}`, broken out rather than folded into `total`. Null when the source published no separate figure."
                              },
                              "originalTotal": {
                                "type": "object",
                                "additionalProperties": true,
                                "description": "The pre-discount total when one is published, in the same money shape. Above `total` means a discount is applied; null means there is none to report. Measured: `{amount: 1083.09}` against a `total` of `{amount: 940.47}`."
                              },
                              "qualifier": {
                                "type": "string",
                                "description": "What the figures are qualified as, in the source's own words — \"total\" or \"for 4 nights\"."
                              },
                              "note": {
                                "type": "string",
                                "description": "Whatever the source printed alongside the price, verbatim, e.g. \"Pay $0 today with no extra fees.\" or the room name on a hotel row. Null when there was none."
                              }
                            }
                          }
                        },
                        "additionalProperties": false
                      }
                    }
                  },
                  "additionalProperties": false
                },
                "examples": {
                  "default": {
                    "value": {
                      "transport": "airbnb",
                      "notes": [
                        "More results are available. Continue with nextCursor.",
                        "No check-in/check-out was given, so each listing's price reflects the source's own default stay (2026-10-16 to 2026-10-21), not necessarily the dates you want. Pass checkIn and checkOut to price a specific stay."
                      ],
                      "pagesFetched": 1,
                      "query": "Lisbon, Portugal",
                      "listingCount": 18,
                      "hasMore": true,
                      "nextCursor": "eyJzZWN0aW9uX29mZnNldCI6MCwiaXRlbXNfb2Zmc2V0IjoxOCwidmVyc2lvbiI6MX0=",
                      "checkIn": "2026-10-16",
                      "checkOut": "2026-10-21",
                      "adults": null,
                      "children": null,
                      "infants": null,
                      "pets": null,
                      "listings": [
                        {
                          "id": "33579922",
                          "url": "https://www.airbnb.com/rooms/33579922",
                          "name": "Apartment in Santa Maria Maior",
                          "roomName": null,
                          "coordinates": {
                            "latitude": 38.7132,
                            "longitude": -9.14376
                          },
                          "rating": {
                            "average": 4.95,
                            "count": 465
                          },
                          "badges": [
                            "Guest favorite"
                          ],
                          "photos": [
                            "https://a0.muscache.com/im/pictures/6ae6f001-a512-474b-96ce-bd7852242a3e.jpg"
                          ],
                          "price": {
                            "total": {
                              "amount": 1245,
                              "currency": "USD",
                              "formatted": "$1,245"
                            },
                            "originalTotal": null,
                            "perNight": {
                              "amount": 301.93,
                              "currency": "USD",
                              "formatted": "$301.93"
                            },
                            "nights": 4,
                            "taxesAndFees": {
                              "amount": 36.7,
                              "currency": "USD",
                              "formatted": "$36.70"
                            },
                            "qualifier": "total",
                            "note": "Pay $0 today with no extra fees."
                          }
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing Bearer token"
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ]
      }
    },
    "/listing-detail": {
      "post": {
        "operationId": "listing-detail",
        "tags": [
          "Listings"
        ],
        "summary": "Read one listing in full",
        "description": "Read one listing in full by its numeric id or its `airbnb.com/rooms/…` URL: title, description, property and room type, guest capacity, location, amenities, photos, the aggregate rating **with its per-category breakdown**, and the host's public profile — name, photo, Superhost and verified status, rating, years hosting, response rate and response time. **Dates are optional, and the two routes that take them behave differently on purpose.** `/search-listings` **always prices**: with no dates it prices every result for the source's own default stay window and reports that window back on `checkIn`/`checkOut` plus a note. `/listing-detail` **does not**: with no dates `listing.price` is **`null`** and a note says to pass `checkIn` and `checkOut` to price a stay. A search has to price to be a search; a detail read will not invent a stay you did not ask about. Read one route's behaviour onto the other and a `null` price looks like a broken listing when it is the documented answer. `price` is a structured object, not a number: `{total, perNight, nights, taxesAndFees, originalTotal, qualifier, note}`, where `total`, `perNight`, `taxesAndFees` and `originalTotal` are each `{amount, currency, formatted}`. `originalTotal` above `total` means a discount is applied — a measured example is `originalTotal: {amount: 1083.09}` against `total: {amount: 940.47}`. `taxesAndFees` is broken out rather than folded into the total, and `nights` is the stay length the figures cover. Any of the money fields can be `null` when the source did not publish it. **Dates here price a stay; they are not a calendar.** A stay that cannot be priced returns `price: null` with a note that it may be unavailable for those dates — which tells you about those dates and nothing else. For which nights are open, use [`/listing-availability`](/documentation/airbnb/endpoints/listing-availability), which returns the calendar night by night. **An id that names no listing is a 404 with a message** — `{\"error\":\"no listing was found for id \\\"1\\\"\"}` — not a 503. A wrong id is a wrong request, so there is nothing to retry; a stay that simply cannot be priced is still a 200, with `price: null` and a note. Billing: 1 credit per page fetched, reported as `pagesFetched`. Measured on 17 September 2026: `{\"listing\":\"33579922\"}` returned 200 in 0.96-1.19s across three runs.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "listing": {
                    "type": "string",
                    "description": "A listing: its `airbnb.com/rooms/…` URL, or its numeric id. The only required field."
                  },
                  "checkIn": {
                    "type": "string",
                    "description": "Stay start date, YYYY-MM-DD. Optional — **without it, `listing.price` is null by design** and a note says so. Must be given together with `checkOut`."
                  },
                  "checkOut": {
                    "type": "string",
                    "description": "Stay end date, YYYY-MM-DD. Required if `checkIn` is given, and vice versa."
                  },
                  "adults": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Adult guests to price the stay for, up to 50. Only used when `checkIn`/`checkOut` are given."
                  },
                  "format": {
                    "type": "string",
                    "description": "Envelope format: json or markdown.",
                    "example": "json"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "listing"
                ]
              },
              "examples": {
                "default": {
                  "value": {
                    "listing": "33579922",
                    "checkIn": "2026-12-08",
                    "checkOut": "2026-12-12",
                    "adults": 2
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "transport": {
                      "type": "string",
                      "description": "The source. \"airbnb\"."
                    },
                    "notes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Caveats and completeness statements you can act on, in plain words. On /listing-reviews this is where the \"10 of the 52\" line lives; on the priced routes it is where the stay window used for pricing is stated; on /listing-availability it is where the booking-window warning lives — read it before reading a run of closed nights as a closure."
                    },
                    "pagesFetched": {
                      "type": "number",
                      "description": "Pages read, and what the request is billed at: 1 credit per page. A page costs the same whether it comes back full or half empty. /resolve-url reports 0 and is free."
                    },
                    "listing": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "description": "The listing's numeric id."
                        },
                        "url": {
                          "type": "string",
                          "description": "Its canonical `airbnb.com/rooms/…` address."
                        },
                        "title": {
                          "type": "string",
                          "description": "The listing title as published by the host."
                        },
                        "propertyType": {
                          "type": "string",
                          "description": "The property kind in the source's own vocabulary, e.g. \"APARTMENT\"."
                        },
                        "roomType": {
                          "type": "string",
                          "description": "What is being let, e.g. \"ENTIRE_HOME\"."
                        },
                        "personCapacity": {
                          "type": "number",
                          "description": "How many guests the listing takes."
                        },
                        "descriptionHtml": {
                          "type": "string",
                          "description": "The host's full description, with the source's own markup preserved — measured at 3,330 characters on the example listing, against the one-line teaser a search row carries."
                        },
                        "location": {
                          "type": "object",
                          "additionalProperties": true,
                          "description": "`{area, coordinates: {latitude, longitude}, isExactLocation}`. **Check `isExactLocation`**: it was `false` on the listing measured, meaning the coordinates are the approximate ones the source publishes before a booking, not the front door."
                        },
                        "rating": {
                          "type": "object",
                          "additionalProperties": true,
                          "description": "`{average, count, categories}`. `categories` is the per-category breakdown as `[{category, average}]`, measured as ACCURACY, CLEANLINESS, CHECKIN, LOCATION, COMMUNICATION and VALUE. **This is where per-category ratings live** — individual reviews from /listing-reviews carry a single overall `rating`, not a breakdown."
                        },
                        "host": {
                          "type": "object",
                          "additionalProperties": true,
                          "description": "`{id, name, profilePictureUrl, isSuperhost, isVerified, rating: {average, count}, yearsHosting, monthsHosting, responseRate, responseTime, about, stats, highlights}`. `responseRate` and `responseTime` are the source's own phrases (\"Response rate: 100%\", \"Responds within an hour\"), `stats` is `[{label, value}]`, and `highlights` is the host's own profile lines."
                        },
                        "amenities": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "additionalProperties": true
                          },
                          "description": "`[{title, available, group}]`. `title` is the source's own wording and can carry detail (\"Fast wifi – 232 Mbps\", \"43 inch HDTV with standard cable\"). `available: false` marks an amenity the listing explicitly does not have, so absence and refusal are different answers."
                        },
                        "photos": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "additionalProperties": true
                          },
                          "description": "`[{url, caption}]` — 26 on the listing measured, against the six a search row carries. `caption` is null where the host wrote none."
                        },
                        "highlights": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "The source's own highlight lines for the listing. Empty on the listing measured."
                        },
                        "price": {
                          "type": "string",
                          "description": "**Null when no dates were given** — that is the documented answer, not a failure — and null again when the stay could not be priced, with a note saying it may be unavailable for those dates. Otherwise the structured object: `price` is a structured object, not a number: `{total, perNight, nights, taxesAndFees, originalTotal, qualifier, note}`, where `total`, `perNight`, `taxesAndFees` and `originalTotal` are each `{amount, currency, formatted}`. `originalTotal` above `total` means a discount is applied — a measured example is `originalTotal: {amount: 1083.09}` against `total: {amount: 940.47}`. `taxesAndFees` is broken out rather than folded into the total, and `nights` is the stay length the figures cover. Any of the money fields can be `null` when the source did not publish it.",
                          "properties": {
                            "total": {
                              "type": "object",
                              "additionalProperties": true,
                              "description": "`{amount, currency, formatted}` — the whole stay, e.g. `{amount: 1245, currency: \"USD\", formatted: \"$1,245\"}`."
                            },
                            "perNight": {
                              "type": "object",
                              "additionalProperties": true,
                              "description": "`{amount, currency, formatted}` — the per-night figure, e.g. `{amount: 301.93, currency: \"USD\", formatted: \"$301.93\"}`."
                            },
                            "nights": {
                              "type": "number",
                              "description": "How many nights the figures cover."
                            },
                            "taxesAndFees": {
                              "type": "object",
                              "additionalProperties": true,
                              "description": "`{amount, currency, formatted}`, broken out rather than folded into `total`. Null when the source published no separate figure."
                            },
                            "originalTotal": {
                              "type": "object",
                              "additionalProperties": true,
                              "description": "The pre-discount total when one is published, in the same money shape. Above `total` means a discount is applied; null means there is none to report. Measured: `{amount: 1083.09}` against a `total` of `{amount: 940.47}`."
                            },
                            "qualifier": {
                              "type": "string",
                              "description": "What the figures are qualified as, in the source's own words — \"total\" or \"for 4 nights\"."
                            },
                            "note": {
                              "type": "string",
                              "description": "Whatever the source printed alongside the price, verbatim, e.g. \"Pay $0 today with no extra fees.\" or the room name on a hotel row. Null when there was none."
                            }
                          }
                        },
                        "checkIn": {
                          "type": "string",
                          "description": "The stay start the price is for, echoed back. Null when no dates were given."
                        },
                        "checkOut": {
                          "type": "string",
                          "description": "The stay end, on the same terms."
                        }
                      },
                      "additionalProperties": false
                    }
                  },
                  "additionalProperties": false
                },
                "examples": {
                  "default": {
                    "value": {
                      "transport": "airbnb",
                      "notes": [],
                      "pagesFetched": 1,
                      "listing": {
                        "id": "33579922",
                        "url": "https://www.airbnb.com/rooms/33579922",
                        "title": "Chiado chic 2 bedroom 2 bathroom apt, top location",
                        "propertyType": "APARTMENT",
                        "roomType": "ENTIRE_HOME",
                        "personCapacity": 5,
                        "descriptionHtml": "Situated on Rua da Misericórdia, linking the vibrant neighborhoods of Chiado and Principe Real, my apartment features 2 bedrooms and 2 bathrooms. …",
                        "location": {
                          "area": "Lisbon",
                          "coordinates": {
                            "latitude": 38.7132,
                            "longitude": -9.14376
                          },
                          "isExactLocation": false
                        },
                        "rating": {
                          "average": 4.95,
                          "count": 465,
                          "categories": [
                            {
                              "category": "ACCURACY",
                              "average": 4.95
                            },
                            {
                              "category": "CLEANLINESS",
                              "average": 4.91
                            },
                            {
                              "category": "CHECKIN",
                              "average": 4.95
                            },
                            {
                              "category": "LOCATION",
                              "average": 4.97
                            },
                            {
                              "category": "COMMUNICATION",
                              "average": 4.97
                            },
                            {
                              "category": "VALUE",
                              "average": 4.87
                            }
                          ]
                        },
                        "host": {
                          "id": "RGVtYW5kVXNlcjoyNTMwMjk0Mzc=",
                          "name": "Mario",
                          "profilePictureUrl": "https://a0.muscache.com/im/pictures/user/User/original/85aa5987-1821-473e-bdd6-1fcc95bc794d.jpeg",
                          "isSuperhost": true,
                          "isVerified": true,
                          "rating": {
                            "average": 4.95,
                            "count": 465
                          },
                          "yearsHosting": 7,
                          "monthsHosting": 5,
                          "responseRate": "Response rate: 100%",
                          "responseTime": "Responds within an hour",
                          "stats": [
                            {
                              "label": "Reviews",
                              "value": "465"
                            },
                            {
                              "label": "Rating",
                              "value": "4.95"
                            },
                            {
                              "label": "Years hosting",
                              "value": "7"
                            }
                          ],
                          "highlights": [
                            "Born in the 60s",
                            "Favorite song in high school: The Logical Song - Supertramp"
                          ]
                        },
                        "amenities": [
                          {
                            "title": "Kitchen",
                            "available": true,
                            "group": null
                          },
                          {
                            "title": "Fast wifi – 232 Mbps",
                            "available": true,
                            "group": null
                          }
                        ],
                        "photos": [
                          {
                            "url": "https://a0.muscache.com/im/pictures/6ae6f001-a512-474b-96ce-bd7852242a3e.jpg",
                            "caption": null
                          }
                        ],
                        "highlights": [],
                        "price": {
                          "total": {
                            "amount": 1245,
                            "currency": "USD",
                            "formatted": "$1,245"
                          },
                          "originalTotal": null,
                          "perNight": {
                            "amount": 301.93,
                            "currency": "USD",
                            "formatted": "$301.93"
                          },
                          "nights": 4,
                          "taxesAndFees": {
                            "amount": 36.7,
                            "currency": "USD",
                            "formatted": "$36.70"
                          },
                          "qualifier": "total",
                          "note": "Pay $0 today with no extra fees."
                        },
                        "checkIn": "2026-12-08",
                        "checkOut": "2026-12-12"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing Bearer token"
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ]
      }
    },
    "/listing-reviews": {
      "post": {
        "operationId": "listing-reviews",
        "tags": [
          "Reviews"
        ],
        "summary": "Read a listing’s guest reviews",
        "description": "Guest reviews for one listing, by id or URL. Each review carries its rating, its text **in the language it was written in**, that language code, the source's own translation where one is published, the date, the guest's public first name, photo and home town, and the host's reply when there is one. **A reviews response tells you how complete it is, without a second call.** `totalReviewCount` is the listing's full review count as the source reports it, `reviewCount` is how many came back in this response, `hasMore` is the boolean, and `notes[]` states it in plain words. A real response measured on 17 September 2026: `notes: [\"These are 10 of the 52 reviews the source reports for this listing.\", \"More reviews are available. Continue with nextCursor.\"]`. Pull the whole set and the same field says so instead — `notes: [\"These are 52 of the 52 reviews the source reports for this listing.\"]`, with `hasMore: false` and `nextCursor: null` and no second note. That difference is the point: ten reviews and *the first ten of fifty-two* are different answers, and you can tell which one you are holding from the response in your hand. The response also carries `topics` — the source's own tally of what reviewers talked about, as `[{topic, count}]` — which gives you the shape of 465 reviews from a page of ten. Billing: 1 credit per page fetched, reported as `pagesFetched`. A page is 50 reviews by default; a walk stops early when `limit` is reached and `pagesFetched` reports what was actually read, so asking for `pages: -1` with a small `limit` does not bill for pages nobody fetched. Measured on 17 September 2026: `{\"listing\":\"33579922\",\"limit\":10}` returned 200 in 0.53-0.58s across three runs with 10 reviews and `pagesFetched: 1`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "listing": {
                    "type": "string",
                    "description": "A listing: its `airbnb.com/rooms/…` URL, or its numeric id. The only required field."
                  },
                  "limit": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Maximum reviews returned in total, up to 1000. Pass -1 for the maximum.",
                    "example": "50"
                  },
                  "pages": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "How many pages of reviews to walk in one request, up to 20. Each page fetched is 1 credit. Pass -1 for the maximum — combined with `limit: -1` that is how you pull every review a listing has.",
                    "example": "1"
                  },
                  "cursor": {
                    "type": "string",
                    "description": "Continue from a previous response's `nextCursor`."
                  },
                  "format": {
                    "type": "string",
                    "description": "Envelope format: json or markdown.",
                    "example": "json"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "listing"
                ]
              },
              "examples": {
                "default": {
                  "value": {
                    "listing": "1695539435866678513",
                    "limit": 10
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "transport": {
                      "type": "string",
                      "description": "The source. \"airbnb\"."
                    },
                    "notes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Caveats and completeness statements you can act on, in plain words. On /listing-reviews this is where the \"10 of the 52\" line lives; on the priced routes it is where the stay window used for pricing is stated; on /listing-availability it is where the booking-window warning lives — read it before reading a run of closed nights as a closure."
                    },
                    "pagesFetched": {
                      "type": "number",
                      "description": "Pages read, and what the request is billed at: 1 credit per page. A page costs the same whether it comes back full or half empty. /resolve-url reports 0 and is free."
                    },
                    "listingId": {
                      "type": "string",
                      "description": "The listing the reviews belong to, echoed back — useful when you passed a URL."
                    },
                    "totalReviewCount": {
                      "type": "number",
                      "description": "**The listing's full review count as the source reports it.** Compare it to `reviewCount` and you know whether you are holding all of them, with no second call. This is the field that makes a partial answer honest."
                    },
                    "reviewCount": {
                      "type": "number",
                      "description": "How many reviews came back in this response."
                    },
                    "hasMore": {
                      "type": "boolean",
                      "description": "Whether there are further reviews past this page. False, with `nextCursor: null` and a single \"N of N\" note, is what a complete pull looks like."
                    },
                    "nextCursor": {
                      "type": "string",
                      "description": "Pass back as `cursor` to continue. Null when there is nothing more."
                    },
                    "topics": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      },
                      "description": "`[{topic, count}]` — the source's own tally of what reviewers wrote about, e.g. `{topic: \"Cleanliness\", count: 14}`. Ten topics on the listings measured."
                    },
                    "reviews": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "description": "The review's id."
                          },
                          "rating": {
                            "type": "number",
                            "description": "The guest's overall score for the stay. A single number — the per-category breakdown is published for the listing as a whole on /listing-detail (`listing.rating.categories`), not per review."
                          },
                          "text": {
                            "type": "string",
                            "description": "**The review as the guest wrote it**, in their own language. Never replaced by a machine translation."
                          },
                          "language": {
                            "type": "string",
                            "description": "The language `text` is written in, as a code. Measured across 100 reviews on one listing: 86 en, plus de, pt, ko, fr, ja, is, nl, no and es — so a listing's reviews are not one language, and this is the field to branch on."
                          },
                          "translatedText": {
                            "type": "string",
                            "description": "The source's own published translation, where there is one — 14 of those 100 reviews carried it. Null otherwise. The original in `text` is always there either way."
                          },
                          "createdAt": {
                            "type": "string",
                            "description": "An ISO timestamp, e.g. \"2026-09-03T08:21:19Z\"."
                          },
                          "reviewer": {
                            "type": "object",
                            "additionalProperties": true,
                            "description": "`{id, firstName, profilePictureUrl, location}` — the guest's public profile only. `location` is their stated home town where they published one (\"Boston, Massachusetts\", \"Tokyo, Japan\"), null where they did not."
                          },
                          "hostResponse": {
                            "type": "object",
                            "additionalProperties": true,
                            "description": "`{text, createdAt}` when the host replied, null otherwise — 2 of 100 on the listing measured. `createdAt` here is the month the source prints, e.g. \"October 2025\", not an ISO timestamp."
                          }
                        },
                        "additionalProperties": false
                      }
                    }
                  },
                  "additionalProperties": false
                },
                "examples": {
                  "default": {
                    "value": {
                      "transport": "airbnb",
                      "notes": [
                        "These are 10 of the 52 reviews the source reports for this listing.",
                        "More reviews are available. Continue with nextCursor."
                      ],
                      "pagesFetched": 1,
                      "listingId": "1695539435866678513",
                      "totalReviewCount": 52,
                      "reviewCount": 10,
                      "hasMore": true,
                      "nextCursor": "10",
                      "topics": [
                        {
                          "topic": "Cleanliness",
                          "count": 14
                        },
                        {
                          "topic": "Hospitality",
                          "count": 14
                        },
                        {
                          "topic": "Pool",
                          "count": 13
                        },
                        {
                          "topic": "Location",
                          "count": 12
                        }
                      ],
                      "reviews": [
                        {
                          "id": "1766530781113184592",
                          "rating": 4,
                          "text": "The hotel room was lovely and spotless and had a coffee machine and sink in the room which was great. The staff were really friendly and helpful. The only down side was the swimming pool was tiny, not as it looked in the photo. …",
                          "language": "en",
                          "translatedText": null,
                          "createdAt": "2026-09-03T08:21:19Z",
                          "reviewer": {
                            "id": "584964753",
                            "firstName": "Estelle",
                            "profilePictureUrl": "https://a0.muscache.com/im/pictures/user/User/original/0db2697f-2644-41b4-aa04-ac2edce9d6ae.jpeg",
                            "location": null
                          },
                          "hostResponse": null
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing Bearer token"
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ]
      }
    },
    "/listing-availability": {
      "post": {
        "operationId": "listing-availability",
        "tags": [
          "Availability"
        ],
        "summary": "Read a listing’s availability calendar",
        "description": "Which nights one listing is open, closed or booked — night by night, by id or URL. Every night carries `available`, `bookable`, `availableForCheckin`, `availableForCheckout`, and the `minNights`/`maxNights` the host enforces for a stay starting on it, so you get not only which nights are free but how long a stay has to be to take them. Up to a year in one call: `months` goes to 12 — the source's own ceiling per request, and `months: 13` is a 400 saying so — with `-1` meaning that maximum. `from` is any date inside the first month you want and the range snaps to that whole month, so `from: \"2027-03-15\"` starts the calendar at `2027-03-01`. **The booking-window edge — read this before reading a run of closed nights as a closure.** Every response carries this note verbatim: `\"Dates beyond the host's own booking window come back marked unavailable rather than omitted — a long unbroken run of closed nights near the end of this window usually means the window, not a closure.\"` The source answers every night you ask for. A host who has opened the next four months for booking still returns nights five through twelve when you ask for twelve — marked `available: false`, exactly like a night someone has booked. So a long unbroken run of closed nights at the **far end** of your range is the edge of what the host has opened, not a fully booked property: the calendar simply stops there, and the response fills the rest in rather than truncating. Scattered closed nights in the middle of an otherwise open range are real closures; a solid block running to `endDate` is the window. **A closed night that still allows checkout is the far end of a booked stay.** `available: false` with `availableForCheckout: true` and `availableForCheckin: false` is the last night of somebody else's booking: you cannot start a stay on it, but a stay could end on it. That pairing is how you find where a booking ends — walk the nights and every place `availableForCheckout` flips true inside a closed run marks a departure. Measured: 19 such nights in a 365-night pull on listing `33579922`. **No per-night pricing here.** This route answers which nights are open and how long a stay has to be; it does not price them. For a priced stay, call [`/listing-detail`](/documentation/airbnb/endpoints/listing-detail) with `checkIn` and `checkOut`. Billing: 1 credit per page fetched, reported as `pagesFetched` — and a year of calendar is one page, not twelve. Measured on 17 September 2026: `{\"listing\":\"33579922\"}` returned 200 in 0.36s with `pagesFetched: 1` and 91 nights across the three default months; the same listing with `months: -1` returned 365 nights, still `pagesFetched: 1`. An id that names no listing is a 404 with a message, exactly as on /listing-detail.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "listing": {
                    "type": "string",
                    "description": "A listing: its `airbnb.com/rooms/…` URL, or its numeric id. The only required field."
                  },
                  "from": {
                    "type": "string",
                    "description": "The first month to read, given as **any date within it**, YYYY-MM-DD — the range snaps to that whole month, so `2027-03-15` and `2027-03-01` both start the calendar on 1 March 2027. Defaults to the current month.",
                    "example": "the current month"
                  },
                  "months": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "How many months of calendar to return, up to 12 — that ceiling is the source's own limit per request, not ours, and asking for 13 is a 400 reading \"months: Must be -1 (the maximum) or between 1 and 12\". Pass -1 for the maximum.",
                    "example": "3"
                  },
                  "format": {
                    "type": "string",
                    "description": "Envelope format: json or markdown.",
                    "example": "json"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "listing"
                ]
              },
              "examples": {
                "default": {
                  "value": {
                    "listing": "33579922"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "transport": {
                      "type": "string",
                      "description": "The source. \"airbnb\"."
                    },
                    "notes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Caveats and completeness statements you can act on, in plain words. On /listing-reviews this is where the \"10 of the 52\" line lives; on the priced routes it is where the stay window used for pricing is stated; on /listing-availability it is where the booking-window warning lives — read it before reading a run of closed nights as a closure."
                    },
                    "pagesFetched": {
                      "type": "number",
                      "description": "Pages read, and what the request is billed at: 1 credit per page. A page costs the same whether it comes back full or half empty. /resolve-url reports 0 and is free."
                    },
                    "listingId": {
                      "type": "string",
                      "description": "The listing the calendar belongs to, echoed back — useful when you passed a URL."
                    },
                    "startDate": {
                      "type": "string",
                      "description": "The first night in the range, YYYY-MM-DD. The first of the month `from` falls in, or of the current month when you gave none."
                    },
                    "endDate": {
                      "type": "string",
                      "description": "The last night in the range. **Where a run of closed nights ends here, read the booking window before reading a closure** — see the note the response carries."
                    },
                    "nights": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": {
                            "type": "string",
                            "description": "The night, YYYY-MM-DD. One entry per night, none skipped: 91 entries for the three default months, 365 for `months: -1`."
                          },
                          "available": {
                            "type": "boolean",
                            "description": "Whether the night is open. `false` covers both a night somebody has booked and a night beyond what the host has opened for booking — the two are not distinguished here, which is exactly why the booking-window note matters."
                          },
                          "bookable": {
                            "type": "boolean",
                            "description": "Whether the night can actually be booked. Can be `null` where the source published no answer — measured `null` on nights in the current month that have already passed."
                          },
                          "availableForCheckin": {
                            "type": "boolean",
                            "description": "Whether a stay may **start** on this night. An open night with this `false` sits inside a gap too short for the host's minimum stay."
                          },
                          "availableForCheckout": {
                            "type": "boolean",
                            "description": "**Whether a stay may end on this night — and the field that finds the end of a booking.** `true` on a night that is `available: false` and `availableForCheckin: false` is the far end of somebody else's stay: you could check out on it, you could not check in. Measured on 2026-10-13 of the example response below."
                          },
                          "minNights": {
                            "type": "number",
                            "description": "The shortest stay the host allows starting on this night. It varies night to night — 1 on some nights and 2 on others in a single measured calendar — so it is per night, not per listing."
                          },
                          "maxNights": {
                            "type": "number",
                            "description": "The longest stay allowed starting on this night. Often a very large number (1125 on the listing measured), which means \"no real limit\" rather than a considered cap."
                          }
                        },
                        "additionalProperties": false
                      }
                    }
                  },
                  "additionalProperties": false
                },
                "examples": {
                  "default": {
                    "value": {
                      "transport": "airbnb",
                      "notes": [
                        "Dates beyond the host's own booking window come back marked unavailable rather than omitted — a long unbroken run of closed nights near the end of this window usually means the window, not a closure."
                      ],
                      "pagesFetched": 1,
                      "listingId": "33579922",
                      "startDate": "2026-09-01",
                      "endDate": "2026-11-30",
                      "nights": [
                        {
                          "date": "2026-10-10",
                          "available": false,
                          "bookable": false,
                          "availableForCheckin": false,
                          "availableForCheckout": false,
                          "minNights": 2,
                          "maxNights": 1125
                        },
                        {
                          "date": "2026-10-11",
                          "available": true,
                          "bookable": true,
                          "availableForCheckin": true,
                          "availableForCheckout": false,
                          "minNights": 2,
                          "maxNights": 1125
                        },
                        {
                          "date": "2026-10-12",
                          "available": true,
                          "bookable": true,
                          "availableForCheckin": false,
                          "availableForCheckout": true,
                          "minNights": 2,
                          "maxNights": 1125
                        },
                        {
                          "date": "2026-10-13",
                          "available": false,
                          "bookable": false,
                          "availableForCheckin": false,
                          "availableForCheckout": true,
                          "minNights": 2,
                          "maxNights": 1125
                        },
                        {
                          "date": "2026-10-14",
                          "available": false,
                          "bookable": false,
                          "availableForCheckin": false,
                          "availableForCheckout": false,
                          "minNights": 2,
                          "maxNights": 1125
                        },
                        {
                          "date": "2026-10-15",
                          "available": true,
                          "bookable": true,
                          "availableForCheckin": true,
                          "availableForCheckout": false,
                          "minNights": 2,
                          "maxNights": 1125
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing Bearer token"
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ]
      }
    },
    "/resolve-url": {
      "post": {
        "operationId": "resolve-url",
        "tags": [
          "Utilities"
        ],
        "summary": "Read an Airbnb link",
        "description": "Read any Airbnb link and say what it names — a listing or a search — along with the listing id it encodes and its canonical URL, ready to hand to the other routes. The right first step for a pile of pasted links: it sorts them before a single page fetch is spent, and a link it does not recognise comes back as `kind: \"unknown\"` with a note saying so rather than as an error you have to catch. **Free.** It makes no request to Airbnb at all, answered in ~0.10s across the calls measured on 17 September 2026, and reports `pagesFetched: 0`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "Any Airbnb link — a listing or a search."
                  },
                  "format": {
                    "type": "string",
                    "description": "Envelope format: json or markdown.",
                    "example": "json"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "url"
                ]
              },
              "examples": {
                "default": {
                  "value": {
                    "url": "https://www.airbnb.com/rooms/7743405?check_in=2026-11-10&check_out=2026-11-14"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "transport": {
                      "type": "string",
                      "description": "The source. \"airbnb\"."
                    },
                    "notes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Caveats and completeness statements you can act on, in plain words. On /listing-reviews this is where the \"10 of the 52\" line lives; on the priced routes it is where the stay window used for pricing is stated; on /listing-availability it is where the booking-window warning lives — read it before reading a run of closed nights as a closure."
                    },
                    "pagesFetched": {
                      "type": "number",
                      "description": "Pages read, and what the request is billed at: 1 credit per page. A page costs the same whether it comes back full or half empty. /resolve-url reports 0 and is free."
                    },
                    "kind": {
                      "type": "string",
                      "description": "\"listing\" when the link names one, \"search\" for a search link, or \"unknown\" when it names neither — the last with a note reading \"This link does not name a listing this service recognizes.\" rather than an error."
                    },
                    "canonicalUrl": {
                      "type": "string",
                      "description": "The listing's canonical `airbnb.com/rooms/…` address, with tracking and date parameters stripped. Null on a search or an unknown link."
                    },
                    "listingId": {
                      "type": "string",
                      "description": "The listing id the link encodes, ready to pass to /listing-detail or /listing-reviews as `listing`. Null on a search or an unknown link."
                    }
                  },
                  "additionalProperties": false
                },
                "examples": {
                  "default": {
                    "value": {
                      "transport": "airbnb",
                      "notes": [],
                      "pagesFetched": 0,
                      "kind": "listing",
                      "canonicalUrl": "https://www.airbnb.com/rooms/7743405",
                      "listingId": "7743405"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing Bearer token"
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ]
      }
    }
  }
}