For AI agents: a documentation index is available at /llms.txt
Skip to main content

Predefined Scrapers API

🚧 BETA 🚧

The Predefined Scrapers API is currently in beta. Parameters and response shapes may change in future releases. 🚧

The Predefined Scrapers API is only available for Cloud plans. Contact us for more information here.

Predefined scrapers return validated JSON from supported sites without browser code, at a fixed price per call. Discover the available scrapers, search Zillow listings, or look up an individual property.

When to use: Use this API when a scraper exists for your site. Use Smart Scrape for other single pages and BrowserQL for custom flows.

Endpoint

  • List: GET /scrapers
  • Run: POST /scrapers/{scraper}/{api}
  • Auth: token query parameter (?token=YOUR_API_TOKEN_HERE) or Authorization: Bearer YOUR_API_TOKEN_HERE
  • Content-Type: application/json for POST requests
  • Response: application/json
Prerequisites

Quickstart​

Search for homes for sale in Austin, TX. Run JavaScript server-side with Node.js (not in a browser, where your token would be exposed). The Python examples use requests; install it with python -m pip install requests.

curl --request POST \
--url 'https://production-sfo.browserless.io/scrapers/zillow/search?token=YOUR_API_TOKEN_HERE' \
--header 'Content-Type: application/json' \
--data '{"params":{"query":"Austin, TX","tenure":"sale"}}'

Response (abridged example; listings and counts change):

{
"ok": true,
"data": {
"query": { "text": "Austin, TX", "regionId": 10221, "regionType": 6, "subType": "CITY", "label": "Austin, TX", "tenure": "sale", "currency": "USD" },
"page": 1,
"pageCount": 20,
"pageSize": 41,
"totalUnits": 5575,
"exhausted": false,
"via": "api",
"results": [
{
"kind": "home",
"zpid": "29365471",
"url": "https://www.zillow.com/homedetails/15205-Back-Of-The-Moon-St-Austin-TX-78734/29365471_zpid/",
"address": { "line": "15205 Back Of The Moon St, Austin, TX 78734", "street": "15205 Back Of The Moon St", "city": "Austin", "state": "TX", "zip": "78734", "latitude": 30.410772, "longitude": -97.917114 },
"price": { "amount": 475000, "currency": "USD", "period": "total", "text": "$475,000" },
"beds": 3,
"baths": 2,
"livingAreaSqft": 1800,
"lotArea": { "value": 9400.248, "units": "sqft" },
"homeType": "SINGLE_FAMILY",
"status": "FOR_SALE",
"statusText": "Active",
"daysOnZillow": 4,
"imgSrc": "https://photos.zillowstatic.com/fp/f2fb36cf6cea9d2eeac4b4b3f6509f16-p_e.jpg",
"brokerName": "LPT Realty LLC - Ascend Group",
"building": null
}
// …40 more results omitted.
]
},
"error": null
}

totalUnits is Zillow's count of matching listings, not Browserless billing units. This call costs 10 Browserless units, regardless of how many listings it returns.

Check both the HTTP status and ok: a scraper can return HTTP 200 with ok: false. See Error responses.

List the available scrapers​

GET /scrapers is free and isn't metered. Use it to discover scraper names, supported hostnames, API names, prices, and output schemas. Examples use San Francisco; see the regional endpoints for other regions.

curl --request GET \
--url 'https://production-sfo.browserless.io/scrapers?token=YOUR_API_TOKEN_HERE'

Response (catalog v1.2.0 excerpt; schemas and deployment-specific metadata omitted):

{
"version": "1.2.0",
"contractVersion": 1,
// previousVersion and swap metadata omitted.
"scrapers": {
"zillow": {
"hostnames": ["zillow.com", "www.zillow.com"],
"apis": {
"search": { "ttlSeconds": 1800, "units": 10, "outputSchema": { /* … */ } },
"property": { "ttlSeconds": 21600, "units": 10, "outputSchema": { /* … */ } }
}
},
"example": {
"hostnames": ["example.com", "www.example.com"],
"apis": {
"page-info": { "ttlSeconds": 86400, "units": 1, "outputSchema": { /* … */ } }
}
}
}
}

units is the current price per call, and outputSchema is the JSON Schema every successful result is validated against. ttlSeconds is how long a result is considered fresh. The catalog also includes previousVersion (a string or null) and swap metadata: inProgress, targetVersion, startedAt, lastOutcome, and lastAt.

Send either params.query or a Zillow place url to POST /scrapers/zillow/search. A listing URL belongs to Zillow property, not search.

Request body​

ParameterTypeDefaultDescription
urlstring—Alternative to params.query: a Zillow place URL such as /homes/<slug>_rb/, /<city>-<st>/, a rentals URL, or a Zillow URL containing ?searchQueryState=. Only zillow.com and www.zillow.com are supported.
params.querystring—Place text, for example Austin, TX. Required when you don't supply a place URL.
params.tenurestringsalesale or rent. A value that contradicts the URL, such as sale with a rentals URL, returns invalid-input.
params.pageinteger1Page from 1 through 24.
params.sortstringSite defaultSale: pricea, priced, days, beds, baths, size, lot, priorityscore, zest, zesta. Rent: paymenta, paymentd, days, beds, baths, size, lot, priorityscore.
params.priceMin, params.priceMaxnumberUnsetMinimum and maximum price. For rent, these bound monthly rent.
params.bedsMin, params.bedsMaxnumberUnsetMinimum and maximum bedrooms.
params.bathsMin, params.bathsMaxnumberUnsetMinimum and maximum bathrooms.
params.sqftMin, params.sqftMaxnumberUnsetMinimum and maximum living area in square feet.
params.yearBuiltMin, params.yearBuiltMaxnumberUnsetEarliest and latest construction year.
params.homeTypesstringUnsetComma-separated house, condo, townhouse, apartment, multifamily, manufactured, land. Listing one switches the others off.
params.daysOnZillowstringUnsetOne of 1, 7, 14, 30, 90, 6m, 12m, 24m, 36m.
params.keywordsstringUnset1–200 printable characters.

For all scraper requests, url is limited to 8,192 characters. params accepts at most 64 keys, with keys up to 128 characters and values up to 4,096 characters. Values must be strings, numbers, or booleans, not nested objects or arrays.

Response fields​

Successful responses have ok: true, the following fields under data, and error: null.

FieldDescription
queryResolved place: text, regionId, regionType, subType, label, tenure, and currency (USD or CAD).
page, pageCount, pageSizeCurrent page, Zillow's displayed page count (capped at 20), and page size (41). Pages 21–24 can still return results.
totalUnitsNumber of matching listings, not a charge.
exhaustedtrue when the requested page is past the real end and has no results.
viaapi or browser, indicating which extraction path succeeded.
results[]Matching homes or buildings.
results[].kind, zpid, urlkind is home or building; zpid is a string identifier; url links to Zillow.
results[].addressline, street, city, state, zip, latitude, and longitude.
results[].priceamount, currency, period, and display text.
results[].beds, baths, livingAreaSqft, lotAreaProperty dimensions. lotArea contains value and units.
results[].homeType, status, statusText, daysOnZillowProperty type, listing status, and listing age.
results[].imgSrc, brokerName, buildingPhoto URL, brokerage, and building information. building is null for a home result.

Filter rentals​

Find rentals with at least two bedrooms and monthly rent no higher than $2,500, sorted by ascending payment:

curl --request POST \
--url 'https://production-sfo.browserless.io/scrapers/zillow/search?token=YOUR_API_TOKEN_HERE' \
--header 'Content-Type: application/json' \
--data '{"params":{"query":"Austin, TX","tenure":"rent","bedsMin":2,"priceMax":2500,"sort":"paymenta"}}'

Response uses the search envelope above, with data.query.tenure set to rent. Prices describe rent rather than a sale price; counts and listings depend on current availability.

Zillow property​

Call POST /scrapers/zillow/property with a property ID or a Zillow listing URL. Building URLs aren't accepted.

ParameterTypeDefaultDescription
params.zpidstring or integer—Positive property ID as a digit-only string or integer, at most 2,147,483,647. Use a zpid from a search result.
urlstring—Alternative to params.zpid: https://www.zillow.com/homedetails/…/<zpid>_zpid/.
curl --request POST \
--url 'https://production-sfo.browserless.io/scrapers/zillow/property?token=YOUR_API_TOKEN_HERE' \
--header 'Content-Type: application/json' \
--data '{"params":{"zpid":29365471}}'

Response (excerpt; values change as the listing changes):

{
"ok": true,
"data": {
"identity": { "zpid": "29365471", "url": "https://www.zillow.com/homedetails/15205-Back-Of-The-Moon-St-Austin-TX-78734/29365471_zpid/", "homeStatus": "FOR_SALE", "homeType": "SINGLE_FAMILY", "tenure": "sale", "country": "USA", "currency": "USD", "buildingId": null, "providerListingId": null },
"address": { "street": "15205 Back Of The Moon St", "city": "Austin", "state": "TX", "zip": "78734", "neighborhood": null, "subdivision": "Travis Landing", "county": "Travis County", "latitude": 30.410772, "longitude": -97.917114, "parcelId": "156788" },
"price": { "list": 475000, "zestimate": null, "rentZestimate": null, "lastSold": null, "listLow": null, "baseRent": null, "pricePerSqft": 264, "hoaFeeText": "$166 annually", "taxAssessedValue": 362528 },
// facts, listing, history, schools, photos, media, and rental omitted.
"via": "api"
},
"error": null
}
GroupDescription
identityProperty and provider identifiers, URL, status, home type, country, currency, and tenure (sale, rent, or other). The returned zpid is a string.
addressStreet, city, state, ZIP, neighborhood, subdivision, county, coordinates, and parcel ID. Unlike search results, it has no line field.
priceListing price, estimates, last sale, rent, price per square foot, HOA text, and assessed value. Unavailable values can be null.
facts, listing, historyProperty characteristics, listing information, and historical records.
schools[], photos[]School information and property photos.
media, rentalAdditional media and rental details.
viaapi or browser.

Use the catalog's outputSchema for the full field definitions.

Pricing​

A call is charged the API's fixed units only when it returns HTTP 200 with ok: true or error.code: "not-found"; every other outcome is free, including timeouts and 429s. No browser-time, proxy, or CAPTCHA units are added, and the charge appears under Time units on your account dashboard.

APIUnits per charged call
zillow.search10
zillow.property10
example.page-info1

GET /scrapers always returns the current price for every API. See Unit consumption for your allowance and usage tracking.

Testing cheaply​

Use example.page-info to check authentication and response handling for 1 unit. It accepts only example.com or www.example.com URLs and returns title and heading; heading uses the first paragraph if the page has no heading.

curl --request POST --url 'https://production-sfo.browserless.io/scrapers/example/page-info?token=YOUR_API_TOKEN_HERE' --header 'Content-Type: application/json' --data '{"url":"https://example.com/"}'

Response contains ok: true, data.title: "Example Domain", data.heading with the page's heading or first paragraph, and error: null.

Timeouts and concurrency​

Set the timeout query parameter in milliseconds, for example &timeout=90000. Without it, your plan's maximum session time applies, capped at the server ceiling of 30 minutes. Values above the server ceiling are clamped, but a value above your plan's maximum session time returns HTTP 400 rather than extending the plan limit. Zero, negative, and non-numeric values also return 400.

Allow at least 90,000 ms for Zillow, and keep your HTTP client's timeout longer than the API timeout. The API path usually finishes in a few seconds, but a blocked request can fall back to a stealth browser and take about 90 seconds.

Scraper calls count toward your plan's concurrency limit like any other request, for as long as the call is in flight. Above the limit you get 429 Too Many Requests; it costs nothing, so retry with backoff.

Error responses​

Scraper errors return HTTP 200 with ok: false; host and platform failures use HTTP error statuses. Check the HTTP status first, then inspect error.retryable only if the body contains a scraper error envelope. Use backoff for transient failures.

StatusCodeChargedWhat it means / what to do
200not-foundYesThe lookup ran but no property or region matched. Check the query or property ID; retryable: false.
200invalid-inputNoThe scraper rejected an input. Correct the field named in message; values aren't echoed. Retryable: false.
200blockedNoBot protection defeated the API path and browser fallback. details.stage is api, rotated, or browser. Retryable: true.
200upstream-changedNoThe site's page or API changed. A scraper update is needed; retryable: false.
200upstream-errorNoThe site or network failed. Retryable: true.
400unknown-scraper / unknown-apiNoCheck the path against GET /scrapers. Retryable: false.
400input-not-cloneableNoThe body couldn't be handed to the scraper. Correct its structure; retryable: false.
408job-timeoutNoThe job exceeded timeout. Increase it within your plan limit; retryable: true.
429Too Many Requests (platform response)NoYour plan's concurrency limit is reached, as for every endpoint. Wait for an in-flight request to finish, then retry. This response doesn't use the scraper envelope.
429overloadedNoRare: the region is at scraper capacity. Retry with backoff; retryable: true.
503bundle-unavailable / worker-crashed / job-drainedNoRare: the scraper runtime is restarting or being updated. Retry with backoff; retryable: true.
500invalid-resultNoOutput failed schema validation. Report the error; retryable: false.
401Unauthorized (platform response)NoSupply a valid API token. This response doesn't use the scraper envelope.

For example, a missing property returns HTTP 200 with this envelope (message is abbreviated):

{
"ok": false,
"data": null,
"error": {
"code": "not-found",
"message": "…",
"retryable": false,
"status": null,
"details": null
}
}

error.status and error.details carry additional context when available; they aren't the HTTP response status. Invalid timeout parameters can also produce a plain HTTP 400 platform response, so don't assume every error body is JSON.

FAQ & Troubleshooting​

Why am I getting 401 Unauthorized?

Pass a valid API token in ?token= or Authorization: Bearer. Check your token in the account dashboard; don't put it in the JSON body.

I got not-found and was still charged. Why?

The scraper completed the lookup and found no match. That definitive answer costs the same fixed units as a successful lookup. Other error outcomes are free.

I sent a Zillow listing URL to search and got invalid-input

Send /homedetails/…_zpid/ URLs to zillow/property. zillow/search takes a place query or place URL, not an individual listing.

Why did a request take 40+ seconds?

Allow at least 90 seconds for Zillow. When the site's API path is blocked, the scraper can fall back to a stealth browser. A successful result's via field tells you which path returned it; the price doesn't increase for the browser fallback.

What should I do about 429 Too Many Requests?

Your account is at its plan concurrency limit; scraper calls count toward it while they run. The response costs nothing; retry with backoff.

Next steps​

See the OpenAPI reference for complete parameter details.

Was this page helpful?