Skip to main content

scrapeunblocker-haystack

Haystack integration for ScrapeUnblocker - fetch pages that block ordinary HTTP requests.

ScrapeUnblocker renders web pages in a real browser behind anti-bot protections such as Cloudflare, DataDome, PerimeterX and Akamai, so your pipeline gets the real content instead of a block page, a captcha, or an empty JavaScript shell.

Installation

pip install scrapeunblocker-haystack

Setup

Get an API key at scrapeunblocker.com and export it:

export SCRAPEUNBLOCKER_API_KEY=<your-api-key>

Both components read that variable by default, or accept a Secret explicitly.

Components

ScrapeUnblockerFetcher

Fetches URLs and returns one Document per page.

from scrapeunblocker_haystack import ScrapeUnblockerFetcher

fetcher = ScrapeUnblockerFetcher()
result = fetcher.run(urls=["https://example.com"])

print(result["documents"][0].content[:200])
Parameter Default Description
api_key SCRAPEUNBLOCKER_API_KEY env var ScrapeUnblocker API key
parsed_data False Return AI-parsed structured JSON instead of raw HTML
proxy_country None Two-letter country code for the exit IP
time_sleep None Seconds to wait after load before capturing
steps None Browser actions to run before the HTML is captured (see below)
list_elements False Return the parsed elements JSON instead of HTML (see below)
markdown False Return the page as clean Markdown instead of HTML (see below)
main_content_only True With markdown, keep only the main content (drop navigation, header, footer, sidebars)
base_url https://api.scrapeunblocker.com API base URL
timeout 180 HTTP timeout in seconds
raise_on_failure False Raise instead of skipping a URL that fails

By default a URL that cannot be fetched is logged and skipped, so one bad URL does not discard the rest of the batch.

Markdown for LLMs and RAG

Set markdown=True to get each page as clean Markdown instead of HTML: headings, lists, tables (GitHub-flavored), absolute links, images with their alt text and fenced code blocks, without scripts, styles, navigation, footers, forms or hidden elements. The Markdown goes straight into a prompt or a splitter, with no HTML converter, and is usually a small fraction of the HTML's size. It is billed like an HTML fetch.

from scrapeunblocker_haystack import ScrapeUnblockerFetcher

fetcher = ScrapeUnblockerFetcher(markdown=True)
result = fetcher.run(
    urls=["https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html"]
)

doc = result["documents"][0]
print("# A Light in the Attic" in doc.content)  # True
print(doc.meta["content_type"])                 # text/markdown; charset=utf-8

By default only the page's main content is kept: navigation, the site header, footer and sidebars are dropped. Pass main_content_only=False to convert the whole page. markdown works with proxy_country, time_sleep and steps, but not with parsed_data or list_elements, which return their own JSON formats; combining them raises ValueError. A page that does not exist (404/410) is skipped or raises TargetNotFoundError, as below, with the site's own page as Markdown in body.

Browser steps

Pass steps to drive the page in a real browser before its HTML is captured - wait for content to appear, click, type, scroll, and so on. Each step is a dict with an action key plus its parameters:

from scrapeunblocker_haystack import ScrapeUnblockerFetcher

fetcher = ScrapeUnblockerFetcher(
    steps=[
        {"action": "wait_for", "selector": "#results"},
        {"action": "type", "selector": "input#q", "value": "laptops"},
        {"action": "press_key", "value": "Enter"},
        {"action": "wait_for_text", "value": "in stock"},
        {"action": "click", "selector": "button.load-more"},
        {"action": "scroll", "value": "bottom"},
    ]
)
result = fetcher.run(urls=["https://example.com/search"])

Available actions:

Action Parameters
wait_for selector, selector_type? (css|xPath|className|tagName), timeout_ms?
wait_for_text value, timeout_ms?
wait value (milliseconds)
click selector, selector_type?, timeout_ms?
type selector, selector_type?, value, clear?, timeout_ms? (human-like typing)
select selector, selector_type?, value, timeout_ms?
press_key value (Enter|Tab|Escape|Backspace|Delete|Space|Arrow*|Home|End|PageUp|PageDown)
scroll value ("bottom" or an integer pixel offset)

Steps are non-idempotent. If a step fails, the fetch raises StepExecutionError (with step_index, action, selector and reason), which is logged and skipped by default, or re-raised when raise_on_failure=True.

Pages with nothing to parse

With parsed_data=True, a page that loads but holds no structured data still yields a Document. Its JSON carries "data_extracted": false and the rendered page in html, and meta["data_extracted"] is False, so you can route it to an HTML converter. The call is billed like a plain HTML fetch.

Pages that do not exist

If the site answers HTTP 404 or 410, there is no page to index: the URL is skipped with a warning, or TargetNotFoundError (with url, origin_status and the site's own body) is raised when raise_on_failure=True. That is the site's answer, not a block - the call is billed and a retry returns the same result.

List elements

Set list_elements=True to return the parsed elements JSON ({url, count, elements: [...]}) instead of the page HTML. The JSON is stored as the Document content, and the element count is available in meta["count"]:

fetcher = ScrapeUnblockerFetcher(list_elements=True)
result = fetcher.run(urls=["https://example.com"])

doc = result["documents"][0]
print(doc.meta["count"])          # number of elements
print(doc.content)                # {"url": ..., "count": ..., "elements": [...]}

ScrapeUnblockerWebSearch

Searches Google and returns the organic results as Document objects, with the snippet as content and title / link / position in the metadata.

from scrapeunblocker_haystack import ScrapeUnblockerWebSearch

search = ScrapeUnblockerWebSearch(top_k=5)
result = search.run(query="best web scraping api")

for doc in result["documents"]:
    print(doc.meta["title"], doc.meta["link"])
Parameter Default Description
api_key SCRAPEUNBLOCKER_API_KEY env var ScrapeUnblocker API key
pages_to_check 1 How many result pages to scrape
proxy_country None Two-letter country code for localised results
top_k None Keep at most this many results

In a pipeline

Fetch pages as Markdown and answer a question from them:

from haystack import Pipeline
from haystack.components.builders import ChatPromptBuilder
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage

from scrapeunblocker_haystack import ScrapeUnblockerFetcher

prompt = [
    ChatMessage.from_user(
        "Answer the question using the pages below.\n\n"
        "{% for doc in documents %}{{ doc.content }}\n{% endfor %}\n"
        "Question: {{ question }}"
    )
]

pipe = Pipeline()
pipe.add_component("fetcher", ScrapeUnblockerFetcher(markdown=True))
pipe.add_component("prompt_builder", ChatPromptBuilder(template=prompt, required_variables="*"))
pipe.add_component("llm", OpenAIChatGenerator())

pipe.connect("fetcher.documents", "prompt_builder.documents")
pipe.connect("prompt_builder.prompt", "llm.messages")

result = pipe.run(
    {
        "fetcher": {"urls": ["https://example.com"]},
        "prompt_builder": {"question": "What is this page about?"},
    }
)
print(result["llm"]["replies"][0].text)

Serialization

Both components implement to_dict() / from_dict(), so pipelines using them can be saved and reloaded. The API key is serialized as a Haystack Secret reference, not as its value.

License

Apache-2.0

Metadata

Release files for scrapeunblocker-haystack 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for scrapeunblocker-haystack 0.4.0
File Size Uploaded Provenance
scrapeunblocker_haystack-0.4.0.tar.gz 17.2 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for scrapeunblocker-haystack 0.4.0
File Interpreter ABI Platform
scrapeunblocker_haystack-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 32.3 kB

Release files / scrapeunblocker_haystack-0.4.0.tar.gz

Download URL scrapeunblocker_haystack-0.4.0.tar.gz
Size 17.2 kB
Tags Source
SHA-256 checksum
How to use checksums
bb7710ca199dd7e3835be18c184a4d5dfae2fd6a1f5ff1991fa2e7bca3d67545
BLAKE2b-256 checksum
How to use checksums
05103f31560dedf6ccfbbf8c4faec5051a410df9479dc0164ee560f9d75762c8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.

Transparency log

Release files / scrapeunblocker_haystack-0.4.0-py3-none-any.whl

Download URL scrapeunblocker_haystack-0.4.0-py3-none-any.whl
Size 15.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
557647708d73464cf22ad7a394d565f680ae0b6ba9788e2c4712a72bb0cafd58
BLAKE2b-256 checksum
How to use checksums
31e6d80c7d8ffc5488497bf590217f514f0c3df82475f80a011120ec35683c14
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.

Transparency log

Security for scrapeunblocker-haystack 0.4.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for scrapeunblocker_haystack-0.4.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page