Guides

How to scrape Kasada-protected sites

Kasada is one of the hardest bot protections to get through. It runs its challenge in the browser and checks the result before it serves any content, so a plain HTTP client gets a blank page that only loads the challenge script, never the content. StealthASF Ultra mode gets the real page. In our production tests on 8 October 2026, Hyatt and realestate.com.au, both protected by Kasada, returned HTTP 200 with their real page titles. This guide uses a realestate.com.au search page as the example.

1. Use Ultra mode

The API field engine selects the request mode. For Kasada sites, set it to ultra. Ultra is our strongest mode, made for the hardest protected sites, and it is the mode that passed both Kasada sites in our tests. It costs 50 base credits, renders the page and supports extraction. It does not run browser steps such as clicks or typing, so work from page URLs: search results, listing pages and detail pages each have one you can request directly.

2. Send a first request with curl

Verify your email, create an API key in the dashboard and put it in the STEALTHASF_API_KEY environment variable. Keep it out of source control. This first request extracts the page text, which tells you at a glance whether you have listings or a challenge.

curl --max-time 600 "https://stealthasf.com/v1/scrape" \
  -H "x-api-key: $STEALTHASF_API_KEY" \
  -H "content-type: application/json" \
  --data-raw '{"url":"https://www.realestate.com.au/buy/in-sydney,+nsw+2000/list-1","engine":"ultra","extract":"text"}'

The endpoint takes JSON and authenticates with the x-api-key header. Keep the 600-second client timeout; rendering a protected page takes longer than a plain fetch.

3. Confirm the content

Check the API's HTTP status and the target status in the JSON separately. A real page has target status 200, the site's normal title in html and listing details such as addresses and prices in the text. A Kasada block is close to empty: no title you recognise and no listings. When StealthASF detects a block, it returns HTTP 422 and does not charge the request.

Store engine, credits_charged and job_id with each result. They tell you which mode produced the page, what it cost, and which request to quote if you contact support.

4. Move the request into Python

The Python version uses only the standard library. An API error ends the script with the error body, so nothing from a failed request reaches your data. After a successful response, check for a field you need, such as a listing address, before saving the record.

import json
import os
from urllib.error import HTTPError
from urllib.request import Request, urlopen

payload = {
    "url": "https://www.realestate.com.au/buy/in-sydney,+nsw+2000/list-1",
    "engine": "ultra",
    "extract": "text"
}
request = Request(
    "https://stealthasf.com/v1/scrape",
    data=json.dumps(payload).encode("utf-8"),
    headers={
        "x-api-key": os.environ["STEALTHASF_API_KEY"],
        "content-type": "application/json",
    },
    method="POST",
)
try:
    with urlopen(request, timeout=600) as response:
        result = json.load(response)
except HTTPError as error:
    detail = error.read().decode("utf-8")
    raise SystemExit(f"API error {error.code}: {detail}")

print("Target status:", result["status"])
print("Credits:", result["credits_charged"])
print(result.get("data"))

For structured output, set extract to links to collect every link and its text, which gives you the detail-page URLs from a search page. Use meta for the title, description and Open Graph tags. The full rendered HTML is always in html for your own parser. When the page loads its data from a JSON endpoint, the response can include discovered_api so you can see where the data comes from.

5. Budget the credits

Ultra costs 50 credits per request and includes the first 1 MB of transfer. Each additional MB adds 10 credits, rounded up to a whole credit, where MB means 1,048,576 bytes. A 4 MB Ultra request costs 80 credits. A solved CAPTCHA adds 25 credits. Blocked requests cost nothing. At the base rate, Hobby covers 1,400 Ultra requests a month, Pro 5,000 and Scale 20,000. Check credits_charged for the exact figure on each page.

6. Scale up

Collect in two passes. First request the search or listing pages and keep the detail URLs. Then request each detail page once and extract its fields. This avoids fetching the same page twice and keeps the credit count predictable. Deduplicate URLs before the second pass, because the same listing often appears on several search pages.

Raise concurrency gradually within your plan limit. On HTTP 429, follow Retry-After and cap your retries. If a URL keeps returning 422, stop retrying it and send support the job ID. If the data depends on location, add a country field on Pro or Scale and keep it the same for every run. Track credits per complete record, not per response, so a layout change shows up as soon as fields start going missing. The API reference lists every field, and the pricing page shows plan allowances.