Explainers

CAPTCHA Solving API Key Security: Storage and Rotation

Your CaptchaAI API key controls access to your balance. Exposing it means someone else can use your funds. Here's how to keep it safe.


Common Mistakes

Mistake Risk Impact
Hardcoded in source code Key exposed in Git Balance drained
Committed to public repo Key found by bots Immediate abuse
Shared in chat/email Forwarded or leaked Unauthorized use
Logged in plaintext Visible in log files Exposure to log readers
Same key for dev and prod Dev env compromise Prod balance affected

import os


def get_api_key():
    """Load API key from environment variable."""
    key = os.environ.get("CAPTCHAAI_API_KEY")
    if not key:
        raise RuntimeError(
            "CAPTCHAAI_API_KEY not set. "
            "Set it via: export CAPTCHAAI_API_KEY=your_key"
        )
    return key


# Usage
api_key = get_api_key()

Set the environment variable:

# Linux/macOS
export CAPTCHAAI_API_KEY="your_api_key_here"

# Windows PowerShell
$env:CAPTCHAAI_API_KEY = "your_api_key_here"

# Windows CMD
set CAPTCHAAI_API_KEY=your_api_key_here

.env File with python-dotenv

# .env file (add to .gitignore!)
# CAPTCHAAI_API_KEY=your_api_key_here

from dotenv import load_dotenv
import os

load_dotenv()  # Load from .env file

api_key = os.environ["CAPTCHAAI_API_KEY"]

Critical: Add .env to .gitignore:

# .gitignore
.env
.env.local
.env.production

Configuration File Pattern

import json
import os


def load_config(config_path="config.json"):
    """Load API key from config file outside repo."""
    # Use home directory for config
    if not os.path.isabs(config_path):
        config_path = os.path.join(os.path.expanduser("~"), ".captchaai", config_path)

    if not os.path.exists(config_path):
        raise FileNotFoundError(
            f"Config not found: {config_path}\n"
            f"Create it with: {{'api_key': 'your_key'}}"
        )

    with open(config_path, "r") as f:
        config = json.load(f)

    return config["api_key"]

Prevent Accidental Logging

import logging


class SecureApiKey:
    """API key wrapper that prevents accidental logging."""

    def __init__(self, key):
        self._key = key

    @property
    def value(self):
        return self._key

    def __str__(self):
        """Show only last 4 chars in logs."""
        return f"***{self._key[-4:]}"

    def __repr__(self):
        return f"SecureApiKey(***{self._key[-4:]})"


# Usage
api_key = SecureApiKey(os.environ["CAPTCHAAI_API_KEY"])

logging.info(f"Using API key: {api_key}")  # Logs: "Using API key: ***ab12"

# Use .value when actually making API calls
requests.post("https://ocr.captchaai.com/in.php", data={
    "key": api_key.value,  # Actual key value
    # ...
})

Git Pre-Commit Hook

Prevent committing API keys:

#!/usr/bin/env python3
"""Git pre-commit hook to detect API keys."""
# Save as .git/hooks/pre-commit and chmod +x

import subprocess
import sys
import re

# Patterns that might be API keys
KEY_PATTERNS = [
    r'CAPTCHAAI_API_KEY\s*=\s*["\'][a-f0-9]{32}',
    r'key["\']:\s*["\'][a-f0-9]{32}',
    r'"key":\s*"[a-f0-9]{32}"',
]

def check_staged_files():
    result = subprocess.run(
        ["git", "diff", "--cached", "--name-only"],
        capture_output=True, text=True,
    )
    files = result.stdout.strip().split("\n")

    for filepath in files:
        if not filepath or filepath.endswith((".pyc", ".png", ".jpg")):
            continue
        try:
            diff = subprocess.run(
                ["git", "diff", "--cached", filepath],
                capture_output=True, text=True,
            )
            for pattern in KEY_PATTERNS:
                if re.search(pattern, diff.stdout, re.IGNORECASE):
                    print(f"BLOCKED: Possible API key in {filepath}")
                    print("Use environment variables instead of hardcoding keys.")
                    return 1
        except Exception:
            continue
    return 0

sys.exit(check_staged_files())

Key Rotation Workflow

import os
import time


class KeyRotator:
    """Support multiple API keys for rotation."""

    def __init__(self):
        self.keys = self._load_keys()
        self._current_index = 0

    def _load_keys(self):
        """Load API keys from environment."""
        keys = []
        # Primary key
        primary = os.environ.get("CAPTCHAAI_API_KEY")
        if primary:
            keys.append(primary)

        # Additional keys: CAPTCHAAI_API_KEY_2, _3, etc.
        for i in range(2, 10):
            key = os.environ.get(f"CAPTCHAAI_API_KEY_{i}")
            if key:
                keys.append(key)

        if not keys:
            raise RuntimeError("No API keys configured")
        return keys

    def get_key(self):
        """Get current API key."""
        return self.keys[self._current_index]

    def rotate(self):
        """Switch to next key."""
        self._current_index = (self._current_index + 1) % len(self.keys)

    def on_error(self, error):
        """Rotate key on balance or auth errors."""
        if "ZERO_BALANCE" in str(error) or "WRONG_USER_KEY" in str(error):
            self.rotate()

Security Checklist

Check Status
API key not in source code Required
.env file in .gitignore Required
Key loaded from environment Required
Logging masks key value Recommended
Different keys for dev/prod Recommended
Pre-commit hook installed Recommended
Key rotation plan documented Optional

FAQ

What if my key is exposed?

Immediately generate a new API key from CaptchaAI dashboard. The old key remains active until you regenerate, so act fast to prevent unauthorized usage.

Should I use different keys for different projects?

Yes, if possible. Separate keys let you track usage per project and limit blast radius if one key is exposed.

Can I restrict my key to specific IPs?

Yes. CaptchaAI supports IP whitelisting. See the IP Whitelisting guide for setup instructions.



Secure your integration — manage keys at CaptchaAI.

Discussions (0)

No comments yet.