Airbnb API & Scraper
The Airbnb API returns short-term rental listings, prices and host data as clean JSON.
🤖 Using an AI assistant? Copy this link into ChatGPT / Claude / Cursor — it reads every endpoint and parameter instantly and tells you if this API fits your use case.
The primary stays.search endpoint returns stays with id, name, rating, price, coordinates, badges and a pagination cursor, and you can pull a stays.detail, a host.profile, stays.reviews and an availability_calendar, plus a separate properties search. It is built for travel apps, pricing monitors and short-term-rental analytics that need live Airbnb data without scraping a defended site. One ReefAPI key, one shared credit pool, the standard { ok, data, meta, error } envelope.
Airbnb listing ids, and why price and rating arrive as strings
Airbnb publishes its search cards as rendered text, so several fields that look numeric are strings you have to parse. Listing ids also come in two very different lengths depending on when the listing was created. All values below were measured on 2026-08-27 on a Lisbon search for 2026-09-16 to 2026-09-21, two adults, currency USD.
| Field | Measured example | What it means |
|---|---|---|
| listing_id, legacy | 8096741, 2684458, 29330369 | Seven or eight digits. Older listings. |
| listing_id, modern | 1476784482714562767 | Eighteen or nineteen digits. Both forms are accepted by every action. |
| items[].price | "$1,194 total, originally $1,293" | A string. "total" means the whole stay including taxes, not a nightly rate. |
| items[].rating | "5.0 (279)" | A string: score out of 5, then the review count in parentheses. Two decimals appear too, such as "4.84 (408)". |
| items[].badges | ["Guest favorite"], ["Superhost"], [] | An empty array is normal and means neither badge applies. |
| parsed.room_type | "Entire home/apt" | A display value. The search filter takes entire_home, private_room, shared_room or hotel_room instead. |
| parsed.property_type | "Entire loft" | Free text, more specific than room_type. |
| parsed.ratings.review_count | "408" | Also a string, while parsed.ratings.overall (4.84) is a number. |
| ratings sub-scores | accuracy 4.9, checkin 4.96, cleanliness 4.92, communication 4.97, location 4.41, value 4.65 | Six categories, each out of 5. |
| coordinate lat / lng | 38.7177 / -9.1342 | Four decimals. Airbnb publishes an approximate pin, not the street address. |
| calendar day | available, availableForCheckin, availableForCheckout, bookable, minNights, maxNights | availableForCheckin and availableForCheckout differ on the same day; minNights varied between 1 and 3 within two months. |
| raw_count vs unique_count | 48 vs 30 | Airbnb repeats cards across page sections. unique_count is the figure after de-duplication. |
Search cards carry a raw block per listing and stays.detail returns both parsed and raw, so a single 30-item search response measured 959 KB and one detail call 532 KB. Read from items[] and parsed, and ignore raw unless you need something not surfaced there.
Real request and response JSON
Captured from the indexed primary action, stays.search, on .
{
"method": "POST",
"url": "https://api.reefapi.com/airbnb/v1/stays.search",
"headers": {
"x-api-key": "$REEF_KEY",
"content-type": "application/json"
},
"body": {
"query": "Istanbul, Turkey",
"checkin": "2026-08-10",
"checkout": "2026-08-15",
"adults": 2
}
}{
"ok": true,
"meta": {
"api": "airbnb",
"endpoint": "stays.search",
"mode": "live",
"latency_ms": 1299.4,
"record_count": 26,
"bytes": 906622,
"cache_hit": false,
"expected_tokens_ok": "[redacted-secret]",
"parser": "stays_search_html",
"pagination": {
"has_more": true,
"next_cursor": "eyJzZWN0aW9uX29mZnNldCI6MCwiaXRlbXNfb2Zmc2V0IjozNiwidmVyc2lvbiI6MX0=",
"cursor_count": 15
}
},
"data": {
"items": [
{
"id": "[redacted-phone]",
"name": "[redacted-name]",
"rating": "5.0 (28)",
"rating_label": "5.0 out of 5 average rating, 28 reviews",
"price": "€363 total, originally €646",
"coordinate": {
"lat": 41.0274,
"lng": 28.9758
},
"badges": [
"Guest favorite"
],
"subtitle": "A Modern Apartment in Galata – 2 Minutes to the Subway",
"raw": {
"__typename": "StaySearchResult",
"avgRatingA11yLabel": "5.0 out of 5 average rating, 28 reviews",
"avgRatingLocalized": "5.0 (28)",
"listingParamOverrides": null,
"structuredDisplayPrice": {
"__typename": "[trimmed-depth]",
"primaryLine": "[trimmed-depth]",
"secondaryLine": "[trimmed-depth]",
"explanationData": "[trimmed-depth]",
"explanationDataDisplayPosition": "[trimmed-depth]",
"displayPriceStyle": "[trimmed-depth]",
"loggingMetadata": "[trimmed-depth]"
},
"badges": [
"[trimmed-depth]"
],
"contextualPictures": [
"[trimmed-depth]",
"[trimmed-depth]",
"[trimmed-depth]"
],
"paymentMessages": [
"[trimmed-depth]",
"[trimmed-depth]"
],
"title": "Apartment in Istanbul",
"subtitle": "A Modern Apartment in Galata – 2 Minutes to the Subway",
"nameLocalized": {
"__typename": "[trimmed-depth]",
"localizedStringWithTranslationPreference": "[trimmed-depth]"
},
"propertyId": null,
"structuredContent": {
"__typename": "[trimmed-depth]",
"distance": "[trimmed-depth]",
"mapCategoryInfo": "[trimmed-depth]",
"mapPrimaryLine": "[trimmed-depth]",
"mapSecondaryLine": "[trimmed-depth]",
"primaryLine": "[trimmed-depth]",
"secondaryLine": "[trimmed-depth]",
"tertiaryLine": "[trimmed-depth]",
"reviewSnippet": "[trimmed-depth]",
"explanation": "[trimmed-depth]"
},
"passportData": null,
"demandStayListing": {
"__typename": "[trimmed-depth]",
"id": "[trimmed-depth]",
"location": "[trimmed-depth]",
"description": "[trimmed-depth]",
"wishlistItems": "[trimmed-depth]",
"nameLocale": "[trimmed-depth]"
},
"priceBreakdownMessages": []
}
},
{
"id": "[redacted-phone]",
"name": "[redacted-name]",
"rating": "4.82 (68)",
"rating_label": "4.82 out of 5 average rating, 68 reviews",
"price": "€316 total, originally €405",
"coordinate": {
"lat": 41.0298,
"lng": 28.97502
},
"badges": [
"Guest favorite"
],
"subtitle": "Balcony Studio• Sea View Terrace | In The Taksim",
"raw": {
"__typename": "StaySearchResult",
"avgRatingA11yLabel": "4.82 out of 5 average rating, 68 reviews",
"avgRatingLocalized": "4.82 (68)",
"listingParamOverrides": null,
"structuredDisplayPrice": {
"__typename": "[trimmed-depth]",
"primaryLine": "[trimmed-depth]",
"secondaryLine": "[trimmed-depth]",
"explanationData": "[trimmed-depth]",
"explanationDataDisplayPosition": "[trimmed-depth]",
"displayPriceStyle": "[trimmed-depth]",
"loggingMetadata": "[trimmed-depth]"
},
"badges": [
"[trimmed-depth]"
],
"contextualPictures": [
"[trimmed-depth]",
"[trimmed-depth]",
"[trimmed-depth]"
],
"paymentMessages": [
"[trimmed-depth]",
"[trimmed-depth]"
],
"title": "Hotel in Istanbul",
"subtitle": "Balcony Studio• Sea View Terrace | In The Taksim",
"nameLocalized": {
"__typename": "[trimmed-depth]",
"localizedStringWithTranslationPreference": "[trimmed-depth]"
},
"propertyId": null,
"structuredContent": {
"__typename": "[trimmed-depth]",
"distance": "[trimmed-depth]",
"mapCategoryInfo": "[trimmed-depth]",
"mapPrimaryLine": "[trimmed-depth]",
"mapSecondaryLine": "[trimmed-depth]",
"primaryLine": "[trimmed-depth]",
"secondaryLine": "[trimmed-depth]",
"tertiaryLine": "[trimmed-depth]",
"reviewSnippet": "[trimmed-depth]",
"explanation": "[trimmed-depth]"
},
"passportData": null,
"demandStayListing": {
"__typename": "[trimmed-depth]",
"id": "[trimmed-depth]",
"location": "[trimmed-depth]",
"description": "[trimmed-depth]",
"wishlistItems": "[trimmed-depth]",
"nameLocale": "[trimmed-depth]"
},
"priceBreakdownMessages": []
}
},
{
"id": "[redacted-phone]",
"name": "[redacted-name]",
"rating": "4.95 (38)",
"rating_label": "4.95 out of 5 average rating, 38 reviews",
"price": "€385 total",
"coordinate": {
"lat": 41.0361,
"lng": 28.9738
},
"badges": [
"Guest favorite"
],
"subtitle": "The Blue flat with a View",
"raw": {
"__typename": "StaySearchResult",
"avgRatingA11yLabel": "4.95 out of 5 average rating, 38 reviews",
"avgRatingLocalized": "4.95 (38)",
"listingParamOverrides": null,
"structuredDisplayPrice": {
"__typename": "[trimmed-depth]",
"primaryLine": "[trimmed-depth]",
"secondaryLine": "[trimmed-depth]",
"explanationData": "[trimmed-depth]",
"explanationDataDisplayPosition": "[trimmed-depth]",
"displayPriceStyle": "[trimmed-depth]",
"loggingMetadata": "[trimmed-depth]"
},
"badges": [
"[trimmed-depth]"
],
"contextualPictures": [
"[trimmed-depth]",
"[trimmed-depth]",
"[trimmed-depth]"
],
"paymentMessages": [],
"title": "Apartment in Istanbul",
"subtitle": "The Blue flat with a View",
"nameLocalized": {
"__typename": "[trimmed-depth]",
"localizedStringWithTranslationPreference": "[trimmed-depth]"
},
"propertyId": null,
"structuredContent": {
"__typename": "[trimmed-depth]",
"distance": "[trimmed-depth]",
"mapCategoryInfo": "[trimmed-depth]",
"mapPrimaryLine": "[trimmed-depth]",
"mapSecondaryLine": "[trimmed-depth]",
"primaryLine": "[trimmed-depth]",
"secondaryLine": "[trimmed-depth]",
"tertiaryLine": "[trimmed-depth]",
"reviewSnippet": "[trimmed-depth]",
"explanation": "[trimmed-depth]"
},
"passportData": null,
"demandStayListing": {
"__typename": "[trimmed-depth]",
"id": "[trimmed-depth]",
"location": "[trimmed-depth]",
"description": "[trimmed-depth]",
"wishlistItems": "[trimmed-depth]",
"nameLocale": "[trimmed-depth]"
},
"priceBreakdownMessages": []
}
}
],
"raw_count": 44,
"unique_count": 26,
"pagination": {
"cursor_count": 15,
"cursors": [
{
"cursor": "eyJzZWN0aW9uX29mZnNldCI6MCwiaXRlbXNfb2Zmc2V0IjowLCJ2ZXJzaW9uIjoxfQ==",
"items_offset": 0
},
{
"cursor": "eyJzZWN0aW9uX29mZnNldCI6MCwiaXRlbXNfb2Zmc2V0IjoxOCwidmVyc2lvbiI6MX0=",
"items_offset": 18
},
{
"cursor": "eyJzZWN0aW9uX29mZnNldCI6MCwiaXRlbXNfb2Zmc2V0IjozNiwidmVyc2lvbiI6MX0=",
"items_offset": 36
}
],
"deduped": true
}
}
}What the Airbnb API does
| Action | Description | Concrete use case | Key params |
|---|---|---|---|
| stays.search | Search Airbnb stays by location and dates with full filters: price range, guests, place/property type, bedrooms/beds/baths, amenities, instant-book, Superhost, free cancellation and host language (≈26 cards/page, cursor-paginated). | Travel apps call stays.search to search Airbnb stays by location and dates with full filters. | query, checkin, checkout, adults, children, ... |
| stays.detail | Full listing details: title, property/room type, guest capacity, the six category sub-ratings, host card, complete amenity list, photo URLs, sleeping arrangement, house rules, description and map location. Returns a clean `parsed` summary plus the raw PDP payload. | Pricing monitors call stays.detail to get full listing details. | listing_id, currency, locale |
| host.profile | Host profile for a listing: host name, Superhost/verified status, overall host rating, total reviews, years hosting, response rate and host highlights. | Itinerary products call host.profile to get host profile for a listing. | listing_id |
| stays.reviews | Listing reviews, paginated via offset/limit. | Hospitality analysts call stays.reviews to get listing reviews, paginated via offset/limit.. | listing_id, offset, limit, locale |
| stays.availability_calendar | Availability calendar for a listing (up to 12 months ahead): per-day availability + min/max nights. | Travel apps call stays.availability_calendar to get availability calendar for a listing (up to 12 months ahead). | listing_id |
| properties.search | Alias of stays.search. | Pricing monitors call properties.search to get alias of stays.search.. | query, checkin, checkout, adults, cursor, ... |
| properties.detail | Alias of stays.detail. | Itinerary products call properties.detail to get alias of stays.detail.. | listing_id, currency, locale |
| autocomplete | Location autocomplete suggestions (raw passthrough). | Hospitality analysts call autocomplete to get location autocomplete suggestions (raw passthrough).. | none |
Call stays.search from your stack
curl -X POST https://api.reefapi.com/airbnb/v1/stays.search \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"query":"Istanbul, Turkey","checkin":"2026-08-10","checkout":"2026-08-15","adults":2}'import requests
r = requests.post(
"https://api.reefapi.com/airbnb/v1/stays.search",
headers={"x-api-key": REEF_KEY},
json={
"query": "Istanbul, Turkey",
"checkin": "2026-08-10",
"checkout": "2026-08-15",
"adults": 2
},
)
print(r.json()["data"])const res = await fetch("https://api.reefapi.com/airbnb/v1/stays.search", {
method: "POST",
headers: {
"x-api-key": process.env.REEF_KEY,
"content-type": "application/json",
},
body: JSON.stringify({
"query": "Istanbul, Turkey",
"checkin": "2026-08-10",
"checkout": "2026-08-15",
"adults": 2
}),
});
const { ok, data, meta, error } = await res.json();Ask your MCP-connected assistant: call reefapi.airbnb.stays.search with {"query":"Istanbul, Turkey","checkin":"2026-08-10","checkout":"2026-08-15","adults":2}.Who uses this API and why
- Pricing and revenue tools call stays.search to track nightly rates and availability in a market.
- Short-term-rental analytics use stays.reviews and host.profile to study top hosts and guest sentiment.
- Travel apps use stays.detail and the availability_calendar to show live listings and open dates.
Questions developers ask before integrating
Is the Airbnb search price the nightly rate or the total?
The total for the stay, and the string says so: "$1,194 total, originally $1,293". The underlying breakdown on that card read "5 nights x $249.12" = $1,245.61, a special offer of -$98.75, taxes of $46.70 and a Total of $1,193.56, with display style REGULATED_TOTAL. So the number in front of the word "total" already includes taxes and the discount. Divide by your night count if you want a nightly figure.
Why are price and rating strings instead of numbers?
Because that is what the search card publishes, and converting them would throw away the qualifier. price arrives as "$1,194 total, originally $1,293", or just "$898 total" when there is no discount, and rating as "5.0 (279)". Parse the currency symbol, the amount and the qualifier separately. In stays.detail, ratings.overall is a proper number (4.84) but ratings.review_count is still a string ("408").
Why do Airbnb listing ids have two completely different lengths?
Older listings kept short numeric ids and newer ones use long snowflake-style ids. In one Lisbon search both appeared side by side: 2684458 and 8096741 at seven digits, 29330369 at eight, and 1476784482714562767 at nineteen. Every action takes listing_id as a string and both forms work, so store it as text, never as a 32-bit or 64-bit integer column.
Which stays.detail fields come back null or empty?
Measured on a live listing: description, share_url, picture_count, location.city, location.address and location.where_label were all null, and rooms and house_rules were empty arrays. What is populated is title, property_type, room_type, person_capacity, is_superhost, the seven-part ratings block, coordinates, 37 amenities grouped by room, and 35 photos with captions and aspect ratios.
Can I get host details for a listing?
Not at the moment. On 2026-08-27 host.profile returned ok:true for three different listings and every one came back with name null, is_superhost false, is_verified false and empty highlights and details arrays, carrying only listing_id and listing_title. stays.detail has the same empty host block, and its host.is_superhost read false on a listing whose top-level is_superhost was true. Take superhost status from parsed.is_superhost or from the search card's badges array, and do not spend a call on host.profile until this is fixed.
What does the availability calendar actually give me?
Day rows, not prices. month_count 2 returned 61 days starting from the current month, and each day carries calendarDate, available, availableForCheckin, availableForCheckout, bookable, minNights and maxNights. On the measured listing 19 of 61 days were available, minNights varied between 1, 2 and 3, and bookable came back null for the current month and true or false for the next one. price.localPriceFormatted was null on all 61 days, so use the search card or the detail page for money.
Why is unique_count lower than raw_count?
Airbnb's search page renders the same listing in more than one section, so the raw page held 48 cards for 30 distinct listings. items[] is the de-duplicated set and both counters are returned so you can see how much was folded away. meta.pagination gives next_cursor plus a cursors array, 15 entries on the measured search; pass next_cursor back as cursor for the following page.
Is stays.price_checkout available?
Not at the moment. Measured on 2026-08-27 it returned ok:false with error.code DISABLED and retryable:false, and meta.scope ENDPOINT_DISABLED, in about two milliseconds. It fails fast rather than hanging, so retrying will not help. The search card's total for your exact checkin and checkout dates is the closest live substitute.
What is the Airbnb API?
Airbnb API is a ReefAPI endpoint group for search stays, get listing details, reviews and availability. It returns live JSON through POST requests under /airbnb/v1.
Is the Airbnb API free to try?
Yes. ReefAPI starts with 1,000 free credits, no card required. Airbnb calls use the same shared credit balance as every other ReefAPI engine.
Do I need an Airbnb login or account?
No login to Airbnb is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.
How fresh is the Airbnb data?
The page example is captured from a live stays.search call, and production requests fetch live data through ReefAPI rather than a static sample.
How many credits does the Airbnb API use?
Airbnb actions currently cost 1-3 credits per successful call. Failed or blocked calls are free, and all APIs draw from one credit pool.
Can I call Airbnb from an AI assistant or MCP client?
Yes. Connect ReefAPI once through MCP and your assistant can call airbnb actions with the same key, credit pool and JSON envelope used by normal REST requests.