🎉 Premium Proxies · 3-Day Free TrialClaim Now →
Guides

How to Bypass reCAPTCHA When Web Scraping

How to bypass reCAPTCHA when web scraping the ethical way: how v2 and v3 scoring work, how to raise your reCAPTCHA score, and solvers as a last resort.

S SparkProxy 2 19 min read
Share
How to Bypass reCAPTCHA When Web Scraping

If you want to bypass reCAPTCHA when web scraping, start by understanding what you're actually up against, because reCAPTCHA is not one thing. reCAPTCHA v2 shows a checkbox and sometimes an image grid. reCAPTCHA v3 shows nothing at all and quietly scores every visitor from 0.0 to 1.0. The two need different tactics, and for one of them there is nothing to click. This guide is the honest version: raise your score so the challenge rarely fires, treat solver services as a last resort, and stay on the right side of a site's terms and the law by scraping public data only.

A note before the code. "Bypass" here means getting your legitimate scraper past a challenge that fires on public pages, not breaking into anything gated behind a login you aren't authorized to use. Respect robots.txt and terms of service, and when a site offers an official API, use it.

How reCAPTCHA Actually Works

Every version of reCAPTCHA does the same job: it collects signals in the browser, sends them to Google, and returns a token that the site's backend verifies. What differs is how visible the process is and what the token represents.

reCAPTCHA v2 is the "I'm not a robot" checkbox. The page embeds a widget with a data-sitekey. When you interact, Google runs a risk analysis and, if it isn't confident, escalates to an image grid ("select all traffic lights"). Pass it and the widget writes a token into a hidden field named g-recaptcha-response. The site submits that token with the form, then its server calls https://www.google.com/recaptcha/api/siteverify with a secret key. For v2 the verify response is essentially pass or fail.

reCAPTCHA v3 removes the interaction entirely. The page loads api.js?render=SITEKEY, and JavaScript calls grecaptcha.execute(sitekey, {action: 'login'}) to mint a token on page load or on a specific action. There's no puzzle. The server-side siteverify call returns a JSON object with a score between 0.0 (almost certainly a bot) and 1.0 (almost certainly human), plus the action name. Google recommends a default threshold around 0.5, but the site owner picks the number and what to do below it: block, throttle, or show a v2 challenge as a fallback.

There's a third flavor worth naming: reCAPTCHA Enterprise. It's the v3 scoring model with more signals and a different verify endpoint. From a scraper's seat it behaves like v3, so the v3 advice below applies.

The mechanical takeaway is the thing to internalize. With v2 you can, in principle, produce a valid token. With v3 there is no token to "solve" in any meaningful sense, because the token only encodes a score that was already decided by how trustworthy your browser session looked. That single fact decides your whole strategy.


reCAPTCHA v2 vs v3: The Table That Matters

AspectreCAPTCHA v2reCAPTCHA v3
InteractionCheckbox, then image grid if suspiciousNone, runs invisibly on load or on an action
Site key in HTML`data-sitekey` on a `.g-recaptcha` div`?render=SITEKEY` on the `api.js` script tag
Token field`g-recaptcha-response` hidden textareaReturned by `grecaptcha.execute()`, sent by the site
What the token meansYou passed the challengeYour session scored X on 0.0 to 1.0
Where the gate livesThe client widget you must clearThe site's server-side score threshold
What blocks youFailing the image challengeScore below the threshold (often around 0.5)
Your real leverPass, or farm a valid tokenRaise the score; there's nothing to click
Solver feasibilityReasonable (services return a token)Weak (score depends on session trust, not a click)
Token lifetime~120 seconds, single use~120 seconds, single use

Read the "Your real lever" row twice. For v2 a solver service has something concrete to do: clear the challenge and hand back a token. For v3 a solver can request a token, but the score baked into it still reflects the IP and browser that requested it. Pay a service to fetch a v3 token from a datacenter IP and you'll often get a token that carries a losing score. This is why v3 rewards prevention and punishes brute force.


Free trial

Scraping at scale? Skip the blocks.

Fast, unblockable datacentre proxies with unlimited bandwidth.

Why Your Scraper Gets a Low reCAPTCHA Score

reCAPTCHA reads dozens of signals. You can't see the exact model, but the inputs that move a scraper's score are well understood, and they're the same inputs that trip risk analysis on v2.

Signal reCAPTCHA readsBot-like valueWhy it costs you
IP reputationDatacenter ASN (AWS, GCP, cheap VPS)Cloud ranges are pre-flagged; you start low before a single header
Browser automation flags`navigator.webdriver`, headless Chrome, SwiftShader WebGLDirect tells that a framework, not a person, is driving
Google cookies / sessionEmpty cookie jar, no Google accountreCAPTCHA weights an existing, aged Google session heavily
Interaction historyNo mouse movement, instant navigation, no scrollv3 literally scores behavior on the page
Fingerprint coherenceChrome UA over a Python TLS handshakeA contradiction the model reads as spoofing

The pattern is consistent: a request from a cloud server, driven by an unpatched headless browser, with no cookies and no behavior, is the textbook low-score profile. That's why the same Playwright script that scores fine from your laptop scores near 0.1 from a datacenter. You didn't change the code. You changed the trust of the session.

For the full anti-detection stack across TLS, headers, and browser tells, our guide on how to avoid getting your proxy blocked goes layer by layer. Below, the focus stays narrow: the signals reCAPTCHA specifically rewards.


Prevention First: Raise the Score

For v3 this isn't the polite option, it's the only reliable one. For v2 it decides whether you get a one-click checkbox or a nine-tile image grind. Three levers do most of the work.

Use clean residential IPs. IP reputation is the biggest single input, and it's the one you can change instantly. Datacenter subnets are classified and abused, so they start you near the floor. Residential and mobile IPs are assigned to real households and carriers, so they carry real trust. If you're new to the category, what is a residential proxy explains the types and sourcing.

Drive a real browser and patch the obvious tells. For anything running reCAPTCHA v3 or a v2 that escalates, an HTTP client won't cut it, because there's no JavaScript to run grecaptcha.execute. Use Playwright or undetected-chromedriver, run headful when you can (headless scores lower), and add human motion before you read the page.

from playwright.sync_api import sync_playwright

def fetch_with_behavior(url: str, proxy: str) -> str:
    """Real browser + residential proxy + small human signals reCAPTCHA v3 rewards."""
    with sync_playwright() as p:
        browser = p.chromium.launch(
            headless=False,               # headful scores higher than headless
            proxy={"server": proxy},      # residential exit, not a datacenter IP
        )
        ctx = browser.new_context(
            locale="en-US",
            timezone_id="America/New_York",
        )
        page = ctx.new_page()
        page.goto(url, wait_until="networkidle")
        page.mouse.move(220, 300)         # movement, not teleporting clicks
        page.wait_for_timeout(1200)
        page.mouse.wheel(0, 800)          # a scroll v3 can observe
        html = page.content()
        browser.close()
        return html

Keep the fingerprint coherent. A residential IP with a Python TLS fingerprint is still a contradiction. If you must use an HTTP client for a v2-only page (no v3 scoring), curl_cffi impersonates a real browser's TLS and HTTP/2 stack so the network layer stops screaming "bot."

from curl_cffi import requests as cffi

resp = cffi.get(
    "https://www.sparkproxy.io",
    impersonate="chrome124",  # real Chrome JA3/JA4 + HTTP/2 + header order
    proxies={"https": "http://user:pass@residential.sparkproxy.io:10000"},
    timeout=15,
)

Do these three things and reCAPTCHA v3 tends to wave you through, and v2 tends to stay a single checkbox instead of an image grid. Prevention is the whole game; solving is the exception you reach for when a site deliberately gates a high-value action.


The Google Session Signal Most Guides Miss

Here's the reCAPTCHA-specific detail that generic anti-bot posts skip, including a broader companion piece on how to avoid CAPTCHAs when web scraping: reCAPTCHA is a Google product, and it reads your Google session.

A browser that carries cookies for google.com, especially one signed into an aged Google account, scores meaningfully higher than a cookieless headless profile hitting the page cold. Google can tie the reCAPTCHA request to an existing, established identity, and an established identity is the hardest thing for a bot farm to fake at scale. This is why a fresh, empty browser context is a self-inflicted low score on v3.

You don't need to log in with real credentials to benefit from the principle. What matters is that the browser profile is persistent and warmed, not spun up empty for every request. Reuse a browser context across requests, let it accumulate cookies, and land on a neutral page before the target so the session has a history. Throwing away the profile after every fetch means you re-earn trust from zero every single time, which is the opposite of what you want on a scored challenge.

The practical rule: persist the browser profile directory, keep the same IP bound to that profile for its lifetime, and rotate the whole bundle (profile plus IP plus fingerprint) together rather than shuffling one piece at a time.


Detecting reCAPTCHA in a Response

Before you can react, you have to know which version fired and grab the site key. A 200 OK that contains a reCAPTCHA widget is a soft block, not a success, so check the body, not just the status code.

import re

def detect_recaptcha(html: str):
    """Return (version, sitekey) so you know exactly what you're facing."""
    sitekey = None
    m = re.search(r'data-sitekey="([^"]+)"', html)
    if m:
        sitekey = m.group(1)
    else:
        m = re.search(r'recaptcha/api\.js\?render=([\w-]+)', html)
        if m:
            sitekey = m.group(1)

    if "grecaptcha.execute" in html or "api.js?render=" in html:
        return "v3", sitekey          # invisible, score-based
    if "g-recaptcha" in html or "data-sitekey" in html:
        return "v2", sitekey          # interactive checkbox
    return None, sitekey

version, sitekey = detect_recaptcha(page.content())
if version:
    print(f"reCAPTCHA {version} present, sitekey={sitekey}")

The sitekey is the public identifier a solver service needs, so pulling it reliably is step zero for both the API route and the solver route below.


Let the Scraping API Handle It

Running your own residential pool, patched browsers, warmed profiles, and retry logic is a real, ongoing engineering commitment, because the anti-bot side ships updates constantly. A scraping API folds the whole prevention stack into one endpoint, so you raise your score with flags instead of maintaining infrastructure.

The SparkProxy Scraping API handles proxies, JS rendering, and challenge handling for you. Two flags do the heavy lifting on reCAPTCHA score: premium_proxy=true routes through residential IPs, and stealth=true adds a homepage pre-warm, a forced referrer, and human-like delays. Add render_js=true so grecaptcha.execute actually runs on v3 pages.

curl -H "X-API-Key: YOUR_API_KEY" \
  "https://scrape.sparkproxy.io/api/v1?url=https://www.sparkproxy.io&render_js=true&premium_proxy=true&stealth=true&country_code=US"

The same request in Python, asking for a JSON envelope so you can read the metadata:

import requests

resp = requests.get(
    "https://scrape.sparkproxy.io/api/v1",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={
        "url": "https://www.sparkproxy.io/pricing",
        "render_js": "true",
        "premium_proxy": "true",   # residential exit, high trust
        "stealth": "true",         # pre-warm + referrer + human delays
        "country_code": "US",
        "json_response": "true",   # wrap body + metadata
    },
    timeout=60,
)
data = resp.json()
print(data.get("status_code"))

When a challenge does fire, the API surfaces which system it was so you can react instead of guessing. On a CAPTCHA failure the response carries a captcha_type field, set to recaptcha, hcaptcha, cloudflare_turnstile, or null for a purely behavioral block.

if data.get("captcha_type") == "recaptcha":
    # retry with stealth on and a fresh residential exit before touching a solver
    print("reCAPTCHA fired, rotating exit and retrying")

Whether to build this or buy it is a genuine cost decision on both sides. We lay out the tradeoffs in web scraping API vs self-managed proxies. On reCAPTCHA-heavy targets the API usually wins once you count the maintenance hours a raw proxy bill hides.


Last Resort: Solver Services and the Token Flow

Some sites put a deliberate interactive reCAPTCHA v2 on a high-value gate that every visitor sees regardless of reputation. That's a policy choice, not a score problem, and no amount of clean signal removes it. This is the narrow case where a solver service earns its place. Two caveats up front: using one may breach the site's terms of service, and success is never guaranteed.

The mechanics are the same across 2Captcha, Anti-Captcha, and similar services. You send the site key and the page URL, a worker (human or automated) produces a token, and you inject that token where the page expects it. Here's the v2 flow against 2Captcha's documented API.

import requests
import time

API_KEY = "YOUR_2CAPTCHA_KEY"

def solve_recaptcha_v2(sitekey: str, page_url: str) -> str:
    # 1. Submit the job: the service needs the sitekey and the page URL.
    submit = requests.post("https://2captcha.com/in.php", data={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": page_url,
        "json": 1,
    }, timeout=30).json()
    job_id = submit["request"]

    # 2. Poll until a worker returns the token (usually 10 to 30 seconds).
    for _ in range(24):
        time.sleep(5)
        res = requests.get("https://2captcha.com/res.php", params={
            "key": API_KEY, "action": "get", "id": job_id, "json": 1,
        }, timeout=30).json()
        if res["status"] == 1:
            return res["request"]              # the g-recaptcha-response token
        if res["request"] != "CAPCHA_NOT_READY":
            raise RuntimeError(res["request"])  # bad sitekey, no balance, etc.
    raise TimeoutError("solver timed out")

The token is worthless until you place it where the site reads it. For v2 that's the hidden g-recaptcha-response field, then you submit the form the site expects.

token = solve_recaptcha_v2(sitekey, "https://www.sparkproxy.io/protected")
page.evaluate(
    "(t) => { document.getElementById('g-recaptcha-response').value = t; }",
    token,
)
page.click("button[type=submit]")   # trigger the real form submission

For reCAPTCHA v3 the same service call takes three extra fields: version=v3, the exact action label the page uses, and a min_score you're willing to accept.

submit = requests.post("https://2captcha.com/in.php", data={
    "key": API_KEY,
    "method": "userrecaptcha",
    "version": "v3",
    "action": "verify",     # must match the site's grecaptcha.execute action
    "min_score": 0.3,       # ask for at least this score
    "googlekey": sitekey,
    "pageurl": page_url,
    "json": 1,
}, timeout=30).json()

Two hard limits kill more solver integrations than anything else. First, reCAPTCHA tokens are single-use and expire in about 120 seconds, so a token you fetch and sit on is already dead. Fetch, inject, and submit in one tight window. Second, v3 tokens carry the score from whatever session requested them, so a cheap solver on a bad IP hands you a token that fails the site's threshold anyway. The solver rides on top of prevention; it doesn't replace it.

If per-solve fees stack up at volume, building your own model becomes the cheaper path. We walk through that end to end in how to build a CAPTCHA solver with machine learning. It's a real project with real tradeoffs, so treat it as the destination, not the first stop.


Ethics, Legality, and Why There Are No Guarantees

This part isn't boilerplate. It shapes what you should and shouldn't automate.

Scrape public data, not gated accounts. Getting a scraper past a challenge on a public product page is a different act from defeating a challenge that guards a login you have no right to. Bypassing authentication or access controls can cross legal lines under laws like the US Computer Fraud and Abuse Act, and courts have split on the details. Public, unauthenticated data is the defensible ground. Data behind a login you don't own is not.

Respect robots.txt and terms of service. A reCAPTCHA is often a site telling you it doesn't want automated access on that path. That's not a legal wall by itself, but ignoring it, especially on a service whose terms forbid scraping, is a risk you take on knowingly. Solver services frequently violate those terms outright.

There are no guarantees, by design. reCAPTCHA is an adversarial, constantly updated system. Site owners tune thresholds, add signals, and swap v3 for Enterprise without notice. Any specific tactic, including the ones here, can stop working the week after Google ships an update. Anyone promising a permanent "bypass" is selling something.

Prefer the official door. If a site has a public API or a data license, that's faster, more stable, and unambiguous. Reserve scraping for cases where no official access exists and the data is genuinely public.

Handled this way, "bypass" means keeping a legitimate collector running on public data, not breaking into anything. That framing keeps you technically effective and out of trouble.


A Decision Checklist

Work top to bottom before you reach for a solver:

  • Confirm the data is public and you're not defeating a login or access control.
  • Identify the version with the detector: v3 means raise the score, v2 means you may pass a checkbox.
  • Move off datacenter IPs to residential exits; match country, locale, and timezone.
  • Drive a real, patched browser (headful when possible) and add human motion before reading the page.
  • Persist and warm the browser profile so a Google session and cookies accumulate; rotate profile, IP, and fingerprint together.
  • Treat a 200 that contains a reCAPTCHA widget as a failure, not a success.
  • Only after all of that, and only on a deliberate interactive gate, consider a solver, and inject the token within the ~120-second window.

Most reCAPTCHA problems die at the first four lines. If yours doesn't, the version detector tells you whether solving is even feasible.


Frequently asked questions

FAQ

You can usually get past it, but "bypass" is the wrong mental model for v3. reCAPTCHA v3 scores your session invisibly, so the goal is to raise that score with residential IPs, a real browser, and human behavior until no challenge blocks you. reCAPTCHA v2 can sometimes be passed with a solver-returned token. Neither is guaranteed, since Google updates the system continuously.

You don't "solve" v3 the way you solve a puzzle, because there's no puzzle. The token only encodes a score decided by how trustworthy your browser session looked. Raise the score with a clean residential IP, a headful patched browser, a warmed profile with Google cookies, and real interaction. Solver services can fetch a v3 token, but it carries the score of whatever IP requested it, so a bad session still fails.

v2 is interactive: a checkbox that can escalate to an image grid, and passing it writes a token into the g-recaptcha-response field. v3 is invisible and returns a score from 0.0 to 1.0 with no interaction, and the site's server decides what score to block. v2 gives a solver something to click; v3 gives it only a score to inherit.

reCAPTCHA v3 scores range from 0.0 (bot) to 1.0 (human), and Google suggests a default threshold near 0.5, though each site sets its own. A datacenter IP with a cookieless headless browser often lands near 0.1 to 0.3, below most thresholds. Clean residential IPs, a real warmed browser, and human behavior are what push the score above the line.

The services themselves operate openly, but using them to access a site can breach that site's terms of service, and defeating a challenge that guards a login or private data can raise legal exposure under laws like the CFAA. Scraping genuinely public data is far more defensible than automating past access controls. Check the target's terms and prefer an official API when one exists.

It manages residential proxies, JS rendering, and challenge handling so most reCAPTCHA-gated public pages come back solved without extra code. Set premium_proxy=true and stealth=true to raise your score, and render_js=true so v3's JavaScript runs. If a challenge still fires, the response includes a captcha_type field set to recaptcha so you can rotate the exit and retry.


Limited-time · 50% off

Get 50% off your first purchase

Premium datacentre proxies with unlimited bandwidth. Use the code at checkout.

Offer ends soon — claim it before it's gone

Claim Discount

About the Author

SparkProxy Technical Team. The SparkProxy engineering team builds and operates global datacenter, residential, and mobile proxy networks plus the SparkProxy Scraping API. This guide reflects behavior observed against reCAPTCHA v2, v3, and Enterprise, validated with Python 3.11+, Playwright 1.4x, curl_cffi 0.7+, and the SparkProxy Scraping API (July 2026). It is educational, covers public-data collection only, and is not legal advice.

References: Google reCAPTCHA v3 documentation · Google reCAPTCHA v2 documentation · SparkProxy Scraping API docs

Keep reading

Related articles

How to Scrape Airbnb Listings and Prices

How to Scrape Airbnb Listings and Prices

Learn how to scrape Airbnb listings and prices: extract fields from Airbnb's embedded JSON, handle date-based pricing, map pagination, and anti-bot defenses.

SparkProxy·Guides
How to Scrape GraphQL APIs

How to Scrape GraphQL APIs

Learn to scrape GraphQL API data: find the /graphql endpoint, read the query and variables in DevTools, then replay your own queries with cursor pagination.

SparkProxy·Guides