Airbnb API — Getting Started — FetchLayer Docs
Documentation menu
FetchLayer FetchLayer API

Getting Started

Airbnb listings, prices, guest reviews and availability as structured JSON. No Airbnb account — just your FetchLayer key.

1. Get your API key

Create an account and generate a FetchLayer API key from your dashboard. The same key works on every platform.

Create free account →

2. Search a place

location is the only required field. A page is up to 18 listings, each with its price, rating, badges, coordinates and photos.

bash
curl -X POST https://api.fetchlayer.dev/airbnb/search-listings \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"location":"Lisbon, Portugal"}'

With no dates, this route still prices — and tells you for when

Search always prices, because a results page with no prices answers nothing. Without checkIn/checkOut it uses the source's own default stay window and reports that window back on the response's checkIn and checkOut, plus a note: “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.” Read those two fields before comparing prices across requests made on different days.

Note the difference from listing-detail

/listing-detail does the opposite with no dates: it returns price: null rather than pricing a default stay. That asymmetry is deliberate — see step 3 — and assuming the two routes match is the one thing that makes a correct response look broken.

3. Read one listing in full

Pass a listing id or its airbnb.com/rooms/… URL for the full description, amenities, photos, the per-category rating breakdown and the host's public profile. Add dates to price a specific stay.

bash
curl -X POST https://api.fetchlayer.dev/airbnb/listing-detail \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"listing":"33579922","checkIn":"2026-12-08","checkOut":"2026-12-12","adults":2}'

Without dates, price is null — that is the answer, not a failure

A dateless call returns listing.price: null and the note “No check-in/check-out was given, so no price is included. Pass checkIn and checkOut to price a specific stay.” A detail read will not invent a stay you did not ask about. Dates that simply cannot be priced are also a 200 with a null price and a different note — “This stay (2026-12-08 to 2026-12-12) could not be priced. It may be unavailable for those dates.” — so check notes to tell the two nulls apart. checkIn and checkOut must be given together; one alone is a 400.

price is an object, not a number

A priced response returns {total, perNight, nights, taxesAndFees, originalTotal, qualifier, note}, each money value being {amount, currency, formatted}. A measured example: total $940.47 against originalTotal $1,083.09 over 4 nights, with taxesAndFees $36.70 broken out. An originalTotal above total is a discount; null means there is none to report.

A bad id is a 404 with a message

Sending {"listing":"1"} returns 404 and {"error":"no listing was found for id \"1\""}. Not a 503 — there is nothing to retry, the id names nothing.

4. Read the reviews — and know how many you got

The route worth coming here for. Every response reports the listing's full review count alongside how many came back.

bash
curl -X POST https://api.fetchlayer.dev/airbnb/listing-reviews \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"listing":"1695539435866678513","limit":10}'

The response says whether it is partial — no second call needed

That request returned totalReviewCount: 52, reviewCount: 10, hasMore: true, and in words: “These are 10 of the 52 reviews the source reports for this listing.” plus “More reviews are available. Continue with nextCursor.” Pull the whole set with {"limit":-1,"pages":-1} and the same fields say so instead — one note reading “These are 52 of the 52 reviews the source reports for this listing.”, hasMore: false, nextCursor: null. An average over ten of fifty-two and an average over all fifty-two are different numbers; this is how you know which one you have computed.

Reviews keep their original language

Each review's text is what the guest actually wrote, with a language code beside it and the source's own translatedText where one is published — the original is never replaced by a machine translation. Across 100 reviews measured on one Lisbon listing, 86 were English and the rest spanned nine other languages; 14 carried a published translation and 2 a host reply. The response also carries topics, the source's own tally of what reviewers wrote about.

Per-category ratings are on the listing, not on each review

A review has one overall rating. The accuracy / cleanliness / check-in / location / communication / value breakdown is published for the listing as a whole, on /listing-detail as listing.rating.categories.

5. Read the calendar — which nights are actually open

Dates on /listing-detail price a stay. This is the route that says which nights exist to book, night by night, up to twelve months in one call.

bash
curl -X POST https://api.fetchlayer.dev/airbnb/listing-availability \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"listing":"33579922","months":3}'

That returned 200 in 0.36s with pagesFetched: 1 and 91 nights, each one shaped like this:

json
{
  "date": "2026-10-13",
  "available": false,
  "bookable": false,
  "availableForCheckin": false,
  "availableForCheckout": true,
  "minNights": 2,
  "maxNights": 1125
}

A run of closed nights at the end of your range is usually the booking window, not 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, so a host who has opened four months still returns months five through twelve when you ask for twelve — marked available: false, indistinguishable from a booked night. A solid block running all the way to endDate is the edge of what the host has opened. Scattered closed nights in the middle of an open range are real closures.

Checkout allowed on a closed night means you have found where a booking ends

The night above is exactly that case: available: false with availableForCheckout: true and availableForCheckin: false is the last night of somebody else's stay — you cannot start on it, but a stay could end on it. Walk the nights and every place availableForCheckout flips true inside a closed run marks a departure. There were 19 such nights in a 365-night pull on this listing.

No per-night pricing here

/listing-availability says which nights are open and how long a stay has to be — minNights and maxNights are per night, not per listing. It does not price them. Use /listing-detail with checkIn and checkOut for a priced stay.

months goes up to 12 — the source's own ceiling per request, and 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","months":2} returned 61 nights starting 2027-03-01. A year of calendar is still one credit.

6. Sort a pile of pasted links first

Free, fetches nothing from Airbnb, and hands you the listing id and a canonical URL with the tracking and date parameters stripped.

bash
curl -X POST https://api.fetchlayer.dev/airbnb/resolve-url \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://www.airbnb.com/rooms/7743405?check_in=2026-11-10&check_out=2026-11-14"}'

That returns {"kind":"listing","canonicalUrl":"https://www.airbnb.com/rooms/7743405","listingId":"7743405","pagesFetched":0} in about a tenth of a second. A search link answers kind: "search"; a link it does not recognise answers kind: "unknown" with a note rather than an error to catch.

Speed and billing

One credit per page fetched, reported on every response as pagesFetched. Measured against production on 17 September 2026, three runs each: a one-page Lisbon search answered in 1.6–1.9s with 18 listings, a listing read in 0.96–1.19s, ten reviews in 0.53–0.58s, and three months of calendar in 0.36s with 91 nights. A review walk stops early when limit is reached, so {"pages":-1,"limit":25} billed one page, not twenty. /listing-availability is one credit per call up to its twelve-month ceiling — a year of calendar came back on a single page. /resolve-url is free and reports pagesFetched: 0.

Errors

A 400 names what is wrong — a missing required field, an unrecognised one (the body is a strict object), checkIn without checkOut, or a value past a source ceiling (months: 13 on /listing-availability). A 401 means the key is missing or invalid. A 404 on /listing-detail or /listing-availability means the id names no listing, and says so in the message.

Next steps