Logo of «2Captcha»To home page
Captcha bypass tutorials

Was this helpful?

How to bypass Amazon WAF Captcha with Playwright

Jerry Slimane
Jerry Slimane

Technical engineer

Amazon WAF Captcha shows a task that only a person can complete: the answer is not in the page code. In web scraping and automated testing the captcha is therefore handed to a solving service, which returns ready values.

In this guide we will go through the whole path in Playwright with Python: find the captcha parameters on the page, send the task to the 2Captcha API and get the solution. Putting the solution back into the page is covered separately — that part depends on how the captcha is embedded on a particular site.

What You Will Need

  • Python 3.7 or higher
  • Installed Playwright
  • 2Captcha account and API key
  • Target page URL
  • Optional: proxy if the target site blocks requests from datacenter IPs

Step 1. Installing Dependencies

Install the libraries and the Playwright browsers:

bash Copy
pip install playwright 2captcha-python
playwright install chromium

Step 2. Browser Initialization and Page Navigation

For debugging, start with visible mode headless=False. You will see the captcha appear and the solution go in.

python Copy
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()

    # Navigate to the target page
    page.goto("https://mysite.com/page/with/amazon-captcha")

    # Wait for the captcha scripts to load
    page.wait_for_load_state("networkidle")

Step 3. Extracting the Captcha Parameters

The request needs three values from the page: websiteKey, iv and context. Every site has its own markup, so find them first and only then fix them in code.

Open the page with the captcha and the developer tools. On the Network tab filter the requests by the word awswaf — the addresses of the challenge.js and captcha.js scripts will be there. Save both, they come in useful in Step 4.

On the Elements tab search the page for the word context. The iv value and the public key are next to it. Note the element and the format the values are written in.

Then put your own selector in place of SELECTOR and your own attribute name in place of FIELD.

python Copy
element = page.locator("SELECTOR").first

if element.count() == 0:
    raise RuntimeError("Captcha parameters element not found, check the selector")

raw = element.get_attribute("FIELD")
if raw is None:
    raise RuntimeError("The element holds no captcha parameters")

page_url = page.url

The count() check is needed: if the element is missing, get_attribute will not return an empty value, it will wait until it times out. And .first is needed because a locator with two matches raises an error instead of taking the first element.

Now parse the value the way it is written on your page. If it is JSON, the example below will do; put in your own key names.

python Copy
import json

params = json.loads(raw)
website_key = params["key"]
iv = params["iv"]
context = params["context"]

The values are single-use: read them again before every request, on the same page load.

Step 4. Sending the Task to the API and Getting the Solution

The task is created by the official Python SDK. It polls the server itself until the result arrives.

python Copy
import os

from twocaptcha import TwoCaptcha

api_key = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY')

solver = TwoCaptcha(api_key)

result = solver.amazon_waf(
    sitekey=website_key,
    iv=iv,
    context=context,
    url=page_url
)

solution = result['code']
print("Solution received:", solution)

The answer holds two values:

json Copy
{
  "status": 1,
  "request": {
    "captcha_voucher": "xxxxx",
    "existing_token": "xxxxx"
  }
}

The SDK puts the contents of request into result['code'].

Alternative (direct HTTP request):
If you don't use the SDK, send the request with curl or the requests library. The field names there are different, so copying the parameters from the example above will not work. The answer comes in the solution object of the getTaskResult method, with the same value names.

bash Copy
curl -X POST https://api.2captcha.com/createTask \
  -H "Content-Type: application/json" \
  -d '{
    "clientKey": "YOUR_API_KEY",
    "task": {
      "type": "AmazonTaskProxyless",
      "websiteURL": "https://mysite.com/page/with/amazon-captcha",
      "websiteKey": "AQIDA...wZwdADFLWk7XOA==",
      "context": "qoJYgnKsc...aormh/dYYK+Y=",
      "iv": "CgAAXFFFFSAAABVk"
    }
  }'

If the site requires the captcha to be solved from a specific IP, use the AmazonTask task type and add the proxy parameters: proxyType, proxyAddress, proxyPort, proxyLogin, proxyPassword.

The parameters are convenient to check in sandbox mode. The captcha then does not go to the service workers, it comes back to you: you switch your role to worker, log into the application with the key for that role and solve your own captcha yourself. Nothing is charged for this. The mode is switched on in the account settings, and it has to be switched off once you are done.

Step 5. Injecting the Solution into the Page

With most captchas the answer goes into one known field. Here there are two values, and where exactly to put them depends on the site. You can find this out in a single manual pass.

  1. Open the page with the captcha and the developer tools.
  2. On the Network tab turn on preserving the log so the records are not lost on navigation.
  3. Complete the captcha by hand.
  4. Find the request the page sent right after that. Both values will be in its body, its headers or its cookies.

Then repeat this in the script. The solution variable below is what the API returned in Step 4.

If the values go in form fields, write them like this. The solution is passed as a function argument rather than by string concatenation, so it reaches the page exactly as it is, with any characters in it.

python Copy
page.evaluate("""(solution) => {
    const voucher = document.querySelector('SELECTOR_VOUCHER');
    const token = document.querySelector('SELECTOR_TOKEN');
    if (!voucher || !token) {
        throw new Error('Fields for the captcha solution were not found on the page');
    }
    voucher.value = solution.captcha_voucher;
    token.value = solution.existing_token;
}""", solution)

If the values go in cookies, set the domain and the path explicitly. If you pass the page address instead, the cookie is tied to that address and will not work on other pages of the site.

python Copy
page.context.add_cookies([
    {"name": "COOKIE_VOUCHER", "value": solution["captcha_voucher"],
     "domain": "mysite.com", "path": "/"},
    {"name": "COOKIE_TOKEN", "value": solution["existing_token"],
     "domain": "mysite.com", "path": "/"},
])

The check in the first example is needed: without it the script will submit the form with no solution and report that everything went fine.

Step 6. Submitting the Form

Once the solution is in place, the form can be submitted.

python Copy
page.click("button[type='submit']")
page.wait_for_load_state("networkidle")

Put in your own button selector: button[type='submit'] does not fit every form.

The result is worth reporting to the service. The SDK has the report method for this, the API has reportCorrect and reportIncorrect. The task identifier is in the same response as the solution: solver.report(result['captchaId'], True).

Complete Working Example (Python)

The script goes as far as receiving the solution. Add the injection from Step 5 once you have found the fields on your page.

python Copy
import json
import os

from playwright.sync_api import sync_playwright
from twocaptcha import TwoCaptcha

API_KEY = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY')
TARGET_URL = "https://mysite.com/page/with/amazon-captcha"


def solve_amazon_waf():
    solver = TwoCaptcha(API_KEY)

    with sync_playwright() as p:
        browser = p.chromium.launch(headless=False)
        page = browser.new_page()

        try:
            page.goto(TARGET_URL)
            page.wait_for_load_state("networkidle")

            # 1. Read the captcha parameters from the page
            element = page.locator("SELECTOR").first

            if element.count() == 0:
                raise RuntimeError("Captcha parameters element not found, check the selector")

            raw = element.get_attribute("FIELD")
            if raw is None:
                raise RuntimeError("The element holds no captcha parameters")

            params = json.loads(raw)
            website_key = params["key"]
            iv = params["iv"]
            context = params["context"]

            page_url = page.url
            print("Parameters received")

            # 2. Send the task to the API
            print("Sending the task to the API...")
            result = solver.amazon_waf(
                sitekey=website_key,
                iv=iv,
                context=context,
                url=page_url
            )
            solution = result['code']
            print("Solution received:", solution)

            # 3. Put the code from Step 5 here, for the fields on your page

            # 4. Submit the form
            page.click("button[type='submit']")
            page.wait_for_load_state("networkidle")

            print("Form submitted.")
            page.wait_for_timeout(3000)  # Pause for visual verification

            # 5. Report the result. True if the site accepted the solution, otherwise False
            solver.report(result['captchaId'], True)

        except Exception as e:
            print(f"Error: {e}")
        finally:
            browser.close()


if __name__ == "__main__":
    solve_amazon_waf()

Troubleshooting Common Issues

  • The parameters are not found or come back empty. The captcha scripts have not run yet. Wait for them to load as shown in Step 2 and check the selector.
  • The task is created but no solution arrives. The values have expired: read them again before every request. Reload the page and send the task again.
  • The form does not accept the solution. Check that both values are in place and go where your page sends them. The method from Step 5 will help you find the right place.
  • The script does not find the captcha element. The captcha may load inside an iframe. The parameters from Step 3 are then inside it too: reach them through page.frame_locator.

If the API returned an error code, its description is on the Error Codes page, linked at the end.

Conclusion

The hard part of Amazon WAF Captcha is that the websiteKey, iv and context parameters are single-use. Read them right before creating the task instead of storing them between runs.

The task itself is solved by a service worker, and your code receives two ready values — captcha_voucher and existing_token. Two actions are left to your code: find the parameters on the page and put the solution back. How exactly to do that depends on how the captcha is embedded on the site.

Send your reports through reportCorrect and reportIncorrect: they let the service tell which solutions did not work.

The full parameter reference is on the documentation page: https://2captcha.com/api-docs/amazon-aws-waf-captcha