API Tutorials

Solving CAPTCHAs with Ruby and CaptchaAI API

Ruby developers building web scrapers, automation tools, or testing frameworks hit CAPTCHAs constantly. Whether you use Rails, Sinatra, or standalone scripts, CaptchaAI's HTTP API integrates cleanly with Ruby's standard library and popular HTTP gems.

This guide covers reCAPTCHA v2, Cloudflare Turnstile, and image CAPTCHA solving using net/http, Faraday, and HTTParty — with production-ready classes you can drop into any Ruby project.


Why Ruby for CAPTCHA Automation

Ruby's clean syntax and rich gem ecosystem make it ideal for automation scripts. Combined with CaptchaAI's REST API, you get:

  • Simple HTTP integrationnet/http ships with Ruby, no extra dependencies
  • Gem flexibility — Faraday, HTTParty, or RestClient all work
  • Concurrent solving — Ruby threads handle parallel CAPTCHA requests well
  • Rails/Sinatra compatibility — embed solving into web applications directly

Prerequisites

  • Ruby 2.7+ (3.x recommended)
  • CaptchaAI API key (get one here)
  • Target site URL and sitekey for token CAPTCHAs

Install optional gems:

gem install faraday
gem install httparty
gem install nokogiri   # for sitekey extraction

CaptchaAI API Flow

Every CAPTCHA solve follows two steps:

  1. Submit — POST task to https://ocr.captchaai.com/in.php → receive task ID
  2. Poll — GET https://ocr.captchaai.com/res.php?action=get&id=TASK_ID → receive token

Method 1: net/http (Standard Library)

Zero dependencies — works with any Ruby installation.

Basic reCAPTCHA v2 Solver

require 'net/http'
require 'uri'
require 'json'

class CaptchaSolver
  BASE_URL = 'https://ocr.captchaai.com'
  POLL_INTERVAL = 5
  MAX_ATTEMPTS = 60

  def initialize(api_key)
    @api_key = api_key
  end

  # Submit reCAPTCHA v2 task
  def solve_recaptcha_v2(site_url, sitekey)
    task_id = submit_task(
      method: 'userrecaptcha',
      googlekey: sitekey,
      pageurl: site_url
    )
    poll_result(task_id)
  end

  # Submit Cloudflare Turnstile task
  def solve_turnstile(site_url, sitekey)
    task_id = submit_task(
      method: 'turnstile',
      key: sitekey,
      pageurl: site_url
    )
    poll_result(task_id)
  end

  # Submit image CAPTCHA (base64)
  def solve_image(image_base64)
    task_id = submit_task(
      method: 'base64',
      body: image_base64
    )
    poll_result(task_id)
  end

  private

  def submit_task(params)
    uri = URI("#{BASE_URL}/in.php")
    payload = { key: @api_key, json: 1 }.merge(params)

    response = Net::HTTP.post_form(uri, payload)
    data = JSON.parse(response.body)

    raise "Submit failed: #{data['request']}" unless data['status'] == 1
    data['request']
  end

  def poll_result(task_id)
    uri = URI("#{BASE_URL}/res.php")
    params = { key: @api_key, action: 'get', id: task_id, json: 1 }

    MAX_ATTEMPTS.times do
      sleep(POLL_INTERVAL)

      uri.query = URI.encode_www_form(params)
      response = Net::HTTP.get_response(uri)
      data = JSON.parse(response.body)

      next if data['request'] == 'CAPCHA_NOT_READY'
      raise "Solve failed: #{data['request']}" unless data['status'] == 1

      return data['request']
    end

    raise 'Timeout: CAPTCHA not solved within time limit'
  end
end

# Usage
solver = CaptchaSolver.new('YOUR_API_KEY')

token = solver.solve_recaptcha_v2(
  'https://example.com/login',
  '6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-'
)
puts "reCAPTCHA token: #{token[0..50]}..."

Submitting the Token

require 'net/http'

uri = URI('https://example.com/login')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

request = Net::HTTP::Post.new(uri)
request.set_form_data(
  'username' => 'user@example.com',
  'password' => 'password',
  'g-recaptcha-response' => token
)

response = http.request(request)
puts "Login response: #{response.code}"

Faraday provides middleware, retries, and connection pooling.

require 'faraday'
require 'json'

class FaradayCaptchaSolver
  BASE_URL = 'https://ocr.captchaai.com'

  def initialize(api_key)
    @api_key = api_key
    @conn = Faraday.new(url: BASE_URL) do |f|
      f.request :url_encoded
      f.response :json, content_type: /\bjson$/
      f.adapter Faraday.default_adapter
      f.options.timeout = 10
      f.options.open_timeout = 5
    end
  end

  def solve_recaptcha_v2(site_url, sitekey)
    task_id = submit(
      method: 'userrecaptcha',
      googlekey: sitekey,
      pageurl: site_url
    )
    poll(task_id)
  end

  def solve_turnstile(site_url, sitekey)
    task_id = submit(
      method: 'turnstile',
      key: sitekey,
      pageurl: site_url
    )
    poll(task_id)
  end

  def solve_recaptcha_v3(site_url, sitekey, action: 'verify', min_score: 0.7)
    task_id = submit(
      method: 'userrecaptcha',
      googlekey: sitekey,
      pageurl: site_url,
      version: 'v3',
      action: action,
      min_score: min_score
    )
    poll(task_id)
  end

  def check_balance
    response = @conn.get('/res.php', key: @api_key, action: 'getbalance', json: 1)
    response.body['request'].to_f
  end

  private

  def submit(params)
    response = @conn.post('/in.php', { key: @api_key, json: 1 }.merge(params))
    data = response.body

    unless data['status'] == 1
      raise "Submit error: #{data['request']}"
    end

    data['request']
  end

  def poll(task_id, max_wait: 300, interval: 5)
    deadline = Time.now + max_wait

    loop do
      raise 'Timeout waiting for CAPTCHA solution' if Time.now > deadline
      sleep(interval)

      response = @conn.get('/res.php', key: @api_key, action: 'get', id: task_id, json: 1)
      data = response.body

      next if data['request'] == 'CAPCHA_NOT_READY'
      raise "Solve error: #{data['request']}" unless data['status'] == 1

      return data['request']
    end
  end
end

# Usage
solver = FaradayCaptchaSolver.new('YOUR_API_KEY')

balance = solver.check_balance
puts "Balance: $#{balance}"

token = solver.solve_recaptcha_v2(
  'https://example.com/form',
  '6LdKlZEpAAAAAAOQjzC2v_d36tWxCl6dWsozdSy9'
)
puts "Token: #{token[0..50]}..."

Method 3: HTTParty (Simplest Syntax)

HTTParty is ideal for quick scripts.

require 'httparty'

class HTTPartySolver
  include HTTParty
  base_uri 'https://ocr.captchaai.com'

  def initialize(api_key)
    @api_key = api_key
  end

  def solve_recaptcha_v2(site_url, sitekey)
    response = self.class.post('/in.php', body: {
      key: @api_key,
      method: 'userrecaptcha',
      googlekey: sitekey,
      pageurl: site_url,
      json: 1
    })

    data = response.parsed_response
    raise "Submit failed: #{data['request']}" unless data['status'] == 1

    poll_result(data['request'])
  end

  def solve_image(image_path)
    image_data = Base64.strict_encode64(File.read(image_path, mode: 'rb'))

    response = self.class.post('/in.php', body: {
      key: @api_key,
      method: 'base64',
      body: image_data,
      json: 1
    })

    data = response.parsed_response
    raise "Submit failed: #{data['request']}" unless data['status'] == 1

    poll_result(data['request'])
  end

  private

  def poll_result(task_id)
    60.times do
      sleep 5

      response = self.class.get('/res.php', query: {
        key: @api_key,
        action: 'get',
        id: task_id,
        json: 1
      })

      data = response.parsed_response
      next if data['request'] == 'CAPCHA_NOT_READY'
      raise "Solve failed: #{data['request']}" unless data['status'] == 1

      return data['request']
    end

    raise 'CAPTCHA solve timeout'
  end
end

# Usage
solver = HTTPartySolver.new('YOUR_API_KEY')
token = solver.solve_recaptcha_v2('https://example.com', 'SITEKEY_HERE')
puts token

Sitekey Extraction with Nokogiri

require 'nokogiri'
require 'open-uri'

def extract_sitekey(url)
  doc = Nokogiri::HTML(URI.open(url))

  # reCAPTCHA
  recaptcha = doc.at_css('[data-sitekey]')
  return { type: 'recaptcha', key: recaptcha['data-sitekey'] } if recaptcha

  # Turnstile
  turnstile = doc.at_css('.cf-turnstile[data-sitekey]')
  return { type: 'turnstile', key: turnstile['data-sitekey'] } if turnstile

  # Check scripts for sitekey patterns
  doc.css('script').each do |script|
    text = script.text
    if (match = text.match(/sitekey['":\s]+['"]([A-Za-z0-9_-]{20,})['"]/))
      return { type: 'unknown', key: match[1] }
    end
  end

  nil
end

result = extract_sitekey('https://example.com/protected')
puts "Found #{result[:type]} sitekey: #{result[:key]}" if result

Concurrent Solving with Threads

Solve multiple CAPTCHAs in parallel using Ruby threads:

require 'thread'

solver = CaptchaSolver.new('YOUR_API_KEY')

tasks = [
  { url: 'https://site-a.com', key: 'SITEKEY_A' },
  { url: 'https://site-b.com', key: 'SITEKEY_B' },
  { url: 'https://site-c.com', key: 'SITEKEY_C' }
]

mutex = Mutex.new
results = {}

threads = tasks.map do |task|
  Thread.new do
    begin
      token = solver.solve_recaptcha_v2(task[:url], task[:key])
      mutex.synchronize { results[task[:url]] = token }
    rescue => e
      mutex.synchronize { results[task[:url]] = "Error: #{e.message}" }
    end
  end
end

threads.each(&:join)

results.each do |url, result|
  puts "#{url}: #{result[0..50]}..."
end

Error Handling Best Practices

class RobustSolver
  MAX_RETRIES = 3
  RETRY_ERRORS = %w[
    ERROR_NO_SLOT_AVAILABLE
    ERROR_CAPTCHA_UNSOLVABLE
    CAPCHA_NOT_READY
  ]

  def initialize(api_key)
    @solver = CaptchaSolver.new(api_key)
  end

  def solve_with_retry(site_url, sitekey, type: :recaptcha_v2)
    retries = 0

    begin
      case type
      when :recaptcha_v2
        @solver.solve_recaptcha_v2(site_url, sitekey)
      when :turnstile
        @solver.solve_turnstile(site_url, sitekey)
      end
    rescue => e
      retries += 1
      if retries <= MAX_RETRIES && retryable?(e.message)
        delay = 2**retries + rand(0..2)
        puts "Retry #{retries}/#{MAX_RETRIES} after #{delay}s: #{e.message}"
        sleep(delay)
        retry
      end
      raise
    end
  end

  private

  def retryable?(message)
    RETRY_ERRORS.any? { |err| message.include?(err) }
  end
end

Rails Integration Example

# app/services/captcha_service.rb
class CaptchaService
  def initialize
    @api_key = Rails.application.credentials.captchaai_api_key
    @solver = FaradayCaptchaSolver.new(@api_key)
  end

  def verify_and_solve(site_url, sitekey)
    Rails.cache.fetch("captcha:#{sitekey}", expires_in: 90.seconds) do
      @solver.solve_recaptcha_v2(site_url, sitekey)
    end
  end
end

# In a controller
class ScrapingController < ApplicationController
  def create
    service = CaptchaService.new
    token = service.verify_and_solve(params[:url], params[:sitekey])
    render json: { token: token }
  rescue => e
    render json: { error: e.message }, status: :unprocessable_entity
  end
end

Troubleshooting

Error Cause Fix
ERROR_WRONG_USER_KEY Invalid API key Verify key at dashboard
ERROR_ZERO_BALANCE No funds Top up account
ERROR_NO_SLOT_AVAILABLE Server busy Retry after 5 seconds
ERROR_CAPTCHA_UNSOLVABLE Failed solving Retry with fresh task
SSL_connect error Ruby SSL config Update ca-certificates or set OpenSSL::SSL::VERIFY_PEER
Timeout::Error Network/polling timeout Increase timeout, check connectivity

FAQ

Does CaptchaAI have a Ruby gem?

CaptchaAI provides a REST API that works with any Ruby HTTP library. No official gem is needed — net/http, Faraday, or HTTParty all integrate cleanly.

Which Ruby HTTP library should I use?

Use net/http for zero-dependency scripts, Faraday for production apps needing middleware and retries, or HTTParty for quick prototypes.

Can I use this with Ruby on Rails?

Yes. Wrap the solver in a service object and optionally use Rails.cache to avoid re-solving within the token validity window.

Is Ruby threading safe for parallel solves?

Yes. CaptchaAI API calls are I/O-bound, so Ruby threads handle concurrency well. Use a Mutex for shared state.



Start solving CAPTCHAs in Ruby — get your API key and integrate in minutes.

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 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
Reference Postman Collection for CaptchaAI API Testing
Import a ready-to-use Postman collection for testing Captcha AI API endpoints — submit, poll, balance check, and error scenarios.

Import a ready-to-use Postman collection for testing all Captcha AI API endpoints — submit, poll, balance chec...

Automation reCAPTCHA v2 Cloudflare Turnstile
Jan 15, 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