Was this helpful?
How to solve Cloudflare Turnstile on Leonardo AI
Technical engineer
Introduction
Leonardo AI is a popular artificial intelligence image generation platform. To protect account login and registration processes from automated scripts, the site uses Cloudflare Turnstile. This is an invisible or interactive captcha that evaluates user behavior and the browser environment.
In this guide, we will break down how to obtain the necessary parameters, solve the captcha via the 2Captcha API, and correctly inject the token into the Leonardo AI authorization page using proven injection methods.
Task parameters
To bypass Cloudflare Turnstile on Leonardo AI, the TurnstileTaskProxyless task type is used (or TurnstileTask if your own proxies are required).
json
{
"clientKey": "YOUR_API_KEY",
"task": {
"type": "TurnstileTaskProxyless",
"websiteURL": "https://app.leonardo.ai/auth/login",
"websiteKey": "0x4AAAAAAAjpS3rLKnsHyb79",
"action": "email_continue"
}
}
How to find parameters
websiteURL
This is the full address of the page where the captcha appears. In this case, it is the login page: https://app.leonardo.ai/auth/login
websiteKey (sitekey)
This is the public Cloudflare Turnstile key.
- Open the login page and press F12 (Developer Tools).
- Go to the Elements tab.
- Press Ctrl+F and type
data-sitekeyorcf-turnstile. - Copy the attribute value. For Leonardo AI, it is
0x4AAAAAAAjpS3rLKnsHyb79.
action
Some Turnstile implementations use the action parameter to determine the context (e.g., login, registration, or continuing email input).
- On the Elements tab, find the captcha container (usually a
divwith the classcf-turnstile). - Check for the presence of the
data-actionattribute. - Alternatively, go to the Network tab, perform the action that triggers the captcha (e.g., enter an email and click Continue), and find the request to
challenges.cloudflare.com. Theactionparameter will be specified in the request body (Payload). For Leonardo AI, when continuing login via email, this value is oftenemail_continue.
Token injection (passing) methods
After receiving the token from the 2Captcha API, it must be correctly passed to the site. Depending on the site's architecture (especially if it is a React or Vue SPA), simple DOM manipulation might not be enough. Below are the main methods based on successful Turnstile bypass practices (including examples from VFS Global scripts).
1. Injection into hidden fields (DOM manipulation)
The most common method. Turnstile creates hidden fields that are submitted along with the form.
python
# For Selenium / SeleniumBase
driver.execute_script("document.getElementsByName('cf-turnstile-response')[0].value = arguments[0];", token)
driver.execute_script(f"document.getElementById('g-recaptcha-response').innerHTML = '{token}';")
driver.execute_script(f"document.getElementById('g-recaptcha-response').value = '{token}';")
2. Calling the callback function
Modern frameworks often do not read the DOM directly but wait for a callback function to be triggered, which Turnstile registers upon initialization. If the site defines a callback, it needs to be called manually.
python
# If you intercepted the callback function name (e.g., window.turnstileCallback)
driver.execute_script(f"window.turnstileCallback('{token}');")
# Or a universal search for the callback in the turnstile object (if globally available)
driver.execute_script(f"""
if (typeof turnstile !== 'undefined' && typeof turnstile.execute === 'function') {{
// Sometimes forced state update is required
}}
// Attempt to find and call the saved callback
if (typeof window.tsCallback === 'function') {{
window.tsCallback('{token}');
}}
""")
3. Unlocking the submit button
Often, the form's submit button has a disabled attribute that is only removed after the callback successfully triggers. If you are injecting the token manually, you may need to force-unlock the button.
python
# Find the button and remove the disabled attribute
el = driver.find_element(By.XPATH, "//button[@type='submit']")
driver.execute_script("arguments[0].removeAttribute('disabled')", el)
4. Verifying injection success
After injection, it is recommended to check if the Turnstile widget accepted the token by calling its built-in getResponse() method.
python
resp = driver.execute_script("return turnstile.getResponse();")
print("turnstile.getResponse() response:", resp) # Should output your token, not null
Code examples
Python + requests (Getting the token)
This code sends a task to the API and returns the ready token.
python
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
from twocaptcha import TwoCaptcha
api_key = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY')
solver = TwoCaptcha(api_key)
try:
result = solver.turnstile(
sitekey='0x4AAAAAAAjpS3rLKnsHyb79',
url='https://app.leonardo.ai/auth/login',
action='email_continue'
)
print(f"Token successfully received: {result['code']}")
except Exception as e:
print(f"An error occurred: {e}")
Python + SeleniumBase (Automation with full injection cycle)
An adapted script using undetected-chromedriver via SeleniumBase to bypass basic detection and applying all the token injection methods described above.
python
import time
import chromedriver_autoinstaller_fix
import requests
from seleniumbase import Driver
from selenium.webdriver.common.by import By
chromedriver_autoinstaller_fix.install()
agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
# Launch the driver with anti-detect settings
driver = Driver(uc=True, headless=False, proxy=False, agent=agent)
url = 'https://app.leonardo.ai/auth/login'
sitekey = "0x4AAAAAAAjpS3rLKnsHyb79"
my_key = "YOUR_2CAPTCHA_KEY"
try:
# 1. Open the page with a delay to bypass initial Cloudflare checks
driver.uc_open_with_reconnect(url, reconnect_time=20)
time.sleep(5)
# 2. Interact with the page to trigger the captcha (example for Leonardo AI)
# Enter email and click the continue button
# driver.find_element(By.ID, "email-input").send_keys("test@example.com")
# driver.find_element(By.XPATH, "//button[contains(text(), 'Continue')]").click()
time.sleep(3)
# 3. Send the task for solving
user_agent = driver.get_user_agent()
data = {
"key": my_key,
"method": "turnstile",
"sitekey": sitekey,
"json": 1,
"pageurl": url,
"useragent": user_agent,
"action": "email_continue"
}
response = requests.post("https://api.2captcha.com/in.php", data=data)
print("Sent to the service:", response.text)
task_id = response.json()["request"]
# 4. Wait for the result
time.sleep(15)
while True:
solu = requests.get(f"https://api.2captcha.com/res.php?key={my_key}&action=get&json=1&id={task_id}").json()
if solu["request"] == "CAPCHA_NOT_READY":
print("Waiting for solution...")
time.sleep(10)
elif "ERROR" in solu["request"]:
print("Error:", solu["request"])
driver.quit()
exit(0)
else:
break
token = solu["request"]
print("Token received:", token)
# 5. Token injection (Comprehensive approach)
# Method 1: Hidden fields
driver.execute_script("document.getElementsByName('cf-turnstile-response')[0].value = arguments[0];", token)
driver.execute_script(f"document.getElementById('g-recaptcha-response').innerHTML = '{token}';")
driver.execute_script(f"document.getElementById('g-recaptcha-response').value = '{token}';")
# Method 2: Attempt to call callback (if defined)
driver.execute_script(f"""
if (typeof window.turnstileCallback === 'function') {{
window.turnstileCallback('{token}');
}}
""")
# Method 3: Unlock the submit button
try:
submit_btn = driver.find_element(By.XPATH, "//button[@type='submit']")
driver.execute_script("arguments[0].removeAttribute('disabled')", submit_btn)
except:
pass
time.sleep(2)
# 6. Verify injection success
resp = driver.execute_script("return turnstile.getResponse();")
print("Checking turnstile.getResponse():", resp)
print("Token successfully injected")
time.sleep(10) # Pause for visual verification or further actions
except Exception as e:
print(f"Error: {e}")
finally:
driver.quit()
Solution accuracy reports
If the site rejected the received token, be sure to report it via the reportIncorrect method. This helps the service improve its algorithms and refunds your funds for the failed attempt. If the token successfully passed the verification, send reportCorrect.
You can submit a report via the API:
python
import requests
API_KEY = "YOUR_API_KEY"
TASK_ID = "123456789" # Replace with the real task ID from the API response
# Report for an incorrect solution
requests.post(
"https://api.2captcha.com/reportIncorrect",
json={"clientKey": API_KEY, "taskId": TASK_ID}
)
# Report for a correct solution
requests.post(
"https://api.2captcha.com/reportCorrect",
json={"clientKey": API_KEY, "taskId": TASK_ID}
)
Detailed guide on submitting reports with examples for different languages: https://2captcha.com/h/how-to-submit-reports
Common errors and solutions
| Error / Problem | Cause | Solution |
|---|---|---|
| ERROR_CAPTCHA_UNSOLVABLE | IP address is blocked by Cloudflare or an incorrect action parameter was passed. |
Use the TurnstileTask task type with high-quality residential proxies. Ensure the action parameter exactly matches what the site expects. |
| Token received, but the site does not let you in | Incorrect token injection or the token's lifespan has expired. | Use a comprehensive injection approach: hidden fields + callback invocation + button unlocking. Verify the result via turnstile.getResponse(). |
| Error finding elements | The site has updated its layout or protection mechanisms. | Check the current XPath or CSS selectors via DevTools. Increase the wait time or use explicit waits. |
| ERROR_WRONG_USER_KEY | Invalid API key. | Check that you are using the correct key from your 2Captcha dashboard. |
Useful links
- Sandbox for API testing: https://2captcha.com/setting#sandbox
- Cloudflare Turnstile documentation: https://2captcha.com/api-docs/cloudflare-turnstile
- Detailed guide on submitting reports: https://2captcha.com/h/how-to-submit-reports
- Library for Python: https://github.com/2captcha/2captcha-python