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

Was this helpful?

How to bypass Cloudflare Turnstile on iFood

Gregory Fisher
Gregory Fisher

Technical engineer

The iFood website (ifood.com.br) uses Cloudflare Turnstile protection to secure login and registration forms. The main feature of this implementation is that the pagedata, data, action, and userAgent parameters are strictly dynamic and change on every page load or interaction attempt.

Using outdated or manually copied parameters will result in an error. The only reliable way to obtain current data for API v2 is to inject a special interception script into the browser console strictly before the captcha loads.

Task Parameters (API v2)

To send a task via the modern API v2, the createTask endpoint is used. The task object must contain the following parameters in strict accordance with the documentation:

Parameter Type Required Description
type String Yes Task type: TurnstileTaskProxyless or TurnstileTask
websiteURL String Yes The full URL of the target web page where the captcha is loaded. We do not open the page, not a problem if it is available only for authenticated users.
websiteKey String Yes Turnstile sitekey. Can be found inside the data-sitekey property of the Turnstile div element.
action String No* Required for Cloudflare Challenge pages. The value of the action parameter of the turnstile.render call.
data String No* Required for Cloudflare Challenge pages. The value of the cData parameter of the turnstile.render call.
pagedata String No* Required for Cloudflare Challenge pages. The value of the chlPageData parameter of the turnstile.render call.
userAgent String No Browser User-Agent. Must match byte-for-byte with the one used in the request.
  • For the iFood website, these parameters are mandatory as Cloudflare Challenge mode is used.

How to Intercept Dynamic Parameters

IMPORTANT: The interception script must be injected into the browser console BEFORE the captcha loads. If you run it after the widget has already appeared on screen, it will not work because the render method will have already been called and the parameters will be missed.

These parameters change with each page load. To get them, you need to inject a special interception script into the browser console (DevTools) immediately after opening the page, before any actions that might trigger the captcha.

Instructions:

  1. Open the iFood page (e.g., login form) in incognito mode and press F12 (Developer Tools).
  2. Go to the Console tab.
  3. Immediately paste the following script and press Enter. Do not refresh the page after this!
javascript Copy
const i = setInterval(()=>{
    if (window.turnstile) {
        clearInterval(i)
        window.turnstile.render = (a, b) => {
            let p = {
                type: "TurnstileTaskProxyless",
                websiteKey: b.sitekey,
                websiteURL: window.location.href,
                data: b.cData,
                pagedata: b.chlPageData,
                action: b.action,
                userAgent: navigator.userAgent
            }
            console.log(JSON.stringify(p))
            window.tsCallback = b.callback
            return 'foo'
        }
    }
}, 10)
  1. A JSON object with the current parameters will instantly appear in the console, fully ready to be pasted into the task field of the API v2 request. Copy it.
  2. Immediately send these parameters in the solving request via the API. Do not refresh the page, otherwise the parameters will become invalid.

Note that the script not only extracts the parameters but also saves the original callback of the widget into the global variable window.tsCallback. This is the function through which the received token must be passed.

Code Examples

Python + requests (API v2)

Example of sending a task with parameters copied from the console via the createTask and getTaskResult endpoints.

python Copy
import requests
import time
import json

API_KEY = 'YOUR_API_KEY'

# Paste here the JSON copied from the browser console (this will be the content of the task object)
captured_task = {
    "type": "TurnstileTaskProxyless",
    "websiteKey": "0x4AAAAAAADnPIDROrmt1Wwj",
    "websiteURL": "https://www.ifood.com.br/",
    "data": "a38db309fda75f1e",
    "pagedata": "oADARYQ6hLArqZoXAcJmO6SSgMjvU6JRVlBH5JUWCO0-1789035619-1.3.1.1-WNWYbaa9ayfIbODYlcw3BPl4m7mQdq98uS8D9vzDTVx0MgPQ67fqIoYfAZzp4xCEcGvu15C4xK4I6nr0z1epc4ghvP8CxUfpV4TrvegIcmQmn28GUWqeKg53o5yOUem0RF7Q0fqhZmCc8yk9tLWCDsEturQiDhSPG3MHc82qPTC2ONlTfeylYMHoM2urkfeCvbTPsbDltGeSU1F23Nu2uBL94LfHOkmLDNAoEf2YsYxlio7SArdaDjDYmDV3uplatKN59hXrr6GpQli7lWD2t.9TWkgL0e8n7hjPxVwbdGzKlpNyqYsr2scsdkXY_n0Hm7QPbvv7A5jLxjuhdLOJLeOhpT_D1jea35Er7z7IMAOqCipyceThlWFyiKTFcwWNe1c0uxr.XvaQtDcIHJCzrEcxSZPBk1k4pn_qfbH5cant4yh1FORgiOSEbm4v3dRUo0q8WZhIxAofnsOkgCEEWYfWkDDQMzVEvoepJOvmW9OIfSg6F51XjBt1bVOSVwvz",
    "action": "managed",
    "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36"
}

# 1. Create task via API v2
url = 'https://api.2captcha.com/createTask'
headers = {
    'Content-Type': 'application/json'
}
payload = {
    "clientKey": API_KEY,
    "task": captured_task
}

response = requests.post(url, headers=headers, json=payload)
result = response.json()

if result.get('errorId') == 0:
    task_id = result.get('taskId')
    print(f"Task created, ID: {task_id}")
    
    # 2. Get result via API v2
    res_url = 'https://api.2captcha.com/getTaskResult'
    while True:
        time.sleep(5)
        
        res_payload = {
            "clientKey": API_KEY,
            "taskId": task_id
        }
        
        res_response = requests.post(res_url, headers=headers, json=res_payload).json()
        
        if res_response.get('errorId') == 0 and res_response.get('status') == 'ready':
            print("Captcha solved successfully!")
            token = res_response['solution']['token']
            print(f"Token: {token}")
            break
        elif res_response.get('errorId') != 0:
            print(f"Error: {res_response.get('errorDescription')}")
            break
        else:
            # Status processing - task is not ready yet
            continue
else:
    print(f"Task creation error: {result.get('errorDescription')}")

Python + Playwright (Automatic Interception)

This script automatically injects the interception code immediately after the page loads, BEFORE the captcha appears, captures the console output, and sends the task to API v2 without manual copying.

python Copy
from playwright.sync_api import sync_playwright
import requests
import time
import json

API_KEY = 'YOUR_API_KEY'
TARGET_URL = 'https://www.ifood.com.br/'

def solve_ifood_turnstile():
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=False)
        context = browser.new_context()
        page = context.new_page()
        
        captured_task = {}

        # Listen to console messages to intercept data from our script
        page.on("console", lambda msg: captured_task.update(json.loads(msg.text)) if msg.text.startswith('{"type":') else None)

        try:
            page.goto(TARGET_URL)
            
            # 1. IMPORTANT: Inject interception script IMMEDIATELY after page load, BEFORE captcha appears
            page.evaluate("""
                const i = setInterval(()=>{
                    if (window.turnstile) {
                        clearInterval(i)
                        window.turnstile.render = (a, b) => {
                            let p = {
                                type: "TurnstileTaskProxyless",
                                websiteKey: b.sitekey,
                                websiteURL: window.location.href,
                                data: b.cData,
                                pagedata: b.chlPageData,
                                action: b.action,
                                userAgent: navigator.userAgent
                            }
                            console.log(JSON.stringify(p))
                            window.tsCallback = b.callback
                            return 'foo'
                        }
                    }
                }, 10)
            """)
            
            # 2. Only after injecting the script, perform the action that triggers the captcha
            # page.fill('input[name="phone"]', '11999999999')
            # page.click('button[type="submit"]')
            
            # Wait for data to appear in console
            for _ in range(20):
                if captured_task:
                    break
                time.sleep(1)
                
            if not captured_task:
                raise Exception("Failed to intercept captcha parameters. Check action selectors.")
            
            print("Parameters intercepted. Sending to API v2...")
            
            # 3. Send to API v2 via createTask
            api_url = 'https://api.2captcha.com/createTask'
            headers = {'Content-Type': 'application/json'}
            payload = {
                "clientKey": API_KEY,
                "task": captured_task
            }
            
            task_response = requests.post(api_url, headers=headers, json=payload).json()
            if task_response.get('errorId') != 0:
                raise Exception(f"Task creation error: {task_response.get('errorDescription')}")
                
            task_id = task_response.get('taskId')
            
            # 4. Wait for solution via getTaskResult
            res_url = 'https://api.2captcha.com/getTaskResult'
            token = None
            for _ in range(20):
                time.sleep(5)
                res = requests.post(res_url, headers=headers, json={"clientKey": API_KEY, "taskId": task_id}).json()
                if res.get('status') == 'ready':
                    token = res['solution']['token']
                    break
                if res.get('errorId') != 0:
                    print(f"API error: {res.get('errorDescription')}")
                    break
                    
            if token:
                print(f"Token received: {token}")
                
                # 5. IMPORTANT: Pass the token ONLY through the callback
                page.evaluate(f"""
                    if (typeof window.tsCallback === 'function') {{
                        window.tsCallback("{token}");
                        console.log("Token passed via tsCallback successfully");
                    }} else {{
                        console.error("tsCallback function not found.");
                    }}
                """)
                print("Token passed via callback.")
            else:
                print("Failed to get token.")
                
        except Exception as e:
            print(f"Error: {e}")
        finally:
            # browser.close()

if __name__ == '__main__':
    solve_ifood_turnstile()

How to use the received token (Only via callback)

The iFood website accepts the Cloudflare Turnstile token exclusively through the callback function. Injecting the token into hidden form fields may not work because the site expects a function call.

The interception script we inject into the console automatically saves the original callback of the widget into the global variable window.tsCallback. After receiving the token from the API, you need to call this function, passing the token as an argument:

window.tsCallback("RECEIVED_TOKEN");

This call is what tells the site that the captcha has been successfully solved and unlocks the form for submission.

Testing in Sandbox Mode

Before running automation, verify the correctness of the extracted parameters in the 2captcha Sandbox.

  1. Copy the JSON from the console and send the task via API v2 (createTask), specifying your clientKey.
  2. Go to the Sandbox: https://2captcha.com/setting#sandbox and switch to Worker mode.
  3. If you see a correctly loading Turnstile widget, the parameters are collected correctly. If the widget shows an error, ensure you sent the request instantly after the JSON appeared in the console, without refreshing the page.

Common Errors and Solutions

Error / Problem Cause Solution
Validation or expiration error The pagedata or data parameters have expired. These parameters are single-use. Intercept them anew via the console script and send to the API instantly, without refreshing the page.
Interception script did not work The script was injected after the captcha had already loaded. Inject the script immediately after opening the page, before any actions that might trigger the widget.
ERROR_MISSING_PARAMETER One of the required parameters (action, data, or pagedata) is missing. Make sure you copied the entire JSON object from the console without truncation.
Site does not accept the token The token was not passed through the callback. You must call window.tsCallback(token) after receiving the token.
window.tsCallback is not a function The interception script did not save the callback. Verify that you injected the script BEFORE the captcha appeared. If the widget has already loaded, reload the page and inject the script again.