# Airbnb Listing Lookup: airbnb scraper by location from $2/1k (`accountable_eel/airbnb-listing-lookup`) Actor

Airbnb listings scraper: search a location or paste an Airbnb search URL and get one row per listing, including price, rating, room type, coordinates, superhost status, and a direct link. No login. Pay per listing returned; empty searches are free.

- **URL**: https://apify.com/accountable\_eel/airbnb-listing-lookup.md
- **Developed by:** [Adrian Voss](https://apify.com/accountable_eel) (community)
- **Categories:** Travel, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.52 / 1,000 listing returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Airbnb Listing Lookup

You give this actor a location ("Austin, TX") or a pasted Airbnb search URL. It runs the same
search a browser would, reads the listings straight out of Airbnb's own search-results page, and
gives you back one row per listing: price, rating, room type, coordinates, a Superhost/Guest
Favorite badge, and a direct link. No login, no scraping a listing's own detail page — just the
search-results card, the same thing you'd see scrolling the map view yourself.

### Who it's for

If you're building a list of short-term-rental comps for a pricing model, sourcing candidate
properties for a real-estate or arbitrage screen, or feeding an AI agent a "what's available and
at what price in this city" question, this turns an Airbnb search into a table instead of a page
you'd have to scroll and copy by hand. It answers "which listings, at what price, where, and how
well-rated" for a location — not "tell me everything about this one listing", which is a heavier,
separate lookup this actor deliberately doesn't make (see "Why this one").

### Why this one

- **Reads the search page's own embedded data, not a scraped-and-guessed DOM.** Airbnb's search
  results ship as a JSON blob inside the page (`__typename: "StaySearchResult"`), the same data
  the page's own React code renders from — not text pulled off rendered HTML elements that change
  with every front-end redesign.
- **Priced per listing, not per search.** A search for a big city can return dozens of listings;
  you pay $0.002 per listing actually returned, not a flat per-search fee, and a search that finds
  nothing costs nothing.
- **Paginates automatically, up to 50 listings per search.** One Airbnb search page ships 18
  listings; this actor follows Airbnb's own next-page cursors (on the same residential proxy
  session, never a bare unproxied request) to gather up to 50 without you writing a page loop.
- **Honest about what a search card can't tell you.** Host ID and a full amenities list live only
  on a listing's own detail page — a much heavier per-listing hop this actor does not make. Rather
  than fake those fields, they're simply not in the output. See the FAQ for what the search card
  *does* carry instead (bedroom/bath counts, Superhost and Guest Favorite badges, rating).
- **Never charged for a miss.** A search with no results, a malformed location, or a proxy block
  all come back as a free row explaining why.

### What you get

A parent row per search (columns below), exploded into one row per listing by default (turn off
"One row per listing" in the Input tab to get one grouped row per search instead — you're charged
the same either way).

| Field | Type | Description |
|---|---|---|
| `query` | text | The search you submitted, echoed back |
| `found` | boolean | `true` if at least one listing was returned |
| `status` | text | `OK` on a match; `NOT_FOUND`, `BLOCKED`, `BAD_FORMAT`, or `REQUEST_FAILED` on a miss |
| `message` | text | Plain-English reason for a miss — only present when `found` is `false` |
| `scrapedAt` | ISO 8601 datetime | When the search was run |
| `searchInput` | text | The location or URL you searched, echoed back |
| `checkin` / `checkout` | text | The dates actually used for this search (from your input or the run's defaults) |
| `guests` | number | The guest count actually used |
| `listingCount` | number | How many listings this search returned (what you're billed for) |
| `truncated` | boolean | `true` if Airbnb had more listings than this run returned |
| `listingId` | text | Airbnb's own numeric listing ID |
| `url` | link | The listing's `airbnb.com/rooms/...` page |
| `title` | text | The listing's headline |
| `roomType` | text | e.g. "Condo", "Private room", "Entire home" |
| `city` | text | Parsed from the listing's category line |
| `lat` / `lng` | number | Coordinates |
| `pricePerNight` | number | Parsed from the price breakdown when a check-in/check-out range is in effect (see FAQ) |
| `priceCurrency` | text | The currency symbol/code as Airbnb displayed it — not a verified ISO-4217 code |
| `priceDisplay` | text | The price exactly as Airbnb showed it, for when the parsed number needs a sanity check |
| `rating` | number | Out of 5 |
| `reviewCount` | number | |
| `isSuperhost` | boolean | |
| `isGuestFavorite` | boolean | |
| `imageUrl` | image | Cover photo |

Deselect any field in the Input tab's column picker to drop it from every row — the first five
always stay.

### Price

- **Listing returned**: $2 per 1,000 listings

Plus a $0.00005 start fee per run. Each event above is billed independently, only when it actually returns data — misses (`found:false`) are never charged.

$2 per 1,000 listings returned (FREE tier — less on a paid Apify plan), plus a $0.00005 actor-start
fee. A search that returns nothing costs nothing. 1,000 listings through this actor: **~$2** if
every search finds results. The store's leading incumbent, `tri_angle/airbnb-scraper`, charges $4
per 1,000 — this actor is half that, for the search-card fields (see "What you get") rather than a
full listing-detail crawl.

### How to use

1. **In the Apify Console.** Open the actor page and click **Start** — the `searches` field is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found.
2. **Via the API.** Call it directly with a POST request — no Console needed once you have an API token:
   ```bash
   curl "https://api.apify.com/v2/acts/accountable_eel~airbnb-listing-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
     -X POST \
     -H "Content-Type: application/json" \
     -d '{"searches":["Austin, TX"]}'
   ```
3. **On a schedule.** Save this actor as an Apify **Task** with the input you want, then add a **Schedule** (hourly, daily, weekly) so it runs on its own — no server of your own required.

### Input

```json
{
  "searches": [
    "Austin, TX"
  ]
}
```

One search per line. Write a location like "Austin, TX" or "Lisbon, Portugal", or paste a full airbnb.com/s/.../homes search URL straight from your browser (dates and guest count in the URL are kept). Accepted formats: Austin, TX, Lisbon, Portugal, https://www.airbnb.com/s/Austin--TX/homes?checkin=2026-10-01\&checkout=2026-10-05\&adults=2.

Optional run-level settings (Input tab, "Search settings"): `maxListings` (default 18, capped at
50\), `checkin`/`checkout` (`YYYY-MM-DD`), and `guests` — all ignored for any line that's already a
full search URL carrying its own dates and guest count.

### Sample output

By default you get **one row per listing** (turn off "One row per listing" in the Input tab for
one grouped row per search instead — you're charged the same either way):

| query | found | status | searchInput | checkin | checkout | guests | listingCount | truncated | listings | listingId | url | title | roomType | city | lat | lng | pricePerNight | priceCurrency | priceDisplay | rating | reviewCount | isSuperhost | isGuestFavorite | imageUrl | scrapedAt |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| Austin, TX | false | NOT\_FOUND | <search submitted> | <check-in used> | <check-out used> | <guests used> | <listings returned> | <more listings were available> | \<all listings found (full list)> | <airbnb listing id> | <listing link> | <listing title> | <room type> | <city> | <latitude> | <longitude> | \<price per night (parsed)> | \<currency (as displayed)> | \<price (as airbnb displayed it)> | <rating> | <review count> | \<superhost?> | \<guest favorite?> | <cover photo> | 2026-09-08T17:38:07.912Z |

A miss comes back as a row with `"found": false` and is never charged.

### Use it from Clay, n8n, Make, or an AI agent

This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.

```bash
curl "https://api.apify.com/v2/acts/accountable_eel~airbnb-listing-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"searches":["Austin, TX"]}'
```

**n8n.** Add an HTTP Request node: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~airbnb-listing-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body Content Type `JSON`, JSON Body `{"searches":["Austin, TX"]}` (swap in an expression from an earlier node for a real value).

**Clay.** Add an "HTTP API" column: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~airbnb-listing-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body `{"searches":["{{search}}"]}`, mapping the row's search into the `searches` array.

**MCP.** In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "Airbnb Listings Scraper by Location | Apify" — the agent will find and run this actor.

### Tips

- Paste a full search URL (with your own `checkin`/`checkout`/`adults` params already set) when you
  want exact control over dates and guests — a URL's own query params always win over the run-level
  defaults.
- Leave check-in/check-out blank for a quick "what's generally available and at what price" sweep;
  Airbnb picks its own default dates and the `checkin`/`checkout` output columns tell you what it
  used.
- `truncated: true` means more listings existed than this run returned — raise "Most listings to
  return per search" (up to 50) and run it again if you need the rest.
- Keep "Max concurrency" conservative — this target runs on Residential proxy specifically because
  a bare datacenter request is not proven safe at volume (see "Why this one"), and a search-heavy
  burst is the fastest way to get a session blocked.
- If `pricePerNight` comes back `null` on a listing that clearly has a price, check `priceDisplay` —
  it always carries Airbnb's raw price string even when the parser couldn't confidently split out a
  nightly rate from a total-for-stay figure.

### vs. alternatives

| | What it costs | What you get | Trade-off |
|---|---|---|---|
| **This actor** (`airbnb-listing-lookup`) | $0.002 per listing returned (FREE tier, less on paid tiers), $0.00005 actor start, nothing for a search with no results | One row per listing — price, rating, room type, coordinates, Superhost/Guest Favorite badges, and a direct link, from a location or a pasted search URL | Search-card data only: no host ID or amenities list (Airbnb doesn't ship either on the search results page — see FAQ), and price is parsed from a display string rather than a guaranteed ISO currency code. |
| **tri\_angle/airbnb-scraper** | $4 per 1,000 results | The store's dominant incumbent — broader field coverage, including per-listing detail-page fields | If you need full listing-detail fields (complete amenities, host profile), that's a heavier per-listing hop this actor doesn't make. For the "which listings, at what price, where" question, this covers it at a quarter of the price. |
| Doing it yourself | Your time + reverse-engineering Airbnb's embedded GraphQL JSON (the query-variable key changes per search), cursor-based pagination, and residential-proxy economics | The same data | The JSON-path parsing, price-string extraction, pagination, and proxy handling are the maintenance burden this actor absorbs. |

Prices for third-party tools are their published list prices as of September 2026 and are not
tracked here — check the vendor before relying on the comparison.

### FAQ

**Why don't I get a host ID or an amenities list?**
Neither exists anywhere in the search-results page's own data — only on a listing's individual
page, a separate and much heavier per-listing fetch this actor doesn't make. Rather than fabricate
those fields from something else, they're simply not part of the output. What the search card does
carry: bedroom/bathroom counts folded into `title`/`roomType`, a rating, a review count, and
Superhost/Guest Favorite badges.

**Is `priceCurrency` a real ISO currency code?**
No — it's the currency symbol or code exactly as Airbnb displayed it (e.g. "Ft", "$", "€"), which
depends on the locale Airbnb resolved for the request. `priceDisplay` keeps the full original string
so you can always check it against `pricePerNight`.

**Why is `pricePerNight` sometimes null?**
Airbnb's search card only ships a pre-formatted price string, not a structured amount. This actor
recovers the nightly rate from the "N nights x \[amount]" line in the price breakdown when a
check-in/check-out range is active; if that breakdown isn't present, `pricePerNight` stays `null`
rather than guessing — `priceDisplay` always has the raw figure regardless.

**Am I charged for a search that finds nothing?**
No. `Actor.charge()` only fires per listing actually returned. A search with zero results, a
malformed location, or a proxy block all produce a free row (unless you turn on "Hide rows with no
result").

**Does this handle rate limits or blocking?**
The default proxy is Residential specifically because this target is anti-bot-sensitive at volume.
A `BLOCKED` status means Airbnb returned HTTP 403/429/503 to the request; lowering "Max
concurrency" is the first thing to try.

**Is this a live check or a stored database?**
Live. Every run is a fresh Airbnb search — there's no cached snapshot, and prices/availability
reflect what Airbnb showed at fetch time, not a stored copy.

**Can I schedule this to re-check the same search on a cadence?**
Yes — save it as an Apify Task with your input and add a Schedule. Each run is a fresh search, so a
daily or weekly schedule is how you'd track price or availability changes for a location over time.

**Can an AI agent call this directly?**
Yes. It's registered on the Apify MCP server — an agent in Claude, Cursor, or another MCP client
can find and run it by name ("Airbnb Listings Scraper by Location | Apify"), or call the REST
endpoint shown above from any script or workflow tool.

### Related actors

- [AutoScout24 Listing Lookup](https://apify.com/accountable_eel/autoscout24-listing-lookup) — the
  same one-request-per-listing pattern applied to Germany's largest car marketplace.
- [Kleinanzeigen Listing Lookup](https://apify.com/accountable_eel/kleinanzeigen-listing-lookup) —
  another Residential-proxy classifieds lookup in this catalogue.
- [Sitemap URL Discovery](https://apify.com/accountable_eel/sitemap-url-discovery) — if you already
  have a target site and need every URL on it rather than a search result.

# Actor input Schema

## `searches` (type: `array`):

One search per line. Write a location like "Austin, TX" or "Lisbon, Portugal", or paste a full airbnb.com/s/.../homes search URL straight from your browser (dates and guest count in the URL are kept). Accepted formats: Austin, TX, Lisbon, Portugal, https://www.airbnb.com/s/Austin--TX/homes?checkin=2026-10-01\&checkout=2026-10-05\&adults=2. You're only charged for the ones we actually find — a miss costs nothing.

## `testRun` (type: `boolean`):

Turn this on to test your input on a small sample before running the full list. Turn it off to process everything.

## `onlyFound` (type: `boolean`):

Only keep rows where something was actually found. Misses are always free, whether or not you show them here.

## `includeKeywords` (type: `array`):

Optional. Only keep results that mention at least one of these words (e.g. a job title, a city, a product name). Leave empty to keep everything.

## `excludeKeywords` (type: `array`):

Optional. Drop any result that mentions one of these words. Leave empty to skip nothing.

## `maxResults` (type: `integer`):

Optional. Stop the run once this many results have been found — useful for a quick, cheap sample. Leave blank for no limit.

## `maxListings` (type: `integer`):

Airbnb hands out 18 listings per page; this actor pages up to 3 pages (54 listings) per search. You pay per listing returned, so this is also your budget control. Capped at 50.

## `checkin` (type: `string`):

Format YYYY-MM-DD, e.g. 2026-10-01. Leave empty to let Airbnb pick its own default dates. Ignored for any line that's already a full search URL with its own dates.

## `checkout` (type: `string`):

Format YYYY-MM-DD. Leave empty along with check-in to let Airbnb pick its own default dates.

## `guests` (type: `integer`):

Number of adult guests. Leave blank to use Airbnb's own default.

## `columns` (type: `array`):

Choose which pieces of information to include in each result row. All are included by default.

## `expandRows` (type: `boolean`):

When on, each listing found gets its own row instead of being grouped under its search. You're still only charged once per search, no matter how many rows it produces.

## `maxConcurrency` (type: `integer`):

Parallel requests. Keep conservative — this target has no browser fallback, so getting blocked costs more than slow-and-steady.

## `proxyConfiguration` (type: `object`):

Apify Proxy config. Residential recommended for anti-bot-sensitive targets.

## Actor input object example

```json
{
  "searches": [
    "Austin, TX"
  ],
  "testRun": false,
  "onlyFound": false,
  "includeKeywords": [],
  "excludeKeywords": [],
  "maxListings": 18,
  "checkin": "",
  "checkout": "",
  "columns": [
    "searchInput",
    "checkin",
    "checkout",
    "guests",
    "listingCount",
    "truncated",
    "listings",
    "listingId",
    "url",
    "title",
    "roomType",
    "city",
    "lat",
    "lng",
    "pricePerNight",
    "priceCurrency",
    "priceDisplay",
    "rating",
    "reviewCount",
    "isSuperhost",
    "isGuestFavorite",
    "imageUrl"
  ],
  "expandRows": true,
  "maxConcurrency": 2,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searches": [
        "Austin, TX"
    ],
    "includeKeywords": [],
    "excludeKeywords": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("accountable_eel/airbnb-listing-lookup").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searches": ["Austin, TX"],
    "includeKeywords": [],
    "excludeKeywords": [],
}

# Run the Actor and wait for it to finish
run = client.actor("accountable_eel/airbnb-listing-lookup").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searches": [
    "Austin, TX"
  ],
  "includeKeywords": [],
  "excludeKeywords": []
}' |
apify call accountable_eel/airbnb-listing-lookup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,accountable_eel/airbnb-listing-lookup"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/7zBYR0Ml8fFWo6bki/builds/9eYx3wYQjpegZVADb/openapi.json
