Integrations

Solving CAPTCHAs in React Native WebViews with CaptchaAI

React Native apps that load web content through react-native-webview frequently encounter CAPTCHAs — reCAPTCHA v2 checkboxes, Cloudflare Turnstile widgets, or challenge pages that block form submissions. CaptchaAI solves these challenges programmatically so your mobile app flows remain uninterrupted.

This guide shows you how to detect CAPTCHAs inside a React Native WebView, extract the required parameters via JavaScript injection, send them to the CaptchaAI API, and inject the solved token back into the page.

Real-World Scenario

You are building a React Native app that loads a third-party web form inside a WebView. The form includes a reCAPTCHA v2 checkbox that must be completed before submission. Your goal is to:

  1. Detect the CAPTCHA widget when the WebView finishes loading
  2. Extract the sitekey from the DOM
  3. Solve it via CaptchaAI's API from a backend service
  4. Inject the token back into the WebView and submit the form

Environment: React Native 0.72+, react-native-webview 13+, Node.js backend, CaptchaAI API.

Architecture Overview

The flow splits across three layers:

Layer Responsibility
React Native WebView Detects CAPTCHA, extracts sitekey, injects solved token
Backend API (Node.js) Receives sitekey + pageurl, calls CaptchaAI, returns token
CaptchaAI API Solves the CAPTCHA and returns the token

The WebView communicates with your React Native code via window.ReactNativeWebView.postMessage(), and your backend handles the CaptchaAI interaction to keep API keys off the client.

Step 1: Detect CAPTCHA and Extract Sitekey in WebView

Use the injectedJavaScript prop to scan for CAPTCHA elements once the page loads:

// CaptchaDetector.js — React Native Component
import React, { useRef, useState } from 'react';
import { View, ActivityIndicator } from 'react-native';
import { WebView } from 'react-native-webview';

const CAPTCHA_DETECTION_SCRIPT = `
  (function() {
    // Detect reCAPTCHA v2
    const recaptchaDiv = document.querySelector('.g-recaptcha');
    if (recaptchaDiv) {
      const sitekey = recaptchaDiv.getAttribute('data-sitekey');
      window.ReactNativeWebView.postMessage(JSON.stringify({
        type: 'captcha_detected',
        captchaType: 'recaptcha_v2',
        sitekey: sitekey,
        pageurl: window.location.href
      }));
      return;
    }

    // Detect Cloudflare Turnstile
    const turnstileDiv = document.querySelector('.cf-turnstile');
    if (turnstileDiv) {
      const sitekey = turnstileDiv.getAttribute('data-sitekey');
      window.ReactNativeWebView.postMessage(JSON.stringify({
        type: 'captcha_detected',
        captchaType: 'turnstile',
        sitekey: sitekey,
        pageurl: window.location.href
      }));
      return;
    }

    window.ReactNativeWebView.postMessage(JSON.stringify({
      type: 'no_captcha'
    }));
  })();
  true;
`;

export default function CaptchaWebView({ url }) {
  const webviewRef = useRef(null);
  const [solving, setSolving] = useState(false);

  const handleMessage = async (event) => {
    const data = JSON.parse(event.nativeEvent.data);

    if (data.type === 'captcha_detected') {
      setSolving(true);
      try {
        const token = await solveCaptchaViaBackend(
          data.captchaType,
          data.sitekey,
          data.pageurl
        );
        injectToken(data.captchaType, token);
      } catch (err) {
        console.error('CAPTCHA solve failed:', err.message);
      } finally {
        setSolving(false);
      }
    }
  };

  const solveCaptchaViaBackend = async (captchaType, sitekey, pageurl) => {
    const response = await fetch('https://your-backend.com/api/solve-captcha', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ captchaType, sitekey, pageurl }),
    });
    const result = await response.json();
    if (!result.token) throw new Error(result.error || 'No token returned');
    return result.token;
  };

  const injectToken = (captchaType, token) => {
    let script;
    if (captchaType === 'recaptcha_v2') {
      script = `
        document.getElementById('g-recaptcha-response').value = '${token}';
        if (typeof ___grecaptcha_cfg !== 'undefined') {
          Object.keys(___grecaptcha_cfg.clients).forEach(key => {
            const client = ___grecaptcha_cfg.clients[key];
            Object.keys(client).forEach(k => {
              const item = client[k];
              if (item && item.callback) {
                item.callback('${token}');
              }
            });
          });
        }
        true;
      `;
    } else if (captchaType === 'turnstile') {
      script = `
        const input = document.querySelector('[name="cf-turnstile-response"]');
        if (input) input.value = '${token}';
        const callback = document.querySelector('.cf-turnstile')
          ?.getAttribute('data-callback');
        if (callback && typeof window[callback] === 'function') {
          window[callback]('${token}');
        }
        true;
      `;
    }
    webviewRef.current?.injectJavaScript(script);
  };

  return (
    <View style={{ flex: 1 }}>
      {solving && <ActivityIndicator size="large" />}
      <WebView
        ref={webviewRef}
        source={{ uri: url }}
        injectedJavaScript={CAPTCHA_DETECTION_SCRIPT}
        onMessage={handleMessage}
        javaScriptEnabled={true}
      />
    </View>
  );
}

Step 2: Build the Backend Solver (Node.js)

Keep your CaptchaAI API key on the server side. The backend receives the sitekey and page URL, submits to CaptchaAI, polls for the result, and returns the token:

// server.js — Express backend
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());

const API_KEY = process.env.CAPTCHAAI_API_KEY || 'YOUR_API_KEY';

app.post('/api/solve-captcha', async (req, res) => {
  const { captchaType, sitekey, pageurl } = req.body;

  try {
    // Step 1: Submit task to CaptchaAI
    const submitParams = {
      key: API_KEY,
      pageurl: pageurl,
      json: '1',
    };

    if (captchaType === 'recaptcha_v2') {
      submitParams.method = 'userrecaptcha';
      submitParams.googlekey = sitekey;
    } else if (captchaType === 'turnstile') {
      submitParams.method = 'turnstile';
      submitParams.sitekey = sitekey;
    }

    const submitResponse = await axios.get(
      'https://ocr.captchaai.com/in.php',
      { params: submitParams }
    );

    if (submitResponse.data.status !== 1) {
      return res.status(400).json({ error: submitResponse.data.request });
    }

    const taskId = submitResponse.data.request;

    // Step 2: Poll for result
    const token = await pollForResult(taskId);
    res.json({ token });
  } catch (error) {
    console.error('Solve error:', error.message);
    res.status(500).json({ error: 'Failed to solve CAPTCHA' });
  }
});

async function pollForResult(taskId, maxAttempts = 30) {
  for (let i = 0; i < maxAttempts; i++) {
    await new Promise((r) => setTimeout(r, 5000));

    const response = await axios.get('https://ocr.captchaai.com/res.php', {
      params: {
        key: API_KEY,
        action: 'get',
        id: taskId,
        json: '1',
      },
    });

    if (response.data.status === 1) {
      return response.data.request;
    }

    if (
      response.data.request !== 'CAPCHA_NOT_READY' &&
      response.data.status === 0
    ) {
      throw new Error(response.data.request);
    }
  }
  throw new Error('Polling timeout — CAPTCHA not solved in time');
}

app.listen(3000, () => console.log('Solver backend running on port 3000'));

Step 3: Handle Token Expiration in WebView

CAPTCHA tokens expire — reCAPTCHA v2 tokens last ~120 seconds, Turnstile tokens ~300 seconds. If the user delays form submission, re-solve before submitting:

// Add to CaptchaWebView component
const [tokenTimestamp, setTokenTimestamp] = useState(null);
const TOKEN_TTL_MS = 110000; // 110 seconds for reCAPTCHA v2

const handleFormSubmit = async (captchaType, sitekey, pageurl) => {
  const now = Date.now();
  if (!tokenTimestamp || now - tokenTimestamp > TOKEN_TTL_MS) {
    const freshToken = await solveCaptchaViaBackend(
      captchaType, sitekey, pageurl
    );
    injectToken(captchaType, freshToken);
    setTokenTimestamp(Date.now());
  }

  webviewRef.current?.injectJavaScript(`
    document.querySelector('form').submit();
    true;
  `);
};

Troubleshooting

Problem Cause Fix
postMessage not received WebView onMessage not set or script error Check onMessage is bound; wrap injection script in try/catch
ERROR_BAD_TOKEN_OR_PAGEURL Sitekey doesn't match the page URL Extract sitekey from the actual iframe src, not the parent page
Token injection doesn't trigger callback reCAPTCHA callback not found in ___grecaptcha_cfg Iterate all client objects and check nested properties for callback functions
CAPCHA_NOT_READY indefinitely Slow solve or invalid parameters Increase polling timeout; verify sitekey and pageurl are correct
WebView shows blank CAPTCHA JavaScript disabled or content blocked Set javaScriptEnabled={true} and ensure no content security policy blocks

FAQ

Can I call CaptchaAI directly from React Native without a backend?

Technically yes, but your API key would be exposed in the app binary. Always route API calls through your own backend to keep credentials secure.

Does this work with Expo managed workflow?

Yes, react-native-webview is supported in Expo SDK 49+ with the expo-dev-client. The JavaScript injection and message passing work identically.

How do I handle pages with both reCAPTCHA and Turnstile?

The detection script checks for both. If a page has multiple CAPTCHAs, extend the script to collect all sitekeys and solve them sequentially before form submission.

What is the average solve time for mobile CAPTCHAs?

reCAPTCHA v2 solves typically take 10-20 seconds. Cloudflare Turnstile is faster at 5-15 seconds. Plan your UX to show a loading indicator during this window.

Does the User-Agent of the WebView affect solve rates?

React Native WebView uses the device's default mobile User-Agent, which aligns well with how real users browse. This generally produces good solve rates without modification.

Next Steps

Start solving CAPTCHAs in your React Native apps — get your CaptchaAI API key and integrate the backend solver today.

Related guides:

Discussions (0)

No comments yet.

Related Posts

Reference CAPTCHA Token Injection Methods Reference
Complete reference for injecting solved CAPTCHA tokens into web pages.

Complete reference for injecting solved CAPTCHA tokens into web pages. Covers re CAPTCHA, Turnstile, and Cloud...

Automation Python reCAPTCHA v2
Apr 08, 2026
Tutorials Pytest Fixtures for CaptchaAI API Testing
Build reusable pytest fixtures to test CAPTCHA-solving workflows with Captcha AI.

Build reusable pytest fixtures to test CAPTCHA-solving workflows with Captcha AI. Covers mocking, live integra...

Automation Python reCAPTCHA v2
Apr 08, 2026
Reference Browser Session Persistence for CAPTCHA Workflows
Manage browser sessions, cookies, and storage across CAPTCHA-solving runs to reduce repeat challenges and maintain authenticated state.

Manage browser sessions, cookies, and storage across CAPTCHA-solving runs to reduce repeat challenges and main...

Automation Python reCAPTCHA v2
Feb 24, 2026
Integrations Browser Profile Isolation + CaptchaAI Integration
Browser profile isolation tools create distinct browser environments with unique fingerprints per session.

Browser profile isolation tools create distinct browser environments with unique fingerprints per session. Com...

Automation Python reCAPTCHA v2
Feb 21, 2026
Comparisons WebDriver vs Chrome DevTools Protocol for CAPTCHA Automation
Compare Web Driver and Chrome Dev Tools Protocol (CDP) for CAPTCHA automation — detection, performance, capabilities, and when to use each with Captcha AI.

Compare Web Driver and Chrome Dev Tools Protocol (CDP) for CAPTCHA automation — detection, performance, capabi...

Automation Python reCAPTCHA v2
Mar 27, 2026
Use Cases CAPTCHA Solving in Ticket Purchase Automation
How to handle CAPTCHAs on ticketing platforms Ticketmaster, AXS, and event sites using Captcha AI for automated purchasing workflows.

How to handle CAPTCHAs on ticketing platforms Ticketmaster, AXS, and event sites using Captcha AI for automate...

Automation Python reCAPTCHA v2
Feb 25, 2026
Use Cases Event Ticket Monitoring with CAPTCHA Handling
Build an event ticket availability monitor that handles CAPTCHAs using Captcha AI.

Build an event ticket availability monitor that handles CAPTCHAs using Captcha AI. Python workflow for checkin...

Automation Python reCAPTCHA v2
Jan 17, 2026
Tutorials Caching CAPTCHA Tokens for Reuse
Cache and reuse CAPTCHA tokens with Captcha AI to reduce API calls and costs.

Cache and reuse CAPTCHA tokens with Captcha AI to reduce API calls and costs. Covers token lifetimes, cache st...

Automation Python reCAPTCHA v2
Feb 15, 2026
Tutorials CAPTCHA Retry Queue with Exponential Backoff
Implement a retry queue with exponential backoff for Captcha AI API calls.

Implement a retry queue with exponential backoff for Captcha AI API calls. Handles transient failures, rate li...

Automation Python reCAPTCHA v2
Feb 15, 2026
Integrations Retool + CaptchaAI: Internal Tool CAPTCHA Form Handling
Build Retool internal tools that solve re CAPTCHA v 2 CAPTCHAs by integrating Captcha AI API through REST API queries and Java Script transformers.

Build Retool internal tools that solve re CAPTCHA v 2 CAPTCHAs by integrating Captcha AI API through REST API...

reCAPTCHA v2 Testing No-Code
Mar 19, 2026
Integrations Axios + CaptchaAI: Solve CAPTCHAs Without a Browser
Use Axios and Captcha AI to solve re CAPTCHA, Turnstile, and image CAPTCHAs in Node.js without launching a browser.

Use Axios and Captcha AI to solve re CAPTCHA, Turnstile, and image CAPTCHAs in Node.js without launching a bro...

Automation All CAPTCHA Types
Apr 08, 2026
Integrations Selenium Wire + CaptchaAI: Request Interception for CAPTCHA Solving
Complete guide to using Selenium Wire for request interception, proxy routing, and automated CAPTCHA solving with Captcha AI in Python.

Complete guide to using Selenium Wire for request interception, proxy routing, and automated CAPTCHA solving w...

Python reCAPTCHA v2 Cloudflare Turnstile
Mar 13, 2026