DevOps & Scaling

GitHub Actions + CaptchaAI: CI/CD CAPTCHA Testing

CAPTCHA-protected pages break automated tests. CaptchaAI integrates with GitHub Actions to solve CAPTCHAs during CI/CD test runs, keeping your pipeline green.


GitHub Actions Workflow

# .github/workflows/captcha-tests.yml
name: CAPTCHA Integration Tests

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:

    - cron: "0 6 * * 1"  # Weekly Monday 6 AM

jobs:
  captcha-tests:
    runs-on: ubuntu-latest
    timeout-minutes: 15

    steps:

      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.11"

      - name: Install dependencies
        run: pip install requests pytest

      - name: Run CAPTCHA integration tests
        env:
          CAPTCHAAI_KEY: ${{ secrets.CAPTCHAAI_KEY }}
        run: pytest tests/test_captcha.py -v --tb=short

Store API Key as GitHub Secret

  1. Go to Settings → Secrets and variables → Actions
  2. Click New repository secret
  3. Name: CAPTCHAAI_KEY
  4. Value: your CaptchaAI API key
  5. Click Add secret

Test File

# tests/test_captcha.py
import os
import time
import pytest
import requests


API_KEY = os.environ.get("CAPTCHAAI_KEY")
BASE_URL = "https://ocr.captchaai.com"


def solve_recaptcha(site_key, page_url, timeout=90):
    """Solve reCAPTCHA v2 via CaptchaAI."""
    resp = requests.post(f"{BASE_URL}/in.php", data={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": site_key,
        "pageurl": page_url,
        "json": 1,
    }, timeout=30)
    result = resp.json()
    assert result.get("status") == 1, f"Submit failed: {result}"

    task_id = result["request"]
    start = time.time()

    while time.time() - start < timeout:
        time.sleep(5)
        resp = requests.get(f"{BASE_URL}/res.php", params={
            "key": API_KEY,
            "action": "get",
            "id": task_id,
            "json": 1,
        }, timeout=15)
        data = resp.json()
        if data["request"] != "CAPCHA_NOT_READY":
            assert data.get("status") == 1, f"Solve failed: {data}"
            return data["request"]

    pytest.fail("CAPTCHA solve timed out")


@pytest.mark.skipif(not API_KEY, reason="CAPTCHAAI_KEY not set")
class TestCaptchaIntegration:
    """Integration tests for CAPTCHA-protected flows."""

    def test_recaptcha_v2_solve(self):
        """Verify CaptchaAI can solve reCAPTCHA v2."""
        token = solve_recaptcha(
            site_key="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
            page_url="https://www.google.com/recaptcha/api2/demo",
        )
        assert len(token) > 100
        assert token.isascii()

    def test_balance_sufficient(self):
        """Ensure account balance is enough for test suite."""
        resp = requests.get(f"{BASE_URL}/res.php", params={
            "key": API_KEY,
            "action": "getbalance",
            "json": 1,
        })
        balance = float(resp.json()["request"])
        assert balance > 0.50, f"Low balance: ${balance}"

    def test_api_key_valid(self):
        """Verify API key is accepted."""
        resp = requests.get(f"{BASE_URL}/res.php", params={
            "key": API_KEY,
            "action": "getbalance",
            "json": 1,
        })
        result = resp.json()
        assert result.get("status") == 1, f"Invalid key: {result}"

Matrix Testing (Multiple CAPTCHA Types)

jobs:
  captcha-matrix:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        captcha-type: [recaptcha-v2, turnstile, image]
      fail-fast: false

    steps:

      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.11"

      - name: Install dependencies
        run: pip install requests pytest

      - name: Run ${{ matrix.captcha-type }} tests
        env:
          CAPTCHAAI_KEY: ${{ secrets.CAPTCHAAI_KEY }}
          CAPTCHA_TYPE: ${{ matrix.captcha-type }}
        run: pytest tests/test_${{ matrix.captcha-type }}.py -v

Caching Test Results

Avoid redundant CAPTCHA solves for unchanged code:


      - name: Cache test results
        uses: actions/cache@v4
        with:
          path: .test-cache
          key: captcha-tests-${{ hashFiles('tests/**') }}

      - name: Skip if cached
        id: check-cache
        run: |
          if [ -f .test-cache/passed ]; then
            echo "skip=true" >> $GITHUB_OUTPUT
          fi

      - name: Run tests
        if: steps.check-cache.outputs.skip != 'true'
        env:
          CAPTCHAAI_KEY: ${{ secrets.CAPTCHAAI_KEY }}
        run: |
          pytest tests/test_captcha.py -v
          mkdir -p .test-cache && touch .test-cache/passed

Slack Notification on Failure


      - name: Notify on failure
        if: failure()
        uses: slackapi/slack-github-action@v1
        with:
          payload: |
            {
              "text": "CAPTCHA tests failed on ${{ github.ref }}",
              "blocks": [
                {
                  "type": "section",
                  "text": {
                    "type": "mrkdwn",
                    "text": "CAPTCHA tests *failed* on `${{ github.ref }}`\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View run>"
                  }
                }
              ]
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

Troubleshooting

Issue Cause Fix
Tests skip with "CAPTCHAAI_KEY not set" Secret not configured Add secret in repo Settings
Timeout in CI but works locally Runner network latency Increase timeout to 120s
Balance check fails Key format issue Verify secret value has no whitespace
Workflow never runs Wrong branch/trigger config Check on: block in YAML

FAQ

How much does running CAPTCHA tests in CI cost?

A typical test suite solving 3-5 CAPTCHAs costs $0.01-0.03 per run. Weekly scheduled runs cost around $0.10-0.15/month.

Should I run CAPTCHA tests on every PR?

Run balance and key validation on every PR. Run full CAPTCHA solve tests on merge to main or on a weekly schedule to minimize costs.

Can I use this with other CI platforms?

Yes. The Python test code works the same in GitLab CI, CircleCI, or Jenkins. Only the workflow YAML syntax differs.



Keep your pipeline green — get CaptchaAI for CI/CD.

Full Working Code

Complete runnable examples for this article in Python, Node.js, PHP, Go, Java, C#, Ruby, Rust, Kotlin & Bash.

View on GitHub →

Discussions (0)

No comments yet.