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 |
Environment Variables (Recommended)
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.
Related Guides
Secure your integration — manage keys at CaptchaAI.
Discussions (0)
Join the conversation
Sign in to share your opinion.
Sign InNo comments yet.