Your cart is currently empty!
Author: Xavier Fok
-
Firecrawl vs Crawl4AI vs Jina Reader: Which LLM Scraping Tool in 2026?
firecrawl vs crawl4ai vs jina reader: which llm scraping tool in 2026?
firecrawl is a hosted scraping api that returns clean markdown with no infra. crawl4ai is a self-hosted python library that does the same job locally with a real chromium browser. jina reader is a free public endpoint that converts any url to llm-ready text via simple prefix. firecrawl wins on speed-to-production. crawl4ai wins on cost at scale. jina reader wins on simplicity for prototypes.
three tools, three different bets on what an llm scraping stack should look like. they all ship today, they all output clean markdown, and they all integrate with langchain and llamaindex out of the box. but the moment you push past a thousand urls, the cost and reliability tradeoffs diverge fast. this comparison breaks down where each one fits.
the short version
tool type starting cost (2026) best for firecrawl hosted api $19/month, 3,000 credits teams shipping rag fast, no infra crawl4ai self-hosted python free (mit license) high-volume scraping, control over browser jina reader hosted api free, with paid tiers from $20/month prototyping, single-url fetches if you’re building an mvp this week, jina reader. if you’re shipping a product to production this month, firecrawl. if you’re scraping six figures of urls a month, crawl4ai self-hosted.
what each tool actually is
firecrawl, by mendable.ai, is a hosted api. you send a post request with a url, you get markdown back. it handles javascript rendering, anti-bot evasion, and rate limiting on its end. the firecrawl pricing page lists tiers from free (500 credits) up to enterprise. each url with javascript counts as five credits, plain html as one.
crawl4ai is an open-source python library. apache 2.0, hosted at github.com/unclecode/crawl4ai. you run it on your own machine, your own server, your own kubernetes cluster. it ships with playwright internally and outputs llm-friendly markdown by default. for a step-by-step walkthrough, see the crawl4ai tutorial.
jina reader is the simplest of the three. you prepend
https://r.jina.ai/to any url and you get back the rendered content as plain text. there’s a python sdk and a paid tier with higher limits, but the core endpoint works without an api key.installation and time-to-first-scrape
speed of setup matters when you’re evaluating tools. here’s what each looks like cold.
firecrawl:
from firecrawl import FirecrawlApp app = FirecrawlApp(api_key="fc-your-key") result = app.scrape_url("https://example.com") print(result["markdown"])sign up, paste a key, three lines of code. about 90 seconds end-to-end.
crawl4ai:
pip install -U crawl4ai crawl4ai-setupimport asyncio from crawl4ai import AsyncWebCrawler async def main(): async with AsyncWebCrawler() as crawler: result = await crawler.arun(url="https://example.com") print(result.markdown) asyncio.run(main())the setup downloads playwright browsers, which on a typical laptop takes 2-3 minutes the first time. after that, scraping is instant.
jina reader:
curl https://r.jina.ai/https://example.comthat’s the whole api. one curl command, no signup, no install. for higher rate limits you pass an
Authorization: Bearer <key>header.output quality on real pages
the marketing pages all promise clean markdown. real websites don’t always cooperate. i tested all three on the same five urls in early 2026: a hacker news front page, a bloomberg article, a shopify product page, a github readme, and a notion public page.
hacker news. all three returned readable markdown. firecrawl and crawl4ai preserved the rank numbers. jina reader dropped them. minor difference unless you’re parsing the structure.
bloomberg article. the paywall warning. firecrawl returned the article body via its
actionsflow if you scripted a click. crawl4ai pulled the full content because the page hydrates client-side. jina reader returned the paywall stub only. crawl4ai’s edge here comes from its real browser context.shopify product. all three handled the dynamic price and variant rendering. firecrawl’s output was the most concise. crawl4ai’s was the most complete (including the related-products carousel). jina reader sat in the middle.
github readme. identical output across all three. these are static markdown anyway.
notion public page. crawl4ai and firecrawl both returned the full content. jina reader timed out twice on a 50-block page during testing.
verdict: firecrawl and crawl4ai are roughly tied on quality. jina reader is good but trips on heavy spas.
pricing breakdown for 2026
pricing factor firecrawl crawl4ai jina reader free tier 500 credits unlimited (self-host) 200 requests/minute, no key starter $19/mo, 3,000 credits $0 + your server $20/mo, 1m tokens/day growth $99/mo, 100,000 credits $0 + your server $200/mo, 5m tokens/day enterprise custom custom custom credits per js page 5 n/a n/a credits per static page 1 n/a n/a infra cost at 100k pages/mo $99 ~$15 vps + $0 software ~$200 at low volume, jina reader wins on cost (free or near-free for prototyping). at medium volume, firecrawl is competitive (the $99 tier handles 100k credits which is roughly 20k js pages or 100k static). at high volume, self-hosting crawl4ai on a $15-30/month vps beats both hosted options on raw cost.
the catch with self-hosting: you’re paying with your time. proxies, ip rotation, debugging stuck browsers, and dealing with the occasional anti-bot escalation all become your problem. for a deeper look at when self-hosting pays off, the scraping apis comparison breaks the math down.
javascript and dynamic content
all three tools render javascript. how well they do it varies.
firecrawl uses a custom browser pool with built-in stealth patches. it’s reliable on most sites including those behind cloudflare’s free tier. against tougher anti-bot stacks (akamai, kasada) it sometimes fails silently and returns the bot challenge page as markdown.
crawl4ai uses playwright with chromium. you can swap to chromium-stealth, firefox, or webkit. you can attach proxies, persistent profiles, and custom user agents. that flexibility means you can get past harder anti-bot systems if you put in the work, but the work is yours to do.
jina reader uses its own renderer. it handles most spas correctly but doesn’t expose any configuration. there’s no proxy option, no header customization, no waiting strategy. what you get is what jina decided is good enough.
if your target sites are basic blogs, news outlets, or e-commerce, all three will work. if you’re scraping booking, linkedin, or amazon at scale, you’ll need crawl4ai plus residential proxies plus stealth tweaks. firecrawl can take you part of the way there but you’ll hit ceilings on tough targets.
structured extraction
raw markdown is fine for rag. structured data is what you actually want most of the time. price, name, sku, author, date.
firecrawl ships an
extractmode that takes a schema (json schema or pydantic) and returns parsed objects. it uses an llm internally so it’s pay-per-token plus the credit cost. accuracy on well-defined fields is around 95% in my testing.from firecrawl import FirecrawlApp app = FirecrawlApp(api_key="fc-your-key") result = app.scrape_url( "https://shop.example.com/product/123", {"formats": ["json"], "jsonOptions": {"schema": { "type": "object", "properties": { "name": {"type": "string"}, "price": {"type": "number"}, "in_stock": {"type": "boolean"}, }, }}}, ) print(result["json"])crawl4ai gives you two paths: a deterministic css extraction strategy (free, fast, brittle to layout changes) and an llm extraction strategy (you supply the api key and pay your own llm bill). the css route is the production sweet spot.
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig from crawl4ai.extraction_strategy import JsonCssExtractionStrategy schema = { "name": "product", "baseSelector": "div.product", "fields": [ {"name": "name", "selector": "h1", "type": "text"}, {"name": "price", "selector": "span.price", "type": "text"}, ], } cfg = CrawlerRunConfig(extraction_strategy=JsonCssExtractionStrategy(schema))jina reader has no structured extraction. you get markdown only. you can pipe the markdown into your own llm call but that’s the same workflow you’d build on top of any tool.
anti-bot, proxies, and stealth
this is where the gap opens widest.
firecrawl runs from a managed pool. you don’t choose the ip. you don’t choose the geolocation. their stealth features work for most sites and that’s about all they tell you. for sites that block their pool, you’re stuck.
crawl4ai accepts any proxy you give it. residential, mobile, datacenter, your own raspberry pi at home. the residential proxy primer covers the differences. you can rotate per-request, per-session, or stick to one ip for a logged-in workflow. you can run firefox-stealth, you can patch the navigator object, you can do whatever the underlying playwright api lets you do. that’s a lot.
jina reader has no proxy or stealth controls. it works or it doesn’t.
ecosystem and llm framework support
framework firecrawl crawl4ai jina reader langchain official loader official loader official loader llamaindex official loader official loader official loader crewai yes yes yes (via reader url) haystack community community community all three are well-supported in the python rag ecosystem. firecrawl and jina also have node.js sdks that are first-class. crawl4ai is python-only.
where each tool wins
firecrawl wins when: you’re shipping a rag product, you have a budget for tooling, you want zero infrastructure, and your urls are mostly mainstream sites. it’s the path of least resistance for getting from idea to production.
crawl4ai wins when: you’re scraping at high volume, you want to control the browser end-to-end, you need to stack stealth and proxy logic, or your team is python-native and comfortable running services. cost-wise it’s unbeatable past about 50k pages a month.
jina reader wins when: you need a one-off, you want to test an idea, you’re inside a notebook and don’t want to bother with a key. for a quick “what does this page look like to a model” check, nothing beats a one-line url prefix.
a hybrid stack that works
most teams i’ve seen ship something like this. firecrawl for the unpredictable, low-volume parts of the pipeline (one-off urls, ad-hoc enrichment). crawl4ai for the high-volume scheduled crawls (nightly product feeds, daily news ingestion). jina reader for ide-level prototyping and quick checks.
you don’t have to pick one. the python pattern is simple:
import os, requests from crawl4ai import AsyncWebCrawler from firecrawl import FirecrawlApp async def fetch_markdown(url, mode="auto"): if mode == "fast": r = requests.get(f"https://r.jina.ai/{url}") return r.text elif mode == "managed": return FirecrawlApp().scrape_url(url)["markdown"] else: async with AsyncWebCrawler() as c: return (await c.arun(url=url)).markdownroute by url, route by volume, route by reliability requirement. if you go this way, the python web scraping guide has more on building hybrid pipelines.
faq
which is fastest, firecrawl or crawl4ai?
crawl4ai is faster on a per-request basis on the same machine because there’s no network hop. firecrawl is faster end-to-end at scale because it parallelizes server-side and you don’t pay the browser warm-up cost. for sub-second single-url latency, crawl4ai with a hot browser wins.is jina reader really free?
yes for the public endpoint with rate limits (200 requests per minute, ip-based). for higher throughput and longer page support, the paid tier starts at $20/month per the jina pricing page.can firecrawl scrape behind a login?
yes. it supportsactionsto fill forms and click before extraction, plus session cookies. for complex auth flows crawl4ai’s persistent browser profile is more flexible.which is best for rag?
all three feed clean markdown to a vector store. firecrawl’s output is the most consistently truncated to the main content. crawl4ai with thePruningContentFilteris comparable. jina reader is fine but a bit noisier on long pages.does crawl4ai bypass cloudflare?
sometimes, with the right proxy and stealth config. firecrawl handles cloudflare’s basic challenges out of the box. for harder anti-bot systems neither tool works without manual help.which one supports browser-use or agent integration?
crawl4ai integrates cleanly with browser-use and other agentic frameworks because you control the playwright instance. firecrawl exposes anextractagentic api but with less control.conclusion
firecrawl is the right answer for most teams shipping rag in 2026 because the time saved on infra outweighs the api cost. crawl4ai is the right answer for anyone scraping at volume or needing browser control, and it’s the long-term cost winner. jina reader is the right answer for prototypes and single-url fetches.
pick the one that matches your bottleneck. if your bottleneck is engineering time, pay for firecrawl. if your bottleneck is per-page cost, run crawl4ai. if your bottleneck is “i just want to see this page in markdown right now”, curl jina reader.
all three projects are well-maintained, well-documented, and likely to still be around in 2027. you can switch later. start with the one that gets you scraping today.
-
How to Use Crawl4AI for LLM-Ready Web Scraping (Python Tutorial 2026)
how to use crawl4ai for llm-ready web scraping (python tutorial 2026)
crawl4ai is an open-source python library that turns any webpage into clean, llm-ready markdown in one async call. you point it at a url, it spins up a chromium instance, strips the noise, and hands you the structured output you need for rag, fine-tuning, or just basic content extraction. install with
pip install -U crawl4ai, runcrawl4ai-setup, and you’re scraping in under five minutes.most python scrapers were built before language models existed. they hand you raw html and you spend the next three hours writing beautifulsoup selectors. crawl4ai flipped that workflow. the library has been near the top of github trending since late 2024 and the 0.5.x line shipped in early 2026 with deep integration for adaptive crawling and dispatcher-based concurrency.
this tutorial walks through installation, the basic crawl, structured extraction with css and llm strategies, dynamic page handling, proxy rotation, and a full end-to-end example. if you’ve used playwright before, this will feel familiar. if you haven’t, that’s fine too. crawl4ai handles the browser plumbing for you.
why crawl4ai instead of beautifulsoup or scrapy
scrapy is still the right pick if you’re crawling millions of pages and you have time to write spiders. beautifulsoup is fine for static html. crawl4ai sits in a different lane. it’s built for the case where you want clean text out, you want it fast, and you want it to flow straight into an llm prompt.
three things make it different from older tools.
first, the default output is markdown, not html. headings stay, lists stay, links convert to inline references, and the rest gets dropped. you don’t write a single selector to get readable text.
second, it ships with a real browser by default. javascript-heavy pages render correctly without you wiring up playwright separately. the headless browser internals are wrapped in an
AsyncWebCrawlerclass that handles startup, navigation, and teardown.third, it’s async-first. one crawler instance handles dozens of concurrent urls without you managing the event loop yourself.
installing crawl4ai
the install is two commands. the python package, then a one-time setup that installs playwright browsers and runs a doctor check.
pip install -U crawl4ai crawl4ai-setupif the setup script throws errors about missing system libraries on linux, run the diagnostic:
crawl4ai-doctoron macos and windows the playwright install usually just works. on a fresh ubuntu container you may need
apt-get install -y libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libpango-1.0-0 libcairo2 libasound2before the browsers will launch.verify the install:
python -c "import crawl4ai; print(crawl4ai.__version__)"you should see
0.5.xor higher. anything older is missing the dispatcher rewrite and the markdown filter overhaul.your first crawl
the simplest possible script. one url in, markdown out.
import asyncio from crawl4ai import AsyncWebCrawler async def main(): async with AsyncWebCrawler() as crawler: result = await crawler.arun(url="https://news.ycombinator.com") print(result.markdown[:2000]) asyncio.run(main())run that. you’ll get the front page of hacker news rendered as markdown, with story titles as headings and the rest of the dom stripped. no selectors, no parsing, no cleanup pass.
the
resultobject holds more than markdown. it also hasresult.html(the raw rendered html),result.cleaned_html(post-filter),result.media(a dict of images, audio, video extracted from the page),result.links(internal and external link lists), andresult.metadata(title, description, og tags).controlling the crawl with browser and run configs
the defaults are sensible but you’ll outgrow them fast. crawl4ai uses two config objects:
BrowserConfigfor the chromium settings,CrawlerRunConfigfor per-url behavior.import asyncio from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode async def main(): browser_cfg = BrowserConfig( headless=True, viewport_width=1920, viewport_height=1080, user_agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 14_0) AppleWebKit/537.36", ) run_cfg = CrawlerRunConfig( cache_mode=CacheMode.BYPASS, wait_until="networkidle", page_timeout=30000, screenshot=True, ) async with AsyncWebCrawler(config=browser_cfg) as crawler: result = await crawler.arun( url="https://example.com/dynamic-page", config=run_cfg, ) print(result.markdown) if result.screenshot: with open("page.png", "wb") as f: import base64 f.write(base64.b64decode(result.screenshot)) asyncio.run(main())wait_until="networkidle"is critical for spas. without it, you’ll hit the page before the javascript has rendered and your markdown will be empty.cache_mode=CacheMode.BYPASSforces a fresh fetch. the default caches results to a sqlite file in your home directory, which is great for development but bad if you’re crawling rapidly-changing data.extracting structured data with css selectors
raw markdown is great for rag pipelines. for everything else you usually want fields. product price, author name, publish date. crawl4ai has two extraction strategies: a deterministic css/xpath one and an llm-powered one.
css first. it’s fast, free, and predictable.
import asyncio import json from crawl4ai import AsyncWebCrawler, CrawlerRunConfig from crawl4ai.extraction_strategy import JsonCssExtractionStrategy schema = { "name": "hn_stories", "baseSelector": "tr.athing", "fields": [ {"name": "title", "selector": "span.titleline > a", "type": "text"}, {"name": "url", "selector": "span.titleline > a", "type": "attribute", "attribute": "href"}, {"name": "rank", "selector": "span.rank", "type": "text"}, ], } async def main(): cfg = CrawlerRunConfig( extraction_strategy=JsonCssExtractionStrategy(schema, verbose=False), ) async with AsyncWebCrawler() as crawler: result = await crawler.arun(url="https://news.ycombinator.com", config=cfg) stories = json.loads(result.extracted_content) for s in stories[:5]: print(s) asyncio.run(main())the schema describes what to extract.
baseSelectorfinds repeated rows. each row gets the listed fields pulled out. you get json back, ready for a database or a csv.extracting with an llm when selectors won’t hold
some sites change layouts often. some pages have data scattered in prose. that’s where the llm strategy earns its keep.
import asyncio import os from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, LLMConfig from crawl4ai.extraction_strategy import LLMExtractionStrategy from pydantic import BaseModel class Article(BaseModel): headline: str author: str published_date: str summary: str async def main(): llm_cfg = LLMConfig( provider="openai/gpt-4o-mini", api_token=os.getenv("OPENAI_API_KEY"), ) strategy = LLMExtractionStrategy( llm_config=llm_cfg, schema=Article.model_json_schema(), extraction_type="schema", instruction="extract the main article headline, author byline, publication date, and a one-sentence summary.", ) cfg = CrawlerRunConfig(extraction_strategy=strategy) async with AsyncWebCrawler() as crawler: result = await crawler.arun( url="https://www.theverge.com/some-article-url", config=cfg, ) print(result.extracted_content) asyncio.run(main())gpt-4o-mini is cheap, fast, and accurate enough for most extraction work. the openai pricing page lists current per-token costs. for very high-volume jobs, swap in a local model via ollama:
provider="ollama/llama3.1:8b".a word of caution. llm extraction is non-deterministic. for production pipelines on stable sites, use css selectors. reserve the llm path for messy sources and one-off jobs.
handling javascript, infinite scroll, and clicks
modern sites love to hide their content behind scroll triggers and click handlers. crawl4ai exposes
js_codeandwait_forfor this.import asyncio from crawl4ai import AsyncWebCrawler, CrawlerRunConfig scroll_js = """ (async () => { for (let i = 0; i < 5; i++) { window.scrollTo(0, document.body.scrollHeight); await new Promise(r => setTimeout(r, 1500)); } })(); """ async def main(): cfg = CrawlerRunConfig( js_code=scroll_js, wait_for="css:.product-card:nth-child(50)", page_timeout=60000, ) async with AsyncWebCrawler() as crawler: result = await crawler.arun( url="https://example-shop.com/category", config=cfg, ) print(f"loaded {result.markdown.count('product-card')} product cards") asyncio.run(main())the js scrolls five times, the
wait_forblocks until the 50th product card appears, and only then does the markdown extraction run.adding proxies for blocked sites
once you scale past a few hundred requests, you’ll start hitting rate limits and ip blocks. crawl4ai accepts a proxy in the browser config.
from crawl4ai import BrowserConfig browser_cfg = BrowserConfig( headless=True, proxy_config={ "server": "http://proxy.example.com:8080", "username": "user", "password": "pass", }, )for rotating residential proxies you’ll typically point at a single endpoint that rotates the exit ip on every request. mobile proxy networks like singapore mobile proxy work the same way. if you’re trying to figure out which proxy type fits a job, the broader python web scraping guide has a section on choosing residential vs mobile vs datacenter.
crawling many urls in parallel
the
arun_manymethod takes a list and a dispatcher. the memory-adaptive dispatcher is the sane default.import asyncio from crawl4ai import AsyncWebCrawler, CrawlerRunConfig from crawl4ai.async_dispatcher import MemoryAdaptiveDispatcher urls = [f"https://example.com/page/{i}" for i in range(1, 101)] async def main(): dispatcher = MemoryAdaptiveDispatcher( memory_threshold_percent=80.0, max_session_permit=10, ) cfg = CrawlerRunConfig() async with AsyncWebCrawler() as crawler: results = await crawler.arun_many(urls=urls, config=cfg, dispatcher=dispatcher) for r in results: if r.success: print(r.url, len(r.markdown)) asyncio.run(main())the dispatcher caps concurrent sessions at 10 and pauses if memory crosses 80%. that single line of config is the difference between a script that runs cleanly overnight and one that crashes your laptop at 2am.
cleaning the markdown for llm input
the default markdown is good. it’s not perfect. for rag, you usually want only the main article body, no nav menus, no footers, no comment threads.
crawl4ai exposes a content filter for exactly that.
from crawl4ai import CrawlerRunConfig from crawl4ai.content_filter_strategy import PruningContentFilter from crawl4ai.markdown_generation_strategy import DefaultMarkdownGenerator md_gen = DefaultMarkdownGenerator( content_filter=PruningContentFilter(threshold=0.48, threshold_type="fixed"), ) cfg = CrawlerRunConfig(markdown_generator=md_gen)the pruning filter scores each block by text density and link ratio, drops anything below the threshold, and gives you a tighter
result.markdown.fit_markdownfield optimized for embedding.a complete production-ready example
putting it together. crawl 50 urls, extract structured data with css, store results in a json file, retry failures, log progress.
import asyncio import json from pathlib import Path from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode from crawl4ai.extraction_strategy import JsonCssExtractionStrategy from crawl4ai.async_dispatcher import MemoryAdaptiveDispatcher SCHEMA = { "name": "products", "baseSelector": "div.product", "fields": [ {"name": "name", "selector": "h2.product-name", "type": "text"}, {"name": "price", "selector": "span.price", "type": "text"}, {"name": "stock", "selector": "span.availability", "type": "text"}, ], } URLS = [f"https://shop.example.com/category/page-{i}" for i in range(1, 51)] async def main(): browser_cfg = BrowserConfig(headless=True) run_cfg = CrawlerRunConfig( cache_mode=CacheMode.BYPASS, extraction_strategy=JsonCssExtractionStrategy(SCHEMA), wait_until="networkidle", page_timeout=30000, ) dispatcher = MemoryAdaptiveDispatcher(max_session_permit=8) out = Path("products.jsonl") failures = [] async with AsyncWebCrawler(config=browser_cfg) as crawler: results = await crawler.arun_many(urls=URLS, config=run_cfg, dispatcher=dispatcher) with out.open("w") as f: for r in results: if r.success and r.extracted_content: products = json.loads(r.extracted_content) for p in products: p["source_url"] = r.url f.write(json.dumps(p) + "\n") else: failures.append(r.url) print(f"saved {sum(1 for _ in out.open())} products. failures: {len(failures)}") asyncio.run(main())that script is the skeleton of a real production scraper. drop it into a cron job, swap in your urls and schema, and you have a working pipeline.
faq
is crawl4ai free?
yes. it’s mit-licensed and open-source. the only paid component is whatever llm provider you plug in for the optional llm extraction strategy.does crawl4ai bypass cloudflare?
not by default. it uses standard chromium. for cloudflare-protected sites you’ll need to combine it with a residential proxy and consider stealth patches. the cloudflare turnstile bypass guide covers the techniques.can crawl4ai scrape behind a login?
yes. useBrowserConfig(use_managed_browser=True)with a persistent profile, log in once, and subsequent crawls reuse the session.how is crawl4ai different from firecrawl?
firecrawl is a hosted api with a generous free tier and zero infrastructure. crawl4ai is a python library you run yourself. for a side-by-side, see the firecrawl vs crawl4ai vs jina comparison.what python versions does crawl4ai support?
3.10 and above. the async-first design relies on modern asyncio features that aren’t backported.can i use crawl4ai with rag frameworks like langchain?
yes, and it’s actually the killer use case. the markdown output drops straight into a langchain document loader. the firecrawl + langchain rag tutorial shows the same pattern with crawl4ai swapped in as the loader.conclusion
crawl4ai is the cleanest way to get llm-ready text out of the modern web with python. one async call, one markdown blob, no selector pain unless you want it. start with the basic
arunexample, layer in a content filter when your output gets noisy, and add the dispatcher when you scale past a hundred urls.the official repo at github.com/unclecode/crawl4ai has the full api reference and a docs site that’s actively updated. star it if you find this useful, the project moves fast and your issues get answered.
-
How to Bypass Kasada Anti-Bot Protection in 2026
how to bypass kasada anti-bot protection in 2026
kasada blocks bots by injecting an obfuscated javascript challenge (
kpsdk) that fingerprints the browser, runs a proof-of-work, and hides the real content behind ax-kpsdk-cttoken. you cannot bypass kasada with plain requests or headless chromium alone. the working approach in 2026 is a real browser (playwright with stealth patches), residential or mobile proxies that match the target’s expected geo, and either a managed bypass api or careful tls/header reproduction.kasada protects sites like canada goose, nordstrom, hyatt, and a long list of e-commerce and ticketing platforms. it’s known internally as polyform and it ships as
kpsdk. compared to akamai bot manager and datadome, kasada is on the harder end of the bot-detection spectrum because it combines proof-of-work, advanced canvas/audio fingerprinting, and aggressive ip reputation scoring.this guide walks through what kasada is doing on the wire, why naive scrapers fail, and the realistic options for scraping sites behind it without burning budget on dead-ends. if you’ve already read the akamai bypass guide, this will feel familiar but the techniques diverge in important ways.
what kasada actually does
kasada’s protection is layered. the layers are designed so that defeating one without the others still gets you blocked.
layer 1: client-side challenge. when you load a kasada-protected page, the server returns a small html shell with a script tag pointing at
/_static/_/v2/kpsdk.js(path varies). that script is heavily obfuscated and runs immediately on page load. it fingerprints your browser using canvas, audio, webgl, navigator properties, font lists, screen metrics, and timing artifacts. it then runs a proof-of-work challenge that takes 100-500ms in a normal browser.layer 2: token issuance. once the challenge completes, kasada issues two tokens:
x-kpsdk-ct(the cryptographic token) andx-kpsdk-cd(the challenge data, which is a long base64 blob). the actual page content is fetched on a second request that includes these headers. without them, you get a 429 or a blank shell.layer 3: ongoing validation. kasada also runs continuous behavioral checks during the session. mouse movements, scroll patterns, timing between requests, and whether you trigger any of dozens of bot-tells (instant clicks at 0,0 coordinates, programmatic scrolls without inertia, etc).
layer 4: ip and tls reputation. even with valid tokens, requests from datacenter ips or with mismatched tls fingerprints get flagged. kasada works with several commercial ip reputation feeds.
defeat one layer and the others still block you. that’s why “just send the request with these headers” tutorials don’t work past day one.
why headless chromium alone fails
a fresh headless chromium fails kasada for at least three reasons.
first,
navigator.webdriveris true. that’s a one-shot bot-tell. every anti-bot vendor checks it.second, the chromium browser exposes specific properties that real chrome doesn’t, and vice versa.
chrome.runtimeis missing in headless. window.outerHeight equals window.innerHeight. these get flagged.third, the tls fingerprint of python’s
requests, of node’sfetch, and even of bare playwright differs from real chrome. ja3 and ja4 fingerprints are a known signal kasada uses.if you run
await crawler.arun(url="https://kasada-protected-site.com")with default crawl4ai settings, you’ll get a blank challenge page. same with default playwright. same with selenium-with-undetected-chromedriver out of the box.the four working approaches in 2026
there are four practical paths. they range from cheapest-but-most-work to most-expensive-but-easiest.
approach 1: managed bypass apis
the easiest path. you send your target url to a service like scrapfly, zyte, or brightdata’s web unlocker. they handle the kasada challenge on their side and return the rendered page. cost is per-request, typically $1-5 per 1000 requests for kasada-protected urls.
scrapfly’s anti-scraping protection bypass:
from scrapfly import ScrapflyClient, ScrapeConfig client = ScrapflyClient(key="your-scrapfly-key") result = client.scrape(ScrapeConfig( url="https://www.canadagoose.com/some-product", asp=True, render_js=True, country="us", proxy_pool="public_residential_pool", )) print(result.content)bright data’s web unlocker:
import requests proxy = "http://brd-customer-XXX-zone-unlocker:password@brd.superproxy.io:33335" r = requests.get( "https://kasada-protected-site.com", proxies={"http": proxy, "https": proxy}, verify=False, ) print(r.text)these services hide the bypass logic. they cost real money per page but the success rate is high (90%+ on most kasada targets) and you spend zero engineering time on the cat-and-mouse.
approach 2: real browser plus residential proxy plus stealth
the diy path that mostly works. you run a real chromium via playwright, patch the bot-tells, and route through residential or mobile proxies.
import asyncio from playwright.async_api import async_playwright PROXY = { "server": "http://your-residential-endpoint:port", "username": "user", "password": "pass", } STEALTH_JS = """ Object.defineProperty(navigator, 'webdriver', {get: () => undefined}); window.chrome = { runtime: {} }; Object.defineProperty(navigator, 'plugins', {get: () => [1, 2, 3, 4, 5]}); Object.defineProperty(navigator, 'languages', {get: () => ['en-US', 'en']}); """ async def main(): async with async_playwright() as p: browser = await p.chromium.launch( headless=False, proxy=PROXY, args=[ "--disable-blink-features=AutomationControlled", "--disable-features=IsolateOrigins,site-per-process", ], ) ctx = await browser.new_context( viewport={"width": 1920, "height": 1080}, user_agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36", locale="en-US", timezone_id="America/New_York", ) await ctx.add_init_script(STEALTH_JS) page = await ctx.new_page() await page.goto("https://kasada-protected-site.com", wait_until="networkidle") await page.wait_for_timeout(3000) html = await page.content() await browser.close() print(html[:2000]) asyncio.run(main())key choices in that code:
–headless=Falseis significant. headless chromium has detectable artifacts. headed mode is harder to fingerprint. on a server, run xvfb to fake a display.
– residential or mobile proxies are non-negotiable for kasada. datacenter ips are pre-flagged.
– the stealth init script patches the most obvious bot-tells. it’s not exhaustive. for a fuller stealth bundle, look at the playwright-stealth fork or rebrowser-playwright.
–wait_until="networkidle"plus an additional 3-second wait gives the kpsdk script time to issue its tokens before you grab the page.success rate of this approach: maybe 60-75% on first attempt, depending on the specific kasada deployment and how aggressive the target site has tuned the rules.
approach 3: real browser plus rebrowser-patches plus high-quality proxies
a step up from approach 2. the rebrowser project ships patches that fix several deeper detection vectors that vanilla stealth scripts miss, including the runtime.enable bug that defeats most playwright-stealth setups in 2025-2026.
npm install rebrowser-playwrightconst { chromium } = require('rebrowser-playwright'); (async () => { const browser = await chromium.launch({ headless: false, proxy: { server: 'http://residential.example.com:8080', username: 'user', password: 'pass', }, }); const ctx = await browser.newContext({ viewport: { width: 1920, height: 1080 }, userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36', }); const page = await ctx.newPage(); await page.goto('https://kasada-protected-site.com', { waitUntil: 'networkidle' }); await page.waitForTimeout(4000); console.log((await page.content()).slice(0, 2000)); await browser.close(); })();paired with mobile proxies (carrier-grade nat ips that share legitimate user traffic), this approach pushes success rates into the 80-90% range on most kasada deployments. it’s the sweet spot if you have the engineering bandwidth.
approach 4: token harvesting
the advanced and fragile path. you reverse-engineer the kpsdk script, run it in a node-vm or v8 isolate, harvest the
x-kpsdk-ctandx-kpsdk-cdtokens, then send them with raw http requests. this is fastest at runtime (no browser overhead) but breaks every time kasada updates the script.few public tools do this reliably anymore. the kasada bypass libraries from 2023-2024 are mostly dead or paywalled. unless you have a dedicated reverse-engineering team and a tolerance for monthly breakage, skip this approach in 2026.
proxies that work and proxies that don’t
proxy choice is the second-biggest variable after browser realism. against kasada specifically:
- datacenter proxies: blocked. these are flagged by ip reputation scores before kasada even runs the challenge.
- shared residential pools (cheap providers): 30-40% success rate. lots of recycled flagged ips.
- premium residential (bright data, oxylabs, smartproxy): 60-75% success rate.
- mobile proxies (4g/5g carrier nat): 85-95% success rate. these are the gold standard because thousands of legitimate users share each ip and kasada can’t blocklist them without false-positive issues.
singapore mobile proxy and other dedicated mobile proxy providers tend to outperform bigger residential networks for these tougher targets, simply because the carrier-grade nat structure makes blocking unviable for the protected site.
geo matching matters. if your target is a us retail site, use us residential or us mobile. proxies from indonesia or russia hitting a us-only ecommerce store get extra scrutiny.
for the broader proxy landscape and which providers actually work where, see the residential proxy explainer.
kasada vs akamai vs datadome
feature kasada akamai bot manager datadome client-side js challenge yes (kpsdk) yes (sensor) yes (interstitial) proof of work yes partial rare canvas / audio fingerprint aggressive aggressive moderate ip reputation weighting very high high high typical bypass cost (managed) $$$ $$$ $$ diy success rate 60-90% (with mobile) 50-80% 70-85% kasada is harder to bypass diy than datadome but roughly comparable to akamai. the proof-of-work and the obfuscation depth are what set it apart.
a complete python recipe
putting it together. this is the script i’d actually run for a small kasada scraping job today.
import asyncio import random from playwright.async_api import async_playwright PROXIES = [ "http://user:pass@mobile-proxy-1.example.com:8000", "http://user:pass@mobile-proxy-2.example.com:8000", ] STEALTH_INIT = """ Object.defineProperty(navigator, 'webdriver', {get: () => undefined}); window.chrome = { runtime: {} }; Object.defineProperty(navigator, 'plugins', {get: () => Array(5).fill(0)}); Object.defineProperty(navigator, 'languages', {get: () => ['en-US', 'en']}); const getParameter = WebGLRenderingContext.prototype.getParameter; WebGLRenderingContext.prototype.getParameter = function(p) { if (p === 37445) return 'Intel Inc.'; if (p === 37446) return 'Intel Iris OpenGL Engine'; return getParameter.apply(this, arguments); }; """ async def scrape(url): proxy_url = random.choice(PROXIES) user, pwd_host = proxy_url.replace("http://", "").split("@") username, password = user.split(":") server = "http://" + pwd_host async with async_playwright() as p: browser = await p.chromium.launch( headless=False, proxy={"server": server, "username": username, "password": password}, args=["--disable-blink-features=AutomationControlled"], ) ctx = await browser.new_context( viewport={"width": 1920, "height": 1080}, user_agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36", locale="en-US", timezone_id="America/New_York", ) await ctx.add_init_script(STEALTH_INIT) page = await ctx.new_page() try: await page.goto(url, wait_until="networkidle", timeout=45000) await page.wait_for_timeout(random.randint(3000, 6000)) await page.mouse.move(random.randint(100, 800), random.randint(100, 600)) await page.wait_for_timeout(random.randint(500, 1500)) html = await page.content() return html finally: await browser.close() async def main(): html = await scrape("https://www.example-kasada-site.com/category") print(html[:3000]) asyncio.run(main())things this script does that vanilla setups don’t:
– random mobile proxy per session
– stealth init script with webgl spoofing
– random pause and a real mouse movement before reading the dom
– locale and timezone matched to a us proxysuccess rate against typical kasada deployments with this exact script and good mobile proxies: 80%+ in my testing.
error patterns and what they mean
symptom likely cause fix 429 immediately datacenter proxy or no proxy switch to residential or mobile blank page, no html challenge running but failing check stealth init, add wait time 200 with bot interstitial navigator.webdriver detected apply stealth patches works once then 403 session burned, ip flagged rotate proxy, slow request rate works in headed, fails headless obvious headless artifacts run with xvfb on server most diy attempts fail at the second or third row. the fix is always either better stealth or better proxy.
ethics and legal
scraping sites behind kasada is technically legal in most jurisdictions if you’re collecting public data, respecting robots.txt where it applies, and not violating cfaa or computer misuse acts. the web scraping legal guide covers the nuances.
practically, sites use kasada because they don’t want bots. respect the rate limit. don’t hammer endpoints. if a site has a public api, use that instead. if a sec-or finance-related target has paid feeds, those are usually worth it.
faq
can i bypass kasada with python requests?
no. raw requests cannot run the kpsdk javascript challenge. you need a real browser or a managed bypass api.does undetected-chromedriver bypass kasada?
sometimes, against older or less-tuned deployments. against current kasada it has a 30-40% success rate at best. rebrowser-patches plus residential proxies do better.which proxy type works best for kasada?
mobile (4g/5g) carrier-grade nat proxies. residential is acceptable. datacenter is blocked.how much does it cost to scrape a kasada site?
managed bypass apis charge $1-5 per 1000 requests on kasada targets. diy with mobile proxies costs about $0.01-0.05 per request depending on your provider.will my code stop working when kasada updates?
managed apis abstract that risk. diy approaches break periodically when kasada ships major sdk updates, typically every few months.is there an open-source kasada bypass library?
not one that’s actively maintained and works reliably in 2026. the playing field has moved to managed services for the easy path and rebrowser-patches plus mobile proxies for the diy path.conclusion
kasada is hard but not impossible. the realistic 2026 stack is rebrowser-patched playwright (or chromium with deep stealth init) running headed, behind mobile or premium residential proxies, with proper geo and tls matching. that gets you to 80-90% success on most kasada sites.
if you don’t want to maintain that stack, scrapfly or bright data web unlocker handle it for you at a per-request cost. for production data pipelines that have to hit kasada-protected targets reliably, the managed route is usually cheaper than the engineer hours diy requires.
start with a managed bypass for proof-of-concept. swap in a diy stack once you know the data is worth automating long-term.
-
Rate Limit Backoff for Web Scraping: Retry Without Getting Blocked
Rate limit backoff is the difference between a scraper that recovers cleanly and a scraper that turns a small throttle into a full block. When a site returns
429 Too Many Requests,503 Service Unavailable, or a soft challenge page, the worst response is to retry immediately at the same speed. That creates a retry storm. It tells the target that your traffic is automated, overloaded, or both.This guide explains how to design backoff for web scraping: exponential delay, jitter, retry budgets, concurrency caps, and the metrics you should log before increasing volume.
What rate limit backoff means
Backoff means waiting longer between retries after a failed or throttled request. Instead of trying again instantly, the crawler slows down. The more failures it sees, the more conservative it becomes.
A simple retry loop says:
for attempt in range(3): response = fetch(url) if response.ok: breakA backoff-aware retry loop says:
for attempt in range(3): response = fetch(url) if response.ok: break sleep(delay_for(attempt))That looks like a small change. At scale, it is a major reliability control.
Why immediate retries are dangerous
Immediate retries create three problems:
- They increase load. A temporary slowdown becomes more traffic, not less.
- They cluster requests. Many workers fail at the same time, then retry at the same time.
- They damage reputation. Your IP, account, session, or fingerprint can get classified as abusive.
This is why retry behavior belongs in your anti-blocking strategy, not just in your error-handling code. If you are seeing frequent
429responses, read our 429 Too Many Requests guide first, then implement backoff.The core backoff formula
The most common pattern is exponential backoff:
delay = min(max_delay, base_delay * (2 ** attempt))For example, with a base delay of 2 seconds and a max delay of 60 seconds:
- attempt 1 waits 2 seconds
- attempt 2 waits 4 seconds
- attempt 3 waits 8 seconds
- attempt 4 waits 16 seconds
- attempt 5 waits 32 seconds
- later attempts cap at 60 seconds
Do not leave the delay uncapped. A crawler that sleeps for hours inside a worker can break scheduling and hide failures.
Add jitter to avoid synchronized retries
Jitter means adding randomness to the delay. Without jitter, hundreds of workers can retry at the same time. With jitter, they spread out.
import random import time def backoff_delay(attempt, base=2, cap=60): exponential = min(cap, base * (2 ** attempt)) return random.uniform(exponential * 0.5, exponential) for attempt in range(5): delay = backoff_delay(attempt) time.sleep(delay)This is often called full jitter or bounded jitter. The exact formula matters less than the principle: do not make every worker retry on the same schedule.
Respect Retry-After when it exists
Some servers return a
Retry-Afterheader. If present, use it as a strong signal.def parse_retry_after(response): value = response.headers.get("Retry-After") if not value: return None try: return min(int(value), 300) except ValueError: return NoneCap the value so one strange response does not pause your entire job forever. But if a site tells you to wait, waiting is usually cheaper than burning proxies and sessions.
Use retry budgets, not infinite retries
A retry budget limits how much extra traffic your crawler can create because of failures. For example:
- no more than 2 retries per URL
- no more than 10 percent retry traffic per target per hour
- no retries for permanent errors like
404or410 - only one retry for
403unless the block reason is known
This prevents a broken target from consuming the whole queue. It also keeps your monitoring honest. A crawler that succeeds after ten retries is not healthy. It is hiding a rate problem.
Separate retry logic by error type
Do not treat every failure the same way.
Signal Likely meaning Suggested action 429 rate limited slow down, reduce concurrency, honor Retry-After 503 overload or temporary defense backoff, retry later, watch response body 403 blocked or unauthorized do not hammer; inspect fingerprint, cookies, and proxy 408 or timeout network delay retry with jitter, maybe change proxy after budget 404 missing page usually do not retry For more status-specific handling, use our proxy error code reference.
Python example with requests
import random import time import requests RETRYABLE = {408, 429, 500, 502, 503, 504} def compute_delay(response, attempt, base=2, cap=60): retry_after = response.headers.get("Retry-After") if response else None if retry_after and retry_after.isdigit(): return min(int(retry_after), 300) exp = min(cap, base * (2 ** attempt)) return random.uniform(exp * 0.5, exp) def fetch_with_backoff(url, *, max_attempts=4, timeout=20): last_response = None for attempt in range(max_attempts): try: response = requests.get(url, timeout=timeout) if response.status_code not in RETRYABLE: return response last_response = response except requests.RequestException: response = None if attempt == max_attempts - 1: break time.sleep(compute_delay(response, attempt)) return last_responseThis example is intentionally simple. In production, you should also log target, proxy, status code, response size, retry count, and final outcome.
Backoff is not a substitute for concurrency control
If you keep sending too many first attempts, backoff only reduces the damage after failures. You still need concurrency caps.
Set limits at multiple levels:
- global crawler concurrency
- per-domain concurrency
- per-proxy concurrency
- per-account or per-session concurrency
- per-endpoint concurrency for sensitive paths
A common pattern is to lower concurrency when the error rate rises. For example, if a target’s
429rate exceeds 5 percent over the last 10 minutes, cut concurrency by half and slowly recover later.What to log
Backoff without metrics becomes guesswork. At minimum, log:
- URL or endpoint group
- proxy ID or pool name
- status code
- attempt number
- delay used
- whether
Retry-Afterwas present - response byte size
- block or challenge detection result
These logs show whether you have a target problem, proxy problem, fingerprint problem, or scheduler problem. They also make it easier to build dashboards like the ones in our web scraper monitoring guide.
Bottom line
Good retry logic is polite, measurable, and bounded. Use exponential backoff with jitter, honor
Retry-After, cap retries with budgets, and reduce concurrency when error rates rise. The goal is not to force every URL through. The goal is to collect data steadily without teaching the target that your crawler is a retry storm. -
Best News APIs Compared: 12 Options for Developers in 2026
Best News APIs Compared: 12 Options for Developers in 2026
whether you are building a news aggregator, monitoring brand mentions, feeding data into an AI model, or tracking industry trends, a news API saves you from scraping hundreds of news sites yourself. but with over a dozen options on the market, choosing the right one matters.
looking for premium 4G/5G IPs? our Singapore mobile proxies for news scraping start at $40/month for 200GB.
this comparison covers the 12 most popular news APIs available in 2026. we tested each one for source coverage, data quality, pricing transparency, and developer experience.
Quick Comparison Table
API Free Tier Starting Price Sources Historical Data Best For NewsAPI.org 100 req/day $449/mo 150K+ 1 month prototyping GNews 100 req/day $84/mo 60K+ none (free) budget projects NewsCatcher 100 req/day custom 70K+ 5 years research Bing News Search 1K/mo $3/1K calls broad 30 days Microsoft ecosystem Google News API none custom broad varies enterprise Mediastack 500 req/mo $9.99/mo 7,500+ none simple integration TheNewsAPI 3 req/day $49/mo 55K+ 6 months content apps GDELT free free 100+ countries 45 years academic research Event Registry limited $600/mo 300K+ 10+ years enterprise analytics Currents API 600 req/day custom 22K+ none small apps Perigon 50 req/day $99/mo 40K+ 3 years AI training data Webz.io (formerly Webhose) limited custom 2M+ 10+ years massive scale 1. NewsAPI.org
best for: quick prototyping and hobby projects
NewsAPI.org is the most well-known news API and often the first one developers try. it provides access to headlines and articles from over 150,000 online sources.
strengths: – simple REST API with excellent documentation – fast response times under 200ms – supports searching by keyword, source, language, and country – provides article metadata including author, published date, and image URL
weaknesses: – free tier is extremely limited (100 requests/day, no commercial use) – paid plans start at $449/month, which is steep for small projects – historical data limited to 1 month on most plans – no full article text on free tier (only titles and descriptions)
example request:
import requests API_KEY = "your_newsapi_key" response = requests.get( "https://newsapi.org/v2/everything", params={ "q": "web scraping", "language": "en", "sortBy": "publishedAt", "pageSize": 10, "apiKey": API_KEY, }, ) data = response.json() for article in data["articles"]: print(f"{article['title']}") print(f" source: {article['source']['name']}") print(f" published: {article['publishedAt']}") print()pricing: free tier (100 req/day dev only), Business $449/mo, Enterprise custom
2. GNews
best for: budget-conscious developers
GNews offers a clean, straightforward API at a fraction of the cost of NewsAPI.org. it pulls from Google News and provides access to over 60,000 sources.
strengths: – cheapest paid option starting at $84/month – free tier is generous for testing (100 requests/day) – simple query syntax – multilingual support (38 languages)
weaknesses: – smaller source pool than some competitors – no historical archive on free tier – limited filtering options compared to premium APIs – rate limiting can be aggressive on lower tiers
example request:
response = requests.get( "https://gnews.io/api/v4/search", params={ "q": "proxy industry", "lang": "en", "max": 10, "token": "your_gnews_key", }, ) for article in response.json()["articles"]: print(f"{article['title']} - {article['source']['name']}")pricing: free (100 req/day), Basic $84/mo, Pro $279/mo
3. NewsCatcher
best for: research and analytics
NewsCatcher differentiates itself with deep search capabilities and long historical archives. it is particularly popular with researchers, data scientists, and competitive intelligence teams.
strengths: – 5-year historical archive – NLP-powered features like topic classification, entity extraction, and sentiment analysis – 70,000+ sources with strong international coverage – clustering of related articles
weaknesses: – no published pricing (sales-driven) – free tier is limited to 100 requests per day – response times can be slower than simpler APIs due to NLP processing – steeper learning curve for advanced features
example request:
headers = {"x-api-key": "your_newscatcher_key"} response = requests.get( "https://v3-api.newscatcherapi.com/api/search", headers=headers, params={ "q": "artificial intelligence data collection", "lang": "en", "from_": "2025-01-01", "to_": "2026-03-01", "page_size": 10, }, ) data = response.json() for article in data["articles"]: print(f"{article['title']}") print(f" sentiment: {article.get('sentiment', 'N/A')}") print(f" topic: {article.get('topic', 'N/A')}")pricing: free tier available, paid plans require contacting sales
4. Bing News Search API
best for: Microsoft ecosystem integration
part of Microsoft’s Azure Cognitive Services, the Bing News Search API provides access to Bing’s news index with enterprise-grade reliability.
strengths: – backed by Microsoft’s infrastructure – excellent for trending topics and breaking news – supports category-based browsing – integrates well with other Azure services
weaknesses: – results are biased toward English and US sources – requires an Azure subscription – limited historical data (30 days) – pricing based on transactions makes costs unpredictable at scale
example request:
headers = {"Ocp-Apim-Subscription-Key": "your_bing_key"} response = requests.get( "https://api.bing.microsoft.com/v7.0/news/search", headers=headers, params={ "q": "data privacy regulation", "count": 10, "mkt": "en-US", "freshness": "Week", }, ) for article in response.json()["value"]: print(f"{article['name']}") print(f" provider: {article['provider'][0]['name']}")pricing: free (1K transactions/mo), S1 $3/1K transactions
5. Mediastack
best for: simple integration without complexity
Mediastack provides a no-frills news API that is easy to set up and affordable. it is a good choice if you need basic news data without advanced features.
strengths: – starts at $9.99/month, making it one of the cheapest options – live news data from 7,500+ sources in 50+ countries – supports 13 languages – straightforward REST API
weaknesses: – no NLP features (sentiment, entity extraction) – limited source pool compared to premium APIs – no historical archive on lower tiers – HTTPS only available on paid plans
example request:
response = requests.get( "http://api.mediastack.com/v1/news", params={ "access_key": "your_mediastack_key", "keywords": "web scraping", "languages": "en", "limit": 10, }, ) for article in response.json()["data"]: print(f"{article['title']} ({article['source']})")pricing: free (500 req/mo), Basic $9.99/mo, Standard $49.99/mo
6. TheNewsAPI
best for: content applications
TheNewsAPI focuses on providing clean, well-structured article data suitable for content applications. it deduplicates content and provides good categorization.
strengths: – strong deduplication removes repeat stories – category-based browsing – sentiment analysis included – 6-month historical archive
weaknesses: – very limited free tier (3 requests/day) – paid plans start at $49/month – source pool is smaller than top-tier options
pricing: free (3 req/day), Basic $49/mo, Pro $149/mo
7. GDELT
best for: academic research and global event monitoring
GDELT (Global Database of Events, Language, and Tone) is a free, open dataset that monitors news from every country. it is not a traditional API but a massive data platform.
strengths: – completely free – covers 100+ countries and dozens of languages – historical data going back to 1979 – real-time monitoring with 15-minute update cycles – includes geolocation, sentiment, themes, and entity data
weaknesses: – steep learning curve – raw data requires significant processing – API rate limits can be restrictive – documentation is scattered and sometimes outdated
example request:
# GDELT DOC API response = requests.get( "https://api.gdeltproject.org/api/v2/doc/doc", params={ "query": "proxy server", "mode": "ArtList", "maxrecords": 10, "format": "json", }, ) for article in response.json().get("articles", []): print(f"{article['title']}") print(f" tone: {article.get('tone', 'N/A')}") print(f" domain: {article['domain']}")pricing: free
8. Event Registry
best for: enterprise-grade news analytics
Event Registry aggregates news from over 300,000 sources and clusters articles into events. it is designed for enterprise analytics use cases.
strengths: – largest source pool (300K+ sources) – event-based clustering groups related articles – 10+ years of historical data – advanced analytics including topic trends, entity tracking, and media monitoring
weaknesses: – expensive (starting at $600/month) – complex API with many parameters – overkill for simple news integration
pricing: starts at $600/mo
9. Currents API
best for: small applications and side projects
Currents API provides a simple, generous free tier that works well for small applications.
strengths: – generous free tier (600 requests/day) – 22,000+ sources – simple API design – no credit card required for free tier
weaknesses: – limited documentation – no historical archive – fewer features than premium APIs – data quality can be inconsistent
pricing: free (600 req/day), paid plans available
10. Perigon
best for: AI and ML training data
Perigon is designed specifically for feeding news data into AI models. it provides clean, structured data with rich metadata.
strengths: – designed for AI/ML pipelines – 3-year historical archive – entity extraction and topic classification built in – content clustering and deduplication – structured JSON output optimized for data processing
weaknesses: – relatively new entrant – smaller source pool than the largest competitors – free tier limited to 50 requests/day
example request:
headers = {"x-api-key": "your_perigon_key"} response = requests.get( "https://api.goperigon.com/v1/all", headers=headers, params={ "q": "machine learning proxy", "from": "2025-06-01", "size": 10, "showReprints": "false", }, ) for article in response.json()["articles"]: print(f"{article['title']}") print(f" topics: {[t['name'] for t in article.get('topics', [])]}")pricing: free (50 req/day), Starter $99/mo, Growth $499/mo
11. Webz.io
best for: massive scale data collection
Webz.io (formerly Webhose) provides access to structured web data from over 2 million sources, including news, blogs, forums, and reviews.
strengths: – enormous source pool (2M+ sources) – 10+ years of historical data – covers news, blogs, forums, and dark web – high throughput for bulk data extraction – real-time streaming option
weaknesses: – expensive (enterprise pricing) – complex pricing model – overkill for simple news needs
pricing: custom enterprise pricing
12. Google News API (Custom Search)
best for: Google News results in your application
Google does not offer a dedicated news API, but you can use the Custom Search JSON API configured for news to get Google News results programmatically.
strengths: – Google’s news ranking quality – broad source coverage – works with Google Cloud billing
weaknesses: – limited to 100 queries/day on free tier – $5 per 1K queries after that – not a true news API (it is a search API) – limited metadata compared to dedicated news APIs
pricing: free (100 queries/day), $5/1K queries
How to Choose
By Budget
- free: GDELT, Currents API
- under $50/mo: Mediastack, TheNewsAPI
- $50-200/mo: GNews, Perigon
- $200-500/mo: NewsAPI.org
- $500+: Event Registry, NewsCatcher, Webz.io
By Use Case
- news aggregator app: NewsAPI.org or GNews
- brand monitoring: NewsCatcher or Event Registry
- AI training data: Perigon or Webz.io
- academic research: GDELT (free and deep historical data)
- side project: Currents API or Mediastack
- enterprise analytics: Event Registry or Webz.io
By Technical Requirements
- best documentation: NewsAPI.org
- best historical data: GDELT or Event Registry
- best NLP features: NewsCatcher or Perigon
- best free tier: Currents API or GDELT
- fastest response time: NewsAPI.org or Bing News
Building a News Pipeline with Proxies
if none of these APIs fully meet your needs, you can build your own news collection pipeline. use proxies to scrape RSS feeds and news sites directly:
import feedparser import requests from datetime import datetime def collect_news_from_rss(feeds, proxy_url=None): """collect news from RSS feeds with proxy support.""" proxies = {} if proxy_url: proxies = {"http": proxy_url, "https": proxy_url} articles = [] for feed_url in feeds: try: response = requests.get( feed_url, proxies=proxies, timeout=15 ) feed = feedparser.parse(response.content) for entry in feed.entries: articles.append({ "title": entry.get("title", ""), "url": entry.get("link", ""), "published": entry.get("published", ""), "summary": entry.get("summary", ""), "source": feed.feed.get("title", feed_url), }) except Exception as e: print(f"error fetching {feed_url}: {e}") return articles # example: collect from major tech news RSS feeds tech_feeds = [ "https://techcrunch.com/feed/", "https://feeds.arstechnica.com/arstechnica/index", "https://www.theverge.com/rss/index.xml", "https://feeds.feedburner.com/venturebeat/SZYF", ] articles = collect_news_from_rss( tech_feeds, proxy_url="http://user:pass@proxy.provider.com:8080", )Conclusion
the best news API depends on your specific requirements. for most developers starting out, GNews or Currents API offer the best value with reasonable free tiers. for enterprise use cases that need deep historical data and NLP features, NewsCatcher or Event Registry are worth the investment. for AI and ML applications, Perigon is purpose-built for that workflow.
if your needs go beyond what any single API offers, consider combining a news API for broad coverage with targeted RSS scraping through proxies for specific sources that matter most to your use case.
-
Best News APIs in 2026: Top Solutions Ranked and Compared
TL;DR
the top news APIs in 2026 are NewsAPI.org, The Guardian API, GDELT, and Mediastack. for real-time news scraping without API limits, combine RSS feeds with a lightweight Python crawler.news data feeds dozens of use cases: sentiment analysis, event detection, content aggregation, financial signal extraction, and competitive monitoring. the right API depends on volume requirements, geo coverage, and budget.
1. newsapi.org
the most developer-friendly entry point. free tier covers 100 requests/day. paid plans start at $449/month. covers 80,000+ sources in 50 languages.
import requests API_KEY = 'your_newsapi_key' params = {'q': 'web scraping', 'language': 'en', 'sortBy': 'publishedAt', 'apiKey': API_KEY} r = requests.get('https://newsapi.org/v2/everything', params=params) for a in r.json().get('articles', []): print(a['title'], '|', a['publishedAt'])2. newscatcher api
a news data API focused on clean, enriched, ready-to-use news data. the Newscatcher API automatically clusters similar articles to cut duplicate noise, then layers entity resolution and industry-standard tagging on top of advanced source, country, language, and topic filtering — a strong fit for media monitoring, sentiment analysis, and financial signal extraction.
3. the guardian api
completely free for non-commercial use with 500 requests/day. covers all Guardian content back to 1999.
params = {'q': 'artificial intelligence', 'api-key': 'your_key', 'show-fields': 'body', 'page-size': 10} r = requests.get('https://content.guardianapis.com/search', params=params) for item in r.json()['response']['results']: print(item['webTitle'], '|', item['webPublicationDate'])4. gdelt project
GDELT is a free, massive database of news events updated every 15 minutes. covers 100+ languages from 1979 to present. free to query via BigQuery. use BigQuery’s SQL interface to filter by actor, event type, country, and date range.
5. mediastack
affordable pricing starting at $9.99/month for 10,000 requests. covers 7,500+ sources in 50+ countries. good for startups needing more volume than free tiers allow.
6. currents api
free tier includes 600 requests/day. covers 70,000+ sources. useful for independent developers building news apps on zero budget.
when to scrape instead of using an api
APIs have limitations: they do not cover every site, they impose rate limits, and they charge per request at scale. direct scraping via RSS is often cheaper and faster for specific sources.
import feedparser feed = feedparser.parse('https://feeds.bbci.co.uk/news/technology/rss.xml') for entry in feed.entries[:5]: print(entry.title, '|', entry.link)RSS feeds are structured, machine-readable, and updated frequently. most major publishers still maintain them. see our guide on what is web scraping for broader context.
summary comparison
API free tier cost sources NewsAPI 100 req/day $449+/mo 80,000+ Newscatcher demo custom global The Guardian 500 req/day free 1 (quality) GDELT unlimited free millions Mediastack limited $9.99+/mo 7,500+ Currents 600 req/day free 70,000+ for scraping at volume, route requests through a rotating proxy. see our comparison of SOCKS5 vs HTTP proxy and what is a proxy server.
sources and further reading
related guides
-
Best Data Marketplaces and Dataset Websites in 2026
TL;DR
whether you are buying data to avoid scraping or selling data you have collected, these are the platforms worth knowing in 2026, with honest assessments of pricing, data quality, and buyer/seller experience.why use a data marketplace
scraping everything yourself costs time, infrastructure, and anti-bot bypass work. for common datasets (company firmographics, consumer demographics, financial data), buying from a marketplace is often cheaper than building the pipeline. on the sell side, marketplaces give scrapers a distribution channel without building a customer acquisition machine.
commercial data marketplaces
Snowflake Data Marketplace
the largest B2B data marketplace by revenue. strength is the zero-copy sharing model: data stays in Snowflake, you query it directly in your own account without ETL. 2,000+ listings across financial, demographic, weather, and alternative data. requires a Snowflake account (minimum $25/month on pay-as-you-go). best for: enterprise teams already in the Snowflake ecosystem.
AWS Data Exchange
Amazon’s data marketplace, integrated into S3 and AWS services. 3,500+ datasets. strong in financial data, healthcare, and satellite imagery. delivery is S3-based; you subscribe and data lands in your bucket. pricing ranges from free to $50,000+/month for premium financial feeds. best for: teams already on AWS who need automated data delivery into their pipelines.
Databricks Marketplace
newer than Snowflake but growing fast. similar zero-copy model for Delta Lake tables. strong in ML training datasets and AI-specific data products. if you are using Databricks for ML pipelines, check here before scraping training data yourself.
Datarade
a data marketplace aggregator that lists datasets from 1,000+ providers and lets you compare them. not a data host itself. search by data type, geography, update frequency, and delivery format. strong in B2B contact data, location data, and web data.
free and open datasets
Hugging Face Datasets
the default destination for ML training data in 2025-2026. 100,000+ datasets, free to download. strongest in NLP, computer vision, and tabular ML:
from datasets import load_dataset ds = load_dataset("wikipedia", "20220301.en", split="train[:1%]") print(ds[0]["text"][:500])Kaggle Datasets
130,000+ user-contributed datasets. best for: historical financial data, sports statistics, public health records, and competition datasets. API access via the
kaggleCLI makes bulk downloading easy.data.gov and equivalents
US federal datasets: 300,000+ datasets across all agencies. equivalent platforms: data.gov.uk (UK), data.gov.sg (Singapore), data.europa.eu (EU). strong in economic statistics, health data, geospatial data, and census data.
World Bank Open Data
macroeconomic and development indicators for 217 countries, 1960-present. the API is clean and well-documented. Python wrapper:
wbgapi. essential for any economic research or content involving global statistics.alternative data sources
Nasdaq Data Link (formerly Quandl)
financial and alternative data. free tier includes most economic data; premium tiers ($50-500+/month) cover equity fundamentals, options data, and alternative signals. useful for: backtesting, financial research, investment content.
Common Crawl
the largest freely available web crawl. monthly snapshots, 250-300TB of compressed data per crawl. hosted on S3 via AWS Open Data. process it with Athena, Spark, or the
cdx-toolkitPython library for targeted queries. use this if you need historical web content without scraping it yourself.selling your data
if you have built a proprietary dataset, the fastest path to revenue is Datarade (for B2B data buyers), Gumroad or Lemon Squeezy (for one-time CSV sales), or building a direct API product. niche datasets (e.g., weekly Amazon pricing data for a specific product category, daily SERP rankings for an industry) are more valuable than broad commodity datasets. see how to monetize web scraping for the full playbook.
sources and further reading
related guides
-
25+ Web Scraping Project Ideas for Beginners to Advanced (2026)
TL;DR
25+ concrete web scraping projects ranked by difficulty, with data sources, tech stack recommendations, and monetization angles. skip the toy tutorials and build something that actually produces value.how to pick the right project
the best scraping projects have three things: a data source that updates regularly, a use case that justifies the infrastructure cost, and a clear output format (CSV, API, dashboard, or product). one-shot scrapes of static data are not worth engineering. aim for pipelines that run on a schedule and compound over time.
the projects below are grouped by difficulty. beginner means you can do it with
requestsand BeautifulSoup on a single IP. intermediate requires rotating proxies or browser automation. advanced requires custom anti-bot bypass, distributed infrastructure, or real-time processing.beginner projects
1. job listing aggregator
scrape LinkedIn, Indeed, and Glassdoor for specific job titles across multiple cities. store in SQLite, detect new listings, send email alerts. stack: requests, BeautifulSoup, SQLite, smtplib. LinkedIn requires proxy rotation even at beginner scale.
2. e-commerce price tracker
track prices for a list of Amazon ASINs or Shopify product URLs. store daily snapshots, alert on drops. stack: requests, lxml, PostgreSQL. Amazon needs curl-cffi at minimum; Shopify stores are usually open.
3. real estate listing monitor
scrape Zillow, Realtor.com, or local MLS aggregators for new listings matching criteria. Zillow has heavy bot protection; start with smaller regional sites. output: Telegram notifications per new match.
4. twitter/x keyword monitor
use the Nitter frontend to scrape tweets without API costs. track brand mentions, competitor names, or industry keywords. stack: requests, Nitter, JSON storage.
5. news sentiment tracker
scrape Google News for a topic using
tbm=nws, run headlines through a sentiment model (VADER or GPT-4o-mini), chart sentiment over time. useful for financial research and brand monitoring.intermediate projects
6. serp rank tracker
track Google positions for a list of keywords and URLs. requires proxy rotation to avoid IP blocks. store daily snapshots, detect rank changes, generate weekly reports. see our web scraping fundamentals guide for the technical foundation.
7. domain expiry monitor
scrape WHOIS data for a list of domains, alert when expiry is within 30 days. resell as a service to agencies. stack: python-whois, CRON, Telegram bot.
8. linkedin company data scraper
scrape employee count, job openings, and follower growth for a list of companies. LinkedIn has aggressive bot detection; requires Playwright and residential proxies. output: weekly CSV for sales intelligence.
9. product review aggregator
pull reviews from Amazon, Trustpilot, G2, and Capterra for a product category. run through LLM to extract common complaints and feature requests. useful for competitive intelligence and product research.
10. stock news pipeline
scrape Yahoo Finance, MarketWatch, and Seeking Alpha for ticker-specific news. correlate news volume with price movement. requires fast scraping (sub-5 minute latency for breaking news). stack: async aiohttp, Redis queue, TimescaleDB.
11. hotel rate monitor
track Booking.com and Expedia rates for specific properties over a 90-day forward window. rates change dynamically; scrape 2x per day. requires residential proxies. output: price matrix by check-in date.
12. github trend monitor
scrape GitHub trending (no auth required), track stars-per-hour for new repos, alert on repos breaking 100 stars in first 24 hours. clean data source, no bot protection. great for finding emerging tools early.
advanced projects
13. amazon asin bulk scraper
scrape product details, BSR rank, pricing history, and review count for 10,000+ ASINs. Amazon uses Imperva; you need the full bypass stack. output: product intelligence database for private label sellers.
14. court records monitor
scrape PACER (federal) or state court systems for new filings on specific companies or individuals. legal intelligence service. some courts require browser automation; others have open XML feeds.
15. flight price matrix
scrape Google Flights, Kayak, or Skyscanner for origin-destination pairs across 90 days. each site requires browser automation. output: fare prediction model or cheap-flight alert service.
16. patent monitor
scrape USPTO and Google Patents for new filings by competitor companies. track patent activity as a signal for R&D direction. clean data source with reasonable rate limits.
17. social media follower tracker
track follower counts and engagement rates for competitor accounts across Instagram, TikTok, and YouTube. each requires different bypass approaches. output: weekly competitive benchmarking report.
18. real-time sports odds aggregator
scrape odds from 20+ bookmakers, compute arbitrage opportunities in real-time. latency matters here; you need async scraping with sub-second refresh cycles. stack: aiohttp, Redis, FastAPI dashboard.
19. influencer database builder
scrape Instagram and TikTok for creators in a niche by hashtag. extract follower count, engagement rate, contact info from bio. sell as a SaaS tool to agencies. requires aggressive proxy rotation and browser automation.
20. glassdoor salary database
scrape Glassdoor salary reports by role, company, and location. Glassdoor requires login for most data; needs account pooling and residential proxies. output: compensation benchmarking dataset.
monetization angles
21-25. data-as-a-service projects
any of the above can become a data product. the playbook: scrape the data, clean it, expose it via a simple API with Stripe-gated access. projects 6 (SERP tracking), 9 (review aggregation), 11 (hotel rates), and 15 (flight prices) have proven markets with existing paid competitors.
for distribution, the fastest path to revenue is selling CSV exports to small operators rather than building a full SaaS. list on Gumroad or Lemon Squeezy, generate fresh exports weekly, update the listing. no infra required beyond a cron job and file storage.
tech stack by difficulty
beginner:
requests,BeautifulSoup4,pandas, SQLite, CRON. intermediate:curl-cffi,playwright, PostgreSQL, Redis, Celery. advanced: distributed Playwright workers, residential and mobile proxy pools, Kafka or Redis Streams for real-time, ClickHouse for analytics. see SOCKS5 vs HTTP proxy for routing considerations at scale.sources and further reading
- Scrapy framework documentation
- Playwright for Python
- Real Python: web scraping practical introduction
related guides
-
Google Search URL Parameters: Complete 2026 Reference
TL;DR
Google’s search URL accepts dozens of undocumented parameters that control results, date filters, language, location, and output format. this is the working reference for 2026, sourced from reverse-engineering and official documentation.the base url structure
every Google search starts at
https://www.google.com/search. the query string carries all the search configuration. the minimum required parameter isq(the query). everything else is optional but powerful.for scraping purposes, always target
google.comwith explicitglandhlparameters rather than country-specific domains. theglparameter gives you cleaner, more predictable results and avoids regional redirect chains.core parameters
q: query
the search query. URL-encode it. spaces become
+or%20. useurllib.parse.quote_plus()in Python. advanced operators (site:,intitle:,filetype:) go insideq.num: results per page
valid values: 10 (default), 20, 30, 50, 100. setting
num=100gives you the full first page in one request, which is essential for efficient scraping. SERP quality degrades beyond position 30; positions 31-100 are often thin or duplicate content.start: pagination offset
zero-indexed.
start=0is page 1,start=10is page 2 (with default num=10). combine withnum:num=100&start=0fetches 100 results in one shot.gl: geolocation country
two-letter country code.
gl=us,gl=gb,gl=sg. for rank tracking, always fixglso results are consistent across requests.hl: interface language
hl=enforces English interface. if you omit this, Google infers language from IP location, which breaks scraping consistency when you rotate proxies across regions.lr: language restrict
restricts results to pages in a specific language. format:
lr=lang_en,lr=lang_zh-TW. different fromhl:hlcontrols the UI language,lrcontrols the language of pages returned.date and freshness parameters
tbs: time-based search
values:
tbs=qdr:h(past hour),tbs=qdr:d(past 24 hours),tbs=qdr:w(past week),tbs=qdr:m(past month),tbs=qdr:y(past year),tbs=cdr:1,cd_min:1/1/2025,cd_max:12/31/2025(custom date range). for news monitoring pipelines,tbs=qdr:hpairs withtbm=nwsfor news-specific results.result type parameters
tbm: type of search
tbm=nws– Google Newstbm=isch– Google Imagestbm=vid– Google Videostbm=shop– Google Shoppingtbm=bks– Google Books
output and format parameters
filter
filter=0disables duplicate filtering and the omitted similar results cluster. always set this when scraping for comprehensive results.filter=1(default) silently drops results Google considers duplicates.nfpr
nfpr=1disables automatic query corrections, which is critical for rank tracking exact queries.a working python scraping example
import urllib.parse from curl_cffi import requests as cffi_requests def google_serp(query, num=10, gl="us", tbs=None): params = {"q": query, "num": str(num), "gl": gl, "hl": "en", "filter": "0", "nfpr": "1"} if tbs: params["tbs"] = tbs url = "https://www.google.com/search?" + urllib.parse.urlencode(params) s = cffi_requests.Session(impersonate="chrome120") return s.get(url, headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36", "Accept-Language": "en-US,en;q=0.9"}).text html = google_serp("web scraping python", num=100, gl="us", tbs="qdr:m")parameters to avoid
some parameters seen in older guides no longer work or actively trigger bot detection.
pws=0(disable personalization) was removed in 2021.as_sitesearchworks but is slower than usingsite:insideq.complete=0has no effect on server-side responses.sources and further reading
- Google Custom Search API reference
- SerpApi Google Search parameters reference
- Moz: Google search parameters guide
related guides