Install

Search Airbnb stays for a place and dates.

Searches Airbnb homes for a destination, dates and number of guests like the site's own search: listing name and type, total price for the stay (and the original price when discounted), rating and reviews, bedrooms and beds, badges such as Guest favorite or Superhost, coordinates and the listing link.

No reports yetRead-only, no login
Site
Airbnb, United States
Version
v2 of 2
Runs, last 7 days
No reports yet
sha256
93b7715df0f6…

Requests

sent from your browser, in order
  1. 1GET/s/{{loc.slug | urlencode}}/homes?adults&checkin&checkout&currency&locale&price_maxServer-rendered search page; the result data sits in script#data-deferred-state-0 (pure JSON). The one-item foreach turns 'Paris, France' into Airbnb's 'Paris--France' path slug.repeats, max 1

Parameters

from your agent
adultsinteger = 2
checkinstringrequired
checkoutstringrequired
currencyenum "EUR" | "USD" | "GBP" | "CHF" = "EUR"
locationstringrequiredCity or area, e.g. 'Lisbon', 'Paris, France', 'Brooklyn, NY'
price_maxintegerMaximum price per night in the chosen currency

Returns

to your agent
checkinstring
checkoutstring
currencystring
locationstring
staysobject[]badges, details, id, latitude, longitude, name, original_price, price, price_basis, rating, reviews, type, url

Where it can send

  • www.airbnb.com

Anything else is blocked by the extension.

Reads from your session

Nothing. Your browser attaches the site's cookies by itself.

Used only in requests to the addresses on the left. Never sent to wield.run or your agent.

Checks

a run passes when
  • length(output.stays) > `5`
  • output.stays[0].price > `0`
  • starts_with(output.stays[0].url, 'https://www.airbnb.com/rooms/')
  • length(output.stays[?rating != `null`]) > `0`
  • length(output.stays[?price == `null`]) <= `2`

Raw recipe

Served as canonical JSON from /api/v1/recipes/airbnb.com/search-stays, so its sha256 matches the ETag and the index.

Check it yourself
curl -s https://wield.run/api/v1/recipes/airbnb.com/search-stays | shasum -a 256
# 93b7715df0f6c651078493638f386c386357014a6763717c9900747c4e9a5ae9
airbnb.com/search-stays.json, v2
{
  "description": "Searches Airbnb homes for a destination, dates and number of guests like the site's own search: listing name and type, total price for the stay (and the original price when discounted), rating and reviews, bedrooms and beds, badges such as Guest favorite or Superhost, coordinates and the listing link.",
  "effect": "read",
  "id": "airbnb.com/search-stays",
  "keywords": [
    "airbnb",
    "vacation rental",
    "apartment",
    "holiday home",
    "stay",
    "travel",
    "lodging",
    "ferienwohnung",
    "unterkunft",
    "urlaub"
  ],
  "output": {
    "schema": {
      "properties": {
        "checkin": {
          "type": "string"
        },
        "checkout": {
          "type": "string"
        },
        "currency": {
          "type": "string"
        },
        "location": {
          "type": "string"
        },
        "stays": {
          "items": {
            "properties": {
              "badges": {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "details": {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "id": {
                "type": "string"
              },
              "latitude": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "longitude": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "original_price": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "price": {
                "description": "Price as shown, see price_basis (usually the total for the stay incl. taxes)",
                "type": [
                  "number",
                  "null"
                ]
              },
              "price_basis": {
                "description": "'total' or 'night'",
                "type": [
                  "string",
                  "null"
                ]
              },
              "rating": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "reviews": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "type": {
                "description": "e.g. 'Apartment in Santa Maria Maior'",
                "type": [
                  "string",
                  "null"
                ]
              },
              "url": {
                "format": "uri",
                "type": "string"
              }
            },
            "type": "object"
          },
          "type": "array"
        }
      },
      "required": [
        "location",
        "stays"
      ],
      "type": "object"
    },
    "value": "{location: params.location, checkin: params.checkin, checkout: params.checkout, currency: params.currency, stays: steps.search[0].stays}"
  },
  "params": {
    "additionalProperties": false,
    "properties": {
      "adults": {
        "default": 2,
        "maximum": 16,
        "minimum": 1,
        "type": "integer"
      },
      "checkin": {
        "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
        "type": "string"
      },
      "checkout": {
        "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
        "type": "string"
      },
      "currency": {
        "default": "EUR",
        "enum": [
          "EUR",
          "USD",
          "GBP",
          "CHF"
        ],
        "type": "string"
      },
      "location": {
        "description": "City or area, e.g. 'Lisbon', 'Paris, France', 'Brooklyn, NY'",
        "maxLength": 80,
        "minLength": 2,
        "type": "string"
      },
      "price_max": {
        "description": "Maximum price per night in the chosen currency",
        "minimum": 1,
        "type": "integer"
      }
    },
    "required": [
      "location",
      "checkin",
      "checkout"
    ],
    "type": "object"
  },
  "provenance": {
    "method": "browser-inspect",
    "notes": "spikes/airbnb.md. script#data-deferred-state-0 holds niobeClientData[0] = StaysSearch. Listing ids in it are base64 GraphQL ids ('DemandStayListing:<id>'), so the numeric id comes from mapResults.staysInViewport[].listingId, which pairs 1:1 by position with mapResults.mapSearchResults (verified on all 20). Price primaryLine: discountedPrice/originalPrice when discounted, else price; qualifier 'total'. v2 (2026-11-07): Airbnb serves three price-line shapes (A/B): QualifiedDisplayPriceLine (price), DiscountedDisplayPriceLine (discountedPrice/originalPrice) and OrderedDisplayPriceLine (orderedComponents[] with discountedPrice/originalPrice/qualifier parts, no top-level price). v1 read only the first two, so a session that gets the ordered variant (17 of 20 in Victor's browser, Paris) had price null. locale=en pins number formatting. v1 pinned in search-stays.v1.json.",
    "recorded_at": "2026-11-07T12:56:00+01:00",
    "recorded_by": "C4"
  },
  "requires_login": false,
  "schema": "https://wield.run/schema/recipe/v1.json",
  "site": {
    "country": "US",
    "id": "airbnb.com",
    "language": "en",
    "login_url": "https://www.airbnb.com/login",
    "name": "Airbnb",
    "origin": "https://www.airbnb.com",
    "origins": [
      "https://www.airbnb.com"
    ]
  },
  "steps": [
    {
      "description": "Server-rendered search page; the result data sits in script#data-deferred-state-0 (pure JSON). The one-item foreach turns 'Paris, France' into Airbnb's 'Paris--France' path slug.",
      "expect": {
        "content_type": "html",
        "status": [
          200
        ]
      },
      "extract": {
        "embedded_json": {
          "selector": "script#data-deferred-state-0",
          "vars": {
            "stays": "let $ss = niobeClientData[0][1].data.presentation.staysSearch in map(&(let $p = @ in {id: $p[1].listingId, name: $p[0].nameLocalized.localizedStringWithTranslationPreference || $p[0].subtitle || `null`, type: $p[0].title || `null`, rating: ($p[0].avgRatingLocalized && to_number(split($p[0].avgRatingLocalized, ' ')[0])) || `null`, reviews: (contains($p[0].avgRatingLocalized || '', '(') && to_number(replace(replace(split($p[0].avgRatingLocalized, ' (')[1], ')', ''), ',', ''))) || `null`, price: ($p[0].structuredDisplayPrice.primaryLine.discountedPrice || $p[0].structuredDisplayPrice.primaryLine.price || ($p[0].structuredDisplayPrice.primaryLine.orderedComponents[?discountedPrice].discountedPrice | [0]) || ($p[0].structuredDisplayPrice.primaryLine.orderedComponents[?price].price | [0])) && to_number(trim(replace(replace(replace(replace(replace(replace(replace(replace(($p[0].structuredDisplayPrice.primaryLine.discountedPrice || $p[0].structuredDisplayPrice.primaryLine.price || ($p[0].structuredDisplayPrice.primaryLine.orderedComponents[?discountedPrice].discountedPrice | [0]) || ($p[0].structuredDisplayPrice.primaryLine.orderedComponents[?price].price | [0])), ' ', ''), ' ', ''), 'EUR', ''), 'US$', ''), '$', ''), '€', ''), '£', ''), ',', ''))) || `null`, original_price: ($p[0].structuredDisplayPrice.primaryLine.originalPrice || ($p[0].structuredDisplayPrice.primaryLine.orderedComponents[?originalPrice].originalPrice | [0])) && to_number(trim(replace(replace(replace(replace(replace(replace(replace(replace(($p[0].structuredDisplayPrice.primaryLine.originalPrice || ($p[0].structuredDisplayPrice.primaryLine.orderedComponents[?originalPrice].originalPrice | [0])), ' ', ''), ' ', ''), 'EUR', ''), 'US$', ''), '$', ''), '€', ''), '£', ''), ',', ''))) || `null`, price_basis: $p[0].structuredDisplayPrice.primaryLine.qualifier || ($p[0].structuredDisplayPrice.primaryLine.orderedComponents[?qualifier].qualifier | [0]) || `null`, details: $p[0].structuredContent.primaryLine[].body || `[]`, badges: $p[0].badges[].text || `[]`, latitude: $p[0].demandStayListing.location.coordinate.latitude || `null`, longitude: $p[0].demandStayListing.location.coordinate.longitude || `null`, url: join('', ['https://www.airbnb.com/rooms/', $p[1].listingId])}), zip($ss.mapResults.mapSearchResults || `[]`, $ss.mapResults.staysInViewport || `[]`))"
          }
        }
      },
      "foreach": {
        "as": "loc",
        "in": "[{slug: replace(replace(trim(params.location), ', ', '--'), ' ', '-')}]",
        "max": 1
      },
      "id": "search",
      "parse": "html",
      "request": {
        "headers": {
          "Accept": "text/html"
        },
        "method": "GET",
        "query": {
          "adults": "{{params.adults}}",
          "checkin": "{{params.checkin}}",
          "checkout": "{{params.checkout}}",
          "currency": "{{params.currency}}",
          "locale": "en",
          "price_max": "{{params.price_max}}"
        },
        "url": "/s/{{loc.slug | urlencode}}/homes"
      }
    }
  ],
  "task": "Search Airbnb stays for a place and dates",
  "tos": {
    "notes": "Airbnb's terms prohibit scraping. One user-initiated search at a time from the user's own browser (1 request). Booking stays on airbnb.com.",
    "reviewed": false
  },
  "verify": {
    "checks": [
      "length(output.stays) > `5`",
      "output.stays[0].price > `0`",
      "starts_with(output.stays[0].url, 'https://www.airbnb.com/rooms/')",
      "length(output.stays[?rating != `null`]) > `0`",
      "length(output.stays[?price == `null`]) <= `2`"
    ],
    "example_params": {
      "adults": 2,
      "checkin": "2026-12-04",
      "checkout": "2026-12-07",
      "location": "Lisbon"
    },
    "max_ms": 6000
  },
  "version": 2
}