El mismo binario debería correr en desarrollo, en staging y en producción sin recompilarse: lo único que cambia entre entornos es la configuración. Con CaptchaAI, una configuración lista para producción se apoya en tres piezas encajadas:
- Una jerarquía donde las variables de entorno mandan sobre un archivo YAML versionado.
- Un gestor de secretos que mantiene tu clave API fuera del repositorio.
- La posibilidad de ajustar parámetros como la concurrencia sin volver a desplegar.
Escribir la clave a mano y fijar los timeouts en el código sirve para un prototipo; en un servicio real acaba generando incidentes.
Jerarquía de configuración
Priority (highest → lowest):
1. Environment variables ← deployment-specific overrides
2. Config file (YAML/JSON) ← version-controlled defaults
3. Application defaults ← fallback values in code
Regla práctica: lo más cercano al operador gana. Un flag de línea de comandos pisa una variable de entorno, que pisa el archivo YAML, que pisa el valor por defecto del código. Así el código es idéntico en todos los entornos y solo se mueve la capa de configuración.
Referencia completa de parámetros
| Parámetro | Variable de entorno | Valor por defecto | Descripción |
|---|---|---|---|
| API key | CAPTCHAAI_API_KEY |
— | Obligatorio. Tu clave API de CaptchaAI |
| URL de envío | CAPTCHAAI_SUBMIT_URL |
https://ocr.captchaai.com/in.php |
Endpoint de envío de tareas |
| URL de sondeo | CAPTCHAAI_POLL_URL |
https://ocr.captchaai.com/res.php |
Endpoint para consultar el resultado |
| Intervalo de sondeo | CAPTCHAAI_POLL_INTERVAL |
5 |
Segundos entre intentos de sondeo |
| Sondeos máximos | CAPTCHAAI_MAX_POLLS |
60 |
Intentos de sondeo antes del tiempo de espera |
| Concurrencia | CAPTCHAAI_CONCURRENCY |
10 |
Máximo de tareas CAPTCHA en paralelo |
| Tiempo de espera | CAPTCHAAI_TIMEOUT |
300 |
Tiempo de espera total, en segundos |
| Proxy | CAPTCHAAI_PROXY |
— | URL del proxy para resolver el CAPTCHA |
| URL de callback | CAPTCHAAI_CALLBACK_URL |
— | URL del webhook para resultados asíncronos |
| Reintentos | CAPTCHAAI_RETRIES |
3 |
Reintentos ante fallos transitorios |
| Nivel de log | CAPTCHAAI_LOG_LEVEL |
info |
Detalle del registro |
Cargador de configuración con precedencia
El cargador aplica las tres capas en orden y valida al final para fallar rápido si falta la clave API. La misma lógica, en Python y en Node.js:
Python
import os
import yaml
from dataclasses import dataclass, field
from pathlib import Path
@dataclass
class CaptchaAIConfig:
api_key: str = ""
submit_url: str = "https://ocr.captchaai.com/in.php"
poll_url: str = "https://ocr.captchaai.com/res.php"
poll_interval: int = 5
max_polls: int = 60
concurrency: int = 10
timeout: int = 300
proxy: str = ""
callback_url: str = ""
retries: int = 3
log_level: str = "info"
@classmethod
def load(cls, config_path=None):
"""Load config: env vars override file, which overrides defaults."""
config = cls()
# Layer 2: Config file
if config_path and Path(config_path).exists():
with open(config_path) as f:
file_config = yaml.safe_load(f) or {}
for key, value in file_config.items():
if hasattr(config, key):
setattr(config, key, value)
# Layer 1: Environment variables (highest priority)
env_map = {
"CAPTCHAAI_API_KEY": "api_key",
"CAPTCHAAI_SUBMIT_URL": "submit_url",
"CAPTCHAAI_POLL_URL": "poll_url",
"CAPTCHAAI_POLL_INTERVAL": "poll_interval",
"CAPTCHAAI_MAX_POLLS": "max_polls",
"CAPTCHAAI_CONCURRENCY": "concurrency",
"CAPTCHAAI_TIMEOUT": "timeout",
"CAPTCHAAI_PROXY": "proxy",
"CAPTCHAAI_CALLBACK_URL": "callback_url",
"CAPTCHAAI_RETRIES": "retries",
"CAPTCHAAI_LOG_LEVEL": "log_level",
}
for env_key, attr_name in env_map.items():
value = os.environ.get(env_key)
if value is not None:
# Cast to correct type
current = getattr(config, attr_name)
if isinstance(current, int):
value = int(value)
setattr(config, attr_name, value)
config.validate()
return config
def validate(self):
if not self.api_key:
raise ValueError("CAPTCHAAI_API_KEY is required")
if self.poll_interval < 1:
raise ValueError("poll_interval must be >= 1")
if self.concurrency < 1:
raise ValueError("concurrency must be >= 1")
# Usage
config = CaptchaAIConfig.load("config/captchaai.yaml")
print(f"Concurrency: {config.concurrency}, Timeout: {config.timeout}s")
JavaScript
const fs = require("fs");
const yaml = require("js-yaml");
const path = require("path");
class CaptchaAIConfig {
static defaults = {
apiKey: "",
submitUrl: "https://ocr.captchaai.com/in.php",
pollUrl: "https://ocr.captchaai.com/res.php",
pollInterval: 5,
maxPolls: 60,
concurrency: 10,
timeout: 300,
proxy: "",
callbackUrl: "",
retries: 3,
logLevel: "info",
};
static envMap = {
CAPTCHAAI_API_KEY: "apiKey",
CAPTCHAAI_SUBMIT_URL: "submitUrl",
CAPTCHAAI_POLL_URL: "pollUrl",
CAPTCHAAI_POLL_INTERVAL: { key: "pollInterval", type: "int" },
CAPTCHAAI_MAX_POLLS: { key: "maxPolls", type: "int" },
CAPTCHAAI_CONCURRENCY: { key: "concurrency", type: "int" },
CAPTCHAAI_TIMEOUT: { key: "timeout", type: "int" },
CAPTCHAAI_PROXY: "proxy",
CAPTCHAAI_CALLBACK_URL: "callbackUrl",
CAPTCHAAI_RETRIES: { key: "retries", type: "int" },
CAPTCHAAI_LOG_LEVEL: "logLevel",
};
static load(configPath = null) {
let config = { ...CaptchaAIConfig.defaults };
// Layer 2: Config file
if (configPath && fs.existsSync(configPath)) {
const ext = path.extname(configPath);
const raw = fs.readFileSync(configPath, "utf8");
const fileConfig = ext === ".json" ? JSON.parse(raw) : yaml.load(raw);
config = { ...config, ...fileConfig };
}
// Layer 1: Environment variables
for (const [envKey, mapping] of Object.entries(CaptchaAIConfig.envMap)) {
const value = process.env[envKey];
if (value !== undefined) {
const attrKey = typeof mapping === "string" ? mapping : mapping.key;
const type = typeof mapping === "string" ? "string" : mapping.type;
config[attrKey] = type === "int" ? parseInt(value, 10) : value;
}
}
CaptchaAIConfig.validate(config);
return config;
}
static validate(config) {
if (!config.apiKey) throw new Error("CAPTCHAAI_API_KEY is required");
if (config.pollInterval < 1) throw new Error("pollInterval must be >= 1");
if (config.concurrency < 1) throw new Error("concurrency must be >= 1");
}
}
// Usage
const config = CaptchaAIConfig.load("config/captchaai.yaml");
console.log(`Concurrency: ${config.concurrency}, Timeout: ${config.timeout}s`);
Archivos de configuración por entorno
Un archivo base recoge los valores comunes y cada entorno añade solo lo que cambia. El patrón se reparte en tres archivos:
- Base: valores compartidos y
api_keyvacío, listo para que lo inyecte la variable de entorno. - Producción: más concurrencia, sondeo más agresivo y logs en
warning. - Staging: poca concurrencia y logs en
debugpara depurar sin gastar de más.
# config/captchaai.yaml — base
api_key: "" # Always set via env var
concurrency: 5
poll_interval: 5
retries: 3
log_level: info
# config/captchaai.production.yaml
concurrency: 20
poll_interval: 3
timeout: 180
log_level: warning
# config/captchaai.staging.yaml
concurrency: 3
poll_interval: 5
timeout: 300
log_level: debug
Un caso típico: una agencia en Ciudad de México o Madrid con un pipeline de scraping para varios clientes corre staging con concurrency: 3 y logs en debug, y sube producción a concurrency: 20 con logs en warning. Ajusta CAPTCHAAI_CONCURRENCY a los threads de tu plan: pedir 50 tareas en paralelo con un plan STANDARD ($30/mes, 15 threads) no aporta nada, porque el rendimiento lo limitan los threads contratados. Para más paralelismo, sube a ADVANCE ($90/mes, 50 threads).
Nota de costos: CaptchaAI cobra en USD, por thread y al mes, con resoluciones ilimitadas por thread. Ese costo fijo facilita presupuestar cuando facturas a clientes en monedas volátiles.
Gestión de secretos
Nunca guardes claves API en archivos de configuración ni en el control de versiones. Cada método mantiene el secreto fuera del código, con distinto equilibrio entre simplicidad y rotación:
| Método | Ideal para | Ejemplo |
|---|---|---|
| Variables de entorno | Contenedores, CI/CD | export CAPTCHAAI_API_KEY=abc123 |
| AWS Secrets Manager | Infraestructura en AWS | Recuperar al arranque; rotación automática |
| HashiCorp Vault | Multinube, on-prem | Secretos dinámicos con TTL |
| Docker secrets | Docker Swarm / Compose | Montado en /run/secrets/ |
Archivo .env (solo desarrollo) |
Desarrollo local | Librería dotenv; añádelo a .gitignore |
En contenedores no hornees la clave en la imagen: inyéctala por variable de entorno o móntala desde el gestor de secretos en el arranque.
Ejemplo con Docker Compose
services:
captcha-worker:
image: captcha-worker:latest
environment:
- CAPTCHAAI_API_KEY=${CAPTCHAAI_API_KEY}
- CAPTCHAAI_CONCURRENCY=15
- CAPTCHAAI_LOG_LEVEL=warning
env_file:
- .env.production
Feature flags: activar capacidades sin redeploy
Los feature flags encienden o apagan comportamientos leyendo una variable de entorno, sin tocar el código ni volver a desplegar. Son ideales para:
- Activar el modo callback de forma gradual antes de generalizarlo.
- Encender o apagar el uso de proxy por entorno.
- Subir un tope de concurrencia durante un pico de tráfico y bajarlo después.
class FeatureFlags:
def __init__(self):
self.flags = {
"use_callback": os.environ.get("FF_USE_CALLBACK", "false") == "true",
"enable_proxy": os.environ.get("FF_ENABLE_PROXY", "true") == "true",
"max_concurrent": int(os.environ.get("FF_MAX_CONCURRENT", "10")),
}
def is_enabled(self, flag):
return self.flags.get(flag, False)
def get(self, flag, default=None):
return self.flags.get(flag, default)
Diagnóstico de problemas frecuentes
Cuando la configuración no se comporta como esperas, casi siempre es una de estas cuatro causas.
| Problema | Causa | Solución |
|---|---|---|
| La clave API no se carga | Falta la variable de entorno o el nombre está mal escrito | Revisa echo $CAPTCHAAI_API_KEY; comprueba la ortografía |
| El archivo de configuración se ignora | Ruta incorrecta o falta la librería YAML | Verifica que el archivo exista; instala pyyaml / js-yaml |
| Producción usa la configuración de desarrollo | No se aplicó la sobrescritura por entorno | Revisa la precedencia de las variables; comprueba NODE_ENV / APP_ENV |
| Secretos visibles en los logs | El volcado de configuración incluye la clave API | Enmascara los campos sensibles en la salida de logs |
Preguntas frecuentes
¿Dónde debo guardar la clave API: en el YAML o en una variable de entorno?
- En una variable de entorno, nunca en el YAML versionado.
- Deja
api_key: ""enconfig/captchaai.yamly queCAPTCHAAI_API_KEYinyecte el valor real en cada entorno. - Así el mismo repositorio sirve para staging y producción sin exponer el secreto en el control de versiones.
¿Cómo evito que la clave API aparezca en los logs?
El riesgo casi siempre viene de un volcado de configuración. Tres reglas lo evitan:
- Nunca hagas un
print(config)que incluyaapi_key. - Enmascara los campos sensibles antes de cualquier dump de depuración.
- Sustituye la clave por
***en la representación del objeto.
¿La concurrencia puede superar los threads de mi plan?
No conviene. El rendimiento lo limitan los threads contratados, así que fijar CAPTCHAAI_CONCURRENCY por encima solo genera tareas en espera. Ajusta la concurrencia al plan —50 con ADVANCE ($90/mes, 50 threads)— y sube de plan cuando el volumen lo pida.
¿Puedo cambiar la concurrencia sin reiniciar el worker?
Sí. Lee la configuración en cada lote de tareas, no solo al arranque. Así puedes ajustar CAPTCHAAI_CONCURRENCY, actualizar la variable y enviar una señal de recarga sin detener el proceso.
Artículos relacionados
- Resolución de CAPTCHA para pruebas de QA autorizadas
- Gestión de claves API para equipos de CaptchaAI
Prepara tu configuración de producción
Deja tu integración lista para producción: crea tu cuenta en captchaai.com y parte de las plantillas de configuración de arriba para separar entornos desde el primer día.
Guías relacionadas: