Handling CAPTCHAs in Selenium: A Practical Guide

· All articles

Selenium fills a form, clicks the button, and the page answers with a challenge instead of a dashboard. This guide is about handling that moment well: recognising which CAPTCHA the page is showing, choosing the route that fits the job, and dropping in the code that gets a real token when you need one. It is the general handling guide; the full API integration, in Python and Node, lives in our Selenium integration guide.

What Selenium runs into

A CAPTCHA appears because the site's rules decided the visit looks automated, and Selenium hands them the evidence: the navigator.webdriver flag, a user agent with no plugins, form fills and submissions faster than any human manages. The defences differ by provider but the shape is the same:

  • reCAPTCHA v2 renders a checkbox inside iframe[title*="reCAPTCHA"] and stores the answer in a hidden g-recaptcha-response field.
  • reCAPTCHA v3 has no widget at all: the page calls grecaptcha.execute() and scores the token invisibly.
  • Cloudflare Turnstile renders its own widget and stores the answer in cf-turnstile-response.
  • Interstitials — Cloudflare, Amazon WAF — replace the page you asked for with a challenge page before your form ever loads.

The practical consequence: the script needs the token the page expects, not a battle with a widget it cannot see. The reliable unit of work is the token, and there are three honest ways to get it.

When you control the application

If it is your own site, the right answer is to keep automation away from the challenge entirely: disable CAPTCHA verification in staging, use the provider's documented test keys in CI, or allow-list the automation addresses. This keeps your tests about the product, not about anti-bot defences, and it is where most Selenium suites should stop.

When you do not: read the challenge first

Working with third-party sites, the first job is knowing what you hit. The DOM tells you:

  • the data-sitekey attribute on the widget element (or on the cf-turnstile div for Turnstile) is the site key the API needs;
  • reCAPTCHA v3 puts its key in the script URL as ?render=SITEKEY and the action in the page's grecaptcha.execute call;
  • the hidden response field names above tell you which answer the form expects.

The key alone is not enough: a solver also needs the full URL of the page hosting the widget, and that has to be the URL your driver is actually on. Reading both from the page keeps the solve and the submit in the same session, which is the difference between a token the site accepts and one it quietly ignores.

The solving API, in brief

For third-party pages the working route is a solving service: the challenge details go out, a token comes back, the script puts it where the page expects it. With the official EndCaptcha client the request is one call — the client uploads and polls for you:

import json
from endcaptcha import HttpClient

client = HttpClient("YOUR_USERNAME", "YOUR_PASSWORD")

captcha = client.decode(
    type=4,
    token_params=json.dumps({
        "googlekey": "6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI",
        "pageurl": "https://www.example.com/signup",
    }),
)

token = captcha["text"]

type=4 is reCAPTCHA v2; type=12 with sitekey instead of googlekey handles Turnstile, and the two field sets are documented on the v2, v3 and Turnstile type pages. If you poll by hand, right after upload the API answers UNSOLVED_YET:/poll/<id> while the job waits, the poll answers ? while a solver works, and the token text when it is solved; poll no faster than every three seconds.

Injecting it takes one script call, and the response fields are in the main document — only the widget's visual part lives in the iframe:

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver.execute_script(
    """
    const field = document.querySelector(
        '[name="g-recaptcha-response"]');
    if (field) {
        field.value = arguments[0];
        field.dispatchEvent(new Event('input', {bubbles: true}));
        field.dispatchEvent(new Event('change', {bubbles: true}));
    }
    """,
    token,
)

WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, 'button[type="submit"]'))
).click()

The complete flow — Node variant, the pageurl and proxy rules, the fetch-based forms — is covered in the Selenium integration guide. This guide keeps to the handling decisions around it.

Keeping the session credible

Tokens come from the API; trust with the page is still built by the driver. Four habits cut down how often you meet a challenge at all:

  1. Be consistent. The same browser flags, the same user agent and the same proxy between the solve and the submit. A run that works headed and fails headless is usually a fingerprint that changed mid-flow, not an API problem.
  2. Keep the session warm. Reuse the driver's cookies across steps so the site sees one returning visitor instead of five new ones. Selenium keeps them by default within a driver; persist them if the flow restarts.
  3. Move like a script that is not a bot. Real waits instead of sleep() guesses: WebDriverWait for elements, ordinary typing into fields, scrolls before clicks. Detection looks at the shape of the interaction, and instant form fills are a shape no human has.
  4. Match the network to the task. If the driver submits through a proxy, pass the same proxy with the token parameters so the solver comes from the address that submits. Mismatched addresses are the most common cause of "the API returned a token and the site rejected it".

Troubleshooting

What you see What it usually means What to do
The widget never appears The page loads it lazily or behind a challenge page Wait for the iframe element, or solve the interstitial first
TimeoutException clicking inside the widget The click target is in the cross-origin iframe Do not click the widget; solve and inject the response field
Token accepted by the API, rejected by the page pageurl differs from driver.current_url, or the token is older than about two minutes Read the URL off the driver, solve, inject and submit in one pass
StaleElementReferenceException after injecting The form re-rendered Re-find the submit button inside the same explicit wait
Works locally, challenged in the pipeline Headless flag or user agent differs Keep the same profile flags in both places
The answer is ? for too long A solver is still working Keep polling every three seconds; the client caps it for you

Questions developers ask

Is clicking the checkbox ever enough? Not on production traffic. The widget is cross-origin and the click pattern is exactly what the check detects; the token route is the one that holds.

Do test keys work with a solving service? They prove plumbing, not solving: the services reject provider test keys, so CI can use them for form wiring while real tokens come from real keys.

How long does a real solve take? The service publishes a seven-second SLA; in practice the poll returns in seconds when the queue is free, and decode() caps the wait for you.

Can I run this with Selenium Grid? Yes — the token is produced outside the browser, so any node can inject it. Keep the pageurl on the public URL of the page, not the node's address.

Where does the rest of the integration live? The Selenium integration guide has the full Python and Node flow, and the Playwright guide covers the same solving pattern for another driver. The quick start has the cURL version, and creating an account takes a minute.

Back to all articles

Sign Up

Create Account

Already have an account? please
Login
Terms Of Services

Terms of Services

EndCaptcha Terms of Service:

EndCaptcha is a Data Entry Company that focuses on fast and reliable conversion of Images to Text and general digitization. Any use of the service for activities that are deemed illegal is strictly prohibited.

EndCaptcha is not responsible for the actions of its clients. Any complaint about any particular EndCaptcha client should be reported to [email protected]. EndCaptcha reserves the right to suspend its clients' account if, through an [email protected] report, gets hold of undeniable proof that the client is engaging in illicit activities.

EndCaptcha reserves the right to change at any time its Terms of Service, prices of packages, Service Level Agreement conditions and compensation, and Unsupported Images without prior notice.

Unsupported Activities:

EndCaptcha reserves the right to terminate any client account without notice if, by its sole judgement, EndCaptcha determines that the client is engaged in activities that negatively affect service levels. Examples of these harmful activities are:
- Using EndCaptcha's API in ways that negatively affects the operation of the service
- Constantly submitting images that are unreadable
- Exploiting in any way the Service Level Agreement compensation
- Exploiting the service function used to claim images as incorrectly solved

Ownership of an EndCaptcha account:

All accounts created in EndCaptcha remain as sole property of EndCaptcha, and its parent company. Sharing account details, selling and reselling accounts, and selling awarded coupons is prohibited. Violation of this policy will result in account suspension.

Service Level Agreement:

Even though customers may submit Unsupported Images, and EndCaptcha will do it’s best effort to solve said images in a timely fashion, the Service Level Agreement won't cover any Unsupported Images and won't be available during dates included in the Maintenance and Holidays Calendar. EndCaptcha reserves the right to include Unsupported Images at any time without previous notice.

Additional information about the types of insurances provided in the Service Level Agreement may be found here.

Refunds and Chargebacks:

Any fulfilled claims for chargebacks before consulting our helpdesk will result in an immediate suspension of the clients account and appropriate legal action (fraud).

Refunds are processed in a case-by-case basis. We will not award refunds if:
- The client is unsatisfied with the results obtained when submitting Unsupported Images.
- The client has partially used the service and has no use for remaining balance in the client's account.
- The client was unaware of any explicitly written service constraints before making a purchase.

If a refund is awarded, the client will receive funds equivalent to the remaining credit balance in the client's account.

Maintenance and Holidays Calendar

Maintenance and Holidays Calendar

Although we will try our best to keep maintenance and undercapacity occurrences to a minimum, there will be rare occasions in which these events will be unavoidable, and thus not covered by the Insurances of our Service Level Agreement. The Maintenance and Holidays Calendar will show the foreseeable events of diminished Quality of Service.

Event Description Date Duration
Unsupported Images

Unsupported Images

The following images can be solved by our team of workers, but are not insured by the Slowness Insurance. These images are usually unreadable, math problems, or have a lengthy amount of characters. The same exception applies for the Recaptcha V2 challenges. Due to their complexity, are not eligible for Slowness Insurance


Recaptcha V2 Fast Solving Service Recaptcha Invisible Decoding Solution
Refunds and Chargebacks

Refunds and Chargebacks

Any fulfilled claims for chargebacks before consulting our helpdesk will result in an immediate suspension of the clients account and appropriate legal action (fraud).

Refunds are processed in a case-by-case basis. We will not award refunds if:
- The client is unsatisfied with the results obtained when submitting Unsupported Images.
- The client has partially used the service and has no use for remaining balance in the client's account.
- The client was unaware of any explicitly written service constraints before making a purchase.

If a refund is awarded, the client will receive funds equivalent to the remaining credit balance in the client's account.

Service Level Agreement

Service Level Agreement

EndCaptcha offers a unique Service Level Agreement that compensates its users when there is a drop in Quality Standards.

About Compensation

Users will be compensated with Compensatory Credits, which will be deposited once the currently used Credit Package has been consumed. The amount of Compensatory Credits will be calculated depending on the type of Insurance and the Compensation Coverage Level of the currently used package.

Compensatory Credits, Promotional Credits, and Free Credits won't be subject to the terms of the Service Level Agreement and its insurances.

Compensation Coverage Level

The user will be given a Slowness Insurance and an Outage Insurance. These types of insurances will provide compensation to users whenever certain Quality Standards are not met. There will be different Compensation Coverage Levels depending on what Credit Package was bought. The amount of Compensatory Credits will be the result of multiplying the Compensation Coverage Level percentage with the amount of quantifiable times the Quality Standards are not met.

Slowness Insurance

*Approximately 70% of all images are decoded with-in 7-8 seconds on average. The remainder is also guaranteed to be decoded with-in 11 seconds max. For any images that take longer than 11 seconds to solve, our slowness insurance policies kick in.

The Slowness Insurance will provide compensation to EndCaptcha users for images that are getting typed in more than 11 seconds. The compensation will be calculated on an Image-by-image basis.

Example Case:
Compensation Level: 60% Coverage
Images over 11 seconds: 2,000
Compensation Credits: 1,200

The Slowness Insurance won't cover Unsupported Images, and is not applicable during the time ranges included in the Maintenance and Holidays Calendar.

Outage Insurance

The Outage Insurance will provide compensation to EndCaptcha users when the service suffers from an unexpected outage that renders the service partially or completely unresponsive. The Outage Insurance will only cover cases of registered server problems on EndCaptcha's side, it will not cover issues in the user's side or connectivity problems between the user and EndCaptcha.

The Outage Insurance will only compensate credits in accordance with the usual activity of the users during the event. The usual activity of the user will be determined by the average past submissions made by the user during the weekday and time of the outage event.

Example Case:
Compensation Level: 100% Coverage
Usual Activity During Event: 2,000 Images
Compensation Credits: 2,000 Images

The Outage Insurance will be evaluated on a case by case basis whenever the user opens an outage claim at [email protected]. For outage claims to be valid, they have to be opened with-in 72 hours of the outage occurring.

The Outage Insurance is not applicable during the Maintenance time ranges included in the Maintenance and Holidays Calendar, which have been announced a minimum of 12 hours in advance. The Outage Insurance is not applicable when deliberate attacks to our service are made.

EndCaptcha reserves the right to change its Service Level Agreement without prior notice.