API Tutorials

Solving CAPTCHAs with Dart for Flutter Applications

Dart developers building Flutter apps, backend services with Dart Frog or Shelf, or CLI automation tools encounter CAPTCHAs in form submissions and API interactions. CaptchaAI's HTTP API integrates natively with Dart's async/await model through the http package or dio.

This guide covers reCAPTCHA v2/v3, Cloudflare Turnstile, and image CAPTCHA solving — with both pure Dart and Flutter-specific implementations.


Why Dart for CAPTCHA Integration

  • Async-firstFuture, async/await, and Stream handle API polling naturally
  • Single codebase — Flutter shares code between iOS, Android, web, and desktop
  • Strong typing — catch API response issues at compile time
  • Server-side Dart — use the same solver on backend with dart_frog or shelf
  • Isolates — true parallel solving without shared-state complexity

Prerequisites

Add to pubspec.yaml:

dependencies:
  http: ^1.2.0
  dio: ^5.4.0       # optional, alternative HTTP client
  image: ^4.2.0     # optional, for image processing

Run:

dart pub get
# or for Flutter:
flutter pub get

Data Models

enum CaptchaType { recaptchaV2, recaptchaV3, turnstile, imageBase64 }

class CaptchaTask {
  final CaptchaType type;
  final String? sitekey;
  final String? pageUrl;
  final String? action;
  final double? minScore;
  final String? imageBody;

  CaptchaTask.recaptchaV2({required this.sitekey, required this.pageUrl})
      : type = CaptchaType.recaptchaV2,
        action = null,
        minScore = null,
        imageBody = null;

  CaptchaTask.recaptchaV3({
    required this.sitekey,
    required this.pageUrl,
    this.action = 'verify',
    this.minScore = 0.7,
  })  : type = CaptchaType.recaptchaV3,
        imageBody = null;

  CaptchaTask.turnstile({required this.sitekey, required this.pageUrl})
      : type = CaptchaType.turnstile,
        action = null,
        minScore = null,
        imageBody = null;

  CaptchaTask.imageBase64({required this.imageBody})
      : type = CaptchaType.imageBase64,
        sitekey = null,
        pageUrl = null,
        action = null,
        minScore = null;
}

class CaptchaException implements Exception {
  final String message;
  CaptchaException(this.message);

  @override
  String toString() => 'CaptchaException: $message';
}

Core Solver — http Package

import 'dart:convert';
import 'dart:async';
import 'package:http/http.dart' as http;

class CaptchaSolver {
  final String apiKey;
  final String _baseUrl = 'https://ocr.captchaai.com';
  final Duration pollInterval;
  final Duration maxWait;
  final http.Client _client;

  CaptchaSolver(
    this.apiKey, {
    this.pollInterval = const Duration(seconds: 5),
    this.maxWait = const Duration(seconds: 300),
    http.Client? client,
  }) : _client = client ?? http.Client();

  /// Solve any CAPTCHA type
  Future<String> solve(CaptchaTask task) async {
    final taskId = await _submit(task);
    return _poll(taskId);
  }

  /// Check account balance
  Future<double> checkBalance() async {
    final uri = Uri.parse('$_baseUrl/res.php').replace(queryParameters: {
      'key': apiKey,
      'action': 'getbalance',
      'json': '1',
    });

    final response = await _client.get(uri);
    final data = jsonDecode(response.body);
    return double.parse(data['request'].toString());
  }

  Future<String> _submit(CaptchaTask task) async {
    final params = <String, String>{
      'key': apiKey,
      'json': '1',
    };

    switch (task.type) {
      case CaptchaType.recaptchaV2:
        params['method'] = 'userrecaptcha';
        params['googlekey'] = task.sitekey!;
        params['pageurl'] = task.pageUrl!;
        break;
      case CaptchaType.recaptchaV3:
        params['method'] = 'userrecaptcha';
        params['googlekey'] = task.sitekey!;
        params['pageurl'] = task.pageUrl!;
        params['version'] = 'v3';
        params['action'] = task.action!;
        params['min_score'] = task.minScore.toString();
        break;
      case CaptchaType.turnstile:
        params['method'] = 'turnstile';
        params['key'] = task.sitekey!;
        params['pageurl'] = task.pageUrl!;
        break;
      case CaptchaType.imageBase64:
        params['method'] = 'base64';
        params['body'] = task.imageBody!;
        break;
    }

    final response = await _client.post(
      Uri.parse('$_baseUrl/in.php'),
      body: params,
    );

    final data = jsonDecode(response.body);

    if (data['status'] != 1) {
      throw CaptchaException('Submit failed: ${data['request']}');
    }

    return data['request'];
  }

  Future<String> _poll(String taskId) async {
    final deadline = DateTime.now().add(maxWait);

    while (DateTime.now().isBefore(deadline)) {
      await Future.delayed(pollInterval);

      final uri = Uri.parse('$_baseUrl/res.php').replace(queryParameters: {
        'key': apiKey,
        'action': 'get',
        'id': taskId,
        'json': '1',
      });

      final response = await _client.get(uri);
      final data = jsonDecode(response.body);

      if (data['request'] == 'CAPCHA_NOT_READY') continue;

      if (data['status'] != 1) {
        throw CaptchaException('Solve failed: ${data['request']}');
      }

      return data['request'];
    }

    throw CaptchaException('Timeout waiting for solution');
  }

  void dispose() => _client.close();
}

Usage

Future<void> main() async {
  final solver = CaptchaSolver('YOUR_API_KEY');

  try {
    // Check balance
    final balance = await solver.checkBalance();
    print('Balance: \$${balance.toStringAsFixed(2)}');

    // Solve reCAPTCHA v2
    final token = await solver.solve(CaptchaTask.recaptchaV2(
      sitekey: '6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-',
      pageUrl: 'https://example.com/login',
    ));
    print('Token: ${token.substring(0, 50)}...');

    // Solve Turnstile
    final turnstile = await solver.solve(CaptchaTask.turnstile(
      sitekey: '0x4AAAAAAAB5...',
      pageUrl: 'https://example.com/form',
    ));
    print('Turnstile: ${turnstile.substring(0, 50)}...');

  } finally {
    solver.dispose();
  }
}

Image CAPTCHA Solving

import 'dart:io';
import 'dart:convert';

Future<String> solveImageFile(CaptchaSolver solver, String path) async {
  final bytes = await File(path).readAsBytes();
  final base64Image = base64Encode(bytes);

  return solver.solve(CaptchaTask.imageBase64(imageBody: base64Image));
}

// From network URL
Future<String> solveImageUrl(CaptchaSolver solver, String imageUrl) async {
  final client = http.Client();
  final response = await client.get(Uri.parse(imageUrl));
  final base64Image = base64Encode(response.bodyBytes);
  client.close();

  return solver.solve(CaptchaTask.imageBase64(imageBody: base64Image));
}

Concurrent Solving

Future<List<String>> solveBatch(
  CaptchaSolver solver,
  List<CaptchaTask> tasks, {
  int concurrency = 5,
}) async {
  final results = <String>[];

  // Simple parallel: all at once
  final futures = tasks.map((task) => solver.solve(task));
  final resolved = await Future.wait(
    futures,
    eagerError: false,
  );

  return resolved;
}

// With error handling
Future<List<Result<String>>> solveBatchSafe(
  CaptchaSolver solver,
  List<CaptchaTask> tasks,
) async {
  final futures = tasks.map((task) async {
    try {
      final token = await solver.solve(task);
      return Result.success(token);
    } catch (e) {
      return Result.failure(e.toString());
    }
  });

  return Future.wait(futures.toList());
}

class Result<T> {
  final T? value;
  final String? error;
  final bool isSuccess;

  Result.success(this.value)
      : error = null,
        isSuccess = true;
  Result.failure(this.error)
      : value = null,
        isSuccess = false;
}

Solving with Isolates (True Parallelism)

import 'dart:isolate';

Future<String> solveInIsolate(String apiKey, CaptchaTask task) async {
  return Isolate.run(() async {
    final solver = CaptchaSolver(apiKey);
    try {
      return await solver.solve(task);
    } finally {
      solver.dispose();
    }
  });
}

// Parallel solving across isolates
Future<List<String>> solveParallel(
  String apiKey,
  List<CaptchaTask> tasks,
) async {
  final futures = tasks.map((task) => solveInIsolate(apiKey, task));
  return Future.wait(futures.toList());
}

Flutter Integration

Provider/Service

import 'package:flutter/foundation.dart';

class CaptchaService extends ChangeNotifier {
  final CaptchaSolver _solver;
  String? _token;
  String? _error;
  bool _isLoading = false;

  String? get token => _token;
  String? get error => _error;
  bool get isLoading => _isLoading;

  CaptchaService(String apiKey) : _solver = CaptchaSolver(apiKey);

  Future<void> solveRecaptcha(String sitekey, String pageUrl) async {
    _isLoading = true;
    _token = null;
    _error = null;
    notifyListeners();

    try {
      _token = await _solver.solve(CaptchaTask.recaptchaV2(
        sitekey: sitekey,
        pageUrl: pageUrl,
      ));
    } catch (e) {
      _error = e.toString();
    }

    _isLoading = false;
    notifyListeners();
  }

  @override
  void dispose() {
    _solver.dispose();
    super.dispose();
  }
}

Widget

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class CaptchaButton extends StatelessWidget {
  final String sitekey;
  final String pageUrl;

  const CaptchaButton({
    required this.sitekey,
    required this.pageUrl,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Consumer<CaptchaService>(
      builder: (context, service, child) {
        if (service.isLoading) {
          return const Column(
            children: [
              CircularProgressIndicator(),
              SizedBox(height: 8),
              Text('Solving CAPTCHA...'),
            ],
          );
        }

        if (service.token != null) {
          return const Icon(Icons.check_circle, color: Colors.green, size: 48);
        }

        return Column(
          children: [
            ElevatedButton(
              onPressed: () => service.solveRecaptcha(sitekey, pageUrl),
              child: const Text('Solve CAPTCHA'),
            ),
            if (service.error != null)
              Text(service.error!, style: const TextStyle(color: Colors.red)),
          ],
        );
      },
    );
  }
}

Dio Alternative

import 'package:dio/dio.dart';

class DioCaptchaSolver {
  final String apiKey;
  final Dio _dio;

  DioCaptchaSolver(this.apiKey)
      : _dio = Dio(BaseOptions(
          baseUrl: 'https://ocr.captchaai.com',
          connectTimeout: const Duration(seconds: 30),
          receiveTimeout: const Duration(seconds: 30),
        ));

  Future<String> solveRecaptchaV2(String siteUrl, String sitekey) async {
    final submitResp = await _dio.post('/in.php', data: {
      'key': apiKey,
      'json': 1,
      'method': 'userrecaptcha',
      'googlekey': sitekey,
      'pageurl': siteUrl,
    }, options: Options(contentType: Headers.formUrlEncodedContentType));

    if (submitResp.data['status'] != 1) {
      throw CaptchaException('Submit: ${submitResp.data['request']}');
    }

    final taskId = submitResp.data['request'];
    final deadline = DateTime.now().add(const Duration(seconds: 300));

    while (DateTime.now().isBefore(deadline)) {
      await Future.delayed(const Duration(seconds: 5));

      final pollResp = await _dio.get('/res.php', queryParameters: {
        'key': apiKey,
        'action': 'get',
        'id': taskId,
        'json': 1,
      });

      if (pollResp.data['request'] == 'CAPCHA_NOT_READY') continue;
      if (pollResp.data['status'] != 1) {
        throw CaptchaException('Solve: ${pollResp.data['request']}');
      }

      return pollResp.data['request'];
    }

    throw CaptchaException('Timeout');
  }

  void dispose() => _dio.close();
}

Error Handling with Retry

Future<String> solveWithRetry(
  CaptchaSolver solver,
  CaptchaTask task, {
  int maxRetries = 3,
}) async {
  const retryable = ['ERROR_NO_SLOT_AVAILABLE', 'ERROR_CAPTCHA_UNSOLVABLE'];
  CaptchaException? lastError;

  for (var attempt = 0; attempt <= maxRetries; attempt++) {
    if (attempt > 0) {
      final delay = Duration(
        milliseconds: (1000 * (1 << attempt)) + (DateTime.now().millisecond % 2000),
      );
      print('Retry $attempt/$maxRetries after ${delay.inSeconds}s');
      await Future.delayed(delay);
    }

    try {
      return await solver.solve(task);
    } on CaptchaException catch (e) {
      lastError = e;
      final isRetryable = retryable.any((err) => e.message.contains(err));
      if (!isRetryable) rethrow;
    }
  }

  throw lastError ?? CaptchaException('Max retries exceeded');
}

Troubleshooting

Error Cause Fix
ERROR_WRONG_USER_KEY Invalid API key Verify key at dashboard
ERROR_ZERO_BALANCE No funds Top up account
SocketException Network connectivity Check internet connection
FormatException Unexpected response Check Content-Type, parse response body
ClientException HTTP client error Add timeout, check URL
Isolate.run error Dart < 2.19 Upgrade Dart SDK or use compute()

FAQ

Does CaptchaAI have a Dart/Flutter package?

CaptchaAI provides a REST API. The examples here use Dart's standard http package — no special SDK needed.

Can I use this in a Flutter app?

Yes. Use the CaptchaService provider pattern shown above. Network calls run asynchronously and don't block the UI thread.

Does this work on Flutter Web?

Yes, but with limitations. The http package works on web, but CORS policies may require routing through a backend proxy.

How do I handle concurrency?

Use Future.wait() for light concurrency within a single isolate, or Isolate.run() for CPU-heavy parallel operations.



Add CAPTCHA solving to your Dart and Flutter apps — get your API key and integrate today.

Discussions (0)

No comments yet.

Related Posts

Tutorials CAPTCHA Solving Fallback Chains
Implement fallback chains for CAPTCHA solving with Captcha AI.

Implement fallback chains for CAPTCHA solving with Captcha AI. Cascade through solver methods, proxy pools, an...

Automation Python reCAPTCHA v2
Apr 06, 2026
API Tutorials CaptchaAI API Latency Optimization: Faster Solves
Reduce CAPTCHA solve latency with Captcha AI by optimizing poll intervals, connection pooling, prefetching, and proxy selection.

Reduce CAPTCHA solve latency with Captcha AI by optimizing poll intervals, connection pooling, prefetching, an...

Automation Python reCAPTCHA v2
Feb 27, 2026
API Tutorials Building a Python Wrapper Library for CaptchaAI API
Build a reusable Python wrapper library for the Captcha AI API with type hints, retry logic, context managers, and support for CAPTCHA types.

Build a reusable Python wrapper library for the Captcha AI API with type hints, retry logic, context managers,...

Automation Python reCAPTCHA v2
Jan 31, 2026
API Tutorials Solving CAPTCHAs with Swift and CaptchaAI API
Complete guide to solving re CAPTCHA, Turnstile, and image CAPTCHAs in Swift using Captcha AI's HTTP API with URLSession, async/await, and Alamofire.

Complete guide to solving re CAPTCHA, Turnstile, and image CAPTCHAs in Swift using Captcha AI's HTTP API with...

Automation reCAPTCHA v2 Cloudflare Turnstile
Apr 05, 2026
API Tutorials Solving CAPTCHAs with Kotlin and CaptchaAI API
Complete guide to solving re CAPTCHA, Turnstile, and image CAPTCHAs in Kotlin using Captcha AI's HTTP API with Ok Http, Ktor client, and coroutines.

Complete guide to solving re CAPTCHA, Turnstile, and image CAPTCHAs in Kotlin using Captcha AI's HTTP API with...

Automation reCAPTCHA v2 Cloudflare Turnstile
Mar 06, 2026
API Tutorials Solving CAPTCHAs with Ruby and CaptchaAI API
Complete guide to solving re CAPTCHA, Turnstile, and image CAPTCHAs in Ruby using Captcha AI's HTTP API with net/http, Faraday, and HTTParty.

Complete guide to solving re CAPTCHA, Turnstile, and image CAPTCHAs in Ruby using Captcha AI's HTTP API with n...

Automation reCAPTCHA v2 Cloudflare Turnstile
Mar 17, 2026
API Tutorials Solving CAPTCHAs with Scala and CaptchaAI API
Complete guide to solving re CAPTCHA, Turnstile, and image CAPTCHAs in Scala using Captcha AI's HTTP API with sttp, Akka HTTP, and Scala Futures.

Complete guide to solving re CAPTCHA, Turnstile, and image CAPTCHAs in Scala using Captcha AI's HTTP API with...

Automation reCAPTCHA v2 Cloudflare Turnstile
Feb 08, 2026
API Tutorials Solving CAPTCHAs with Rust and CaptchaAI API
Complete guide to solving re CAPTCHA, Turnstile, and image CAPTCHAs in Rust using Captcha AI's HTTP API with reqwest, tokio async runtime, and serde.

Complete guide to solving re CAPTCHA, Turnstile, and image CAPTCHAs in Rust using Captcha AI's HTTP API with r...

Automation reCAPTCHA v2 Cloudflare Turnstile
Jan 24, 2026
API Tutorials Solving CAPTCHAs with Perl and CaptchaAI API
Complete guide to solving re CAPTCHA, Turnstile, and image CAPTCHAs in Perl using Captcha AI's HTTP API with LWP User Agent, HTTP Tiny, and Mojo User Agent.

Complete guide to solving re CAPTCHA, Turnstile, and image CAPTCHAs in Perl using Captcha AI's HTTP API with L...

Automation reCAPTCHA v2 Cloudflare Turnstile
Feb 23, 2026
API Tutorials Type-Safe CaptchaAI Client with TypeScript Generics
Build a type-safe Captcha AI API client in Type Script using generics, discriminated unions, and builder patterns for CAPTCHA types.

Build a type-safe Captcha AI API client in Type Script using generics, discriminated unions, and builder patte...

Automation reCAPTCHA v2 Cloudflare Turnstile
Feb 05, 2026
API Tutorials How to Solve reCAPTCHA v2 Callback Using API
how to solve re CAPTCHA v 2 callback implementations using Captcha AI API.

Learn how to solve re CAPTCHA v 2 callback implementations using Captcha AI API. Detect the callback function,...

Automation reCAPTCHA v2 Webhooks
Mar 01, 2026
API Tutorials Solve GeeTest v3 CAPTCHA with Python and CaptchaAI
Step-by-step Python tutorial for solving Gee Test v 3 slide puzzle CAPTCHAs using the Captcha AI API.

Step-by-step Python tutorial for solving Gee Test v 3 slide puzzle CAPTCHAs using the Captcha AI API. Includes...

Automation Python Testing
Mar 23, 2026
API Tutorials Case-Sensitive CAPTCHA API Parameter Guide
How to use the regsense parameter for case-sensitive CAPTCHA solving with Captcha AI.

How to use the regsense parameter for case-sensitive CAPTCHA solving with Captcha AI. Covers when to use, comm...

Python Web Scraping Image OCR
Apr 09, 2026