Quem resolveu qual CAPTCHA, para qual site e a que custo? Quando o time de compliance faz essa pergunta, quem responde é o log de auditoria — não a memória de quem rodou o script. Este guia mostra como registrar cada chamada de resolução feita à CaptchaAI em um formato estruturado, útil tanto para depuração quanto para uma auditoria formal (LGPD incluída, quando há dado pessoal envolvido). Você vai encontrar exemplos completos em Python e Node.js, uma tabela de retenção por volume e um conjunto de FAQs sobre o que registrar — e o que nunca deve entrar no log.
Quais dados entram no log de auditoria de CAPTCHA
Cada resolução deve gerar um registro com estes campos:
| Campo | Para que serve | Exemplo |
|---|---|---|
timestamp |
Quando a requisição foi feita | 2026-04-04T14:30:00Z |
request_id |
Identificador único dessa resolução | uuid4() |
captcha_type |
Método CAPTCHA usado | userrecaptcha |
target_site |
URL da página onde o CAPTCHA apareceu | https://staging.example.com/qa-login |
task_id |
ID da tarefa na CaptchaAI | 73829451 |
status |
Resultado final | solved, failed, timeout |
solve_time_ms |
Tempo entre o envio e o resultado | 18432 |
error_code |
Código de erro, se houver falha | ERROR_CAPTCHA_UNSOLVABLE |
initiator |
Quem ou o que disparou a resolução | scraper-job-42 |
cost |
Custo estimado da chamada | 0.003 |
Nunca registre:
- a chave de API;
- o token do CAPTCHA (expira em minutos e perde valor probatório);
- dado pessoal identificável vindo do site de destino — se a URL em
target_sitecostuma carregar informação sensível na query string, mascare-a antes de gravar.
LGPD e outras exigências de conformidade
A LGPD entra em cena sempre que o log toca, direta ou indiretamente, em dado pessoal. Como a orientação acima já exclui dado pessoal do site de destino, o log tende a ficar fora do escopo mais sensível da lei — mas confirme com o time jurídico se o volume ou o tipo de operação exige alguma adequação formal. Fora do Brasil, SOC 2, GDPR e HIPAA pedem o mesmo tipo de evidência (quem fez o quê, quando, com que resultado): a mesma implementação atende aos dois cenários, mudando apenas o prazo de retenção e quem tem acesso de leitura ao arquivo.
Como registrar cada resolução em Python
Trate o log de auditoria como um canal separado dos logs de aplicação: um RotatingFileHandler dedicado evita que ruído de debug se misture ao rastro que um auditor vai pedir depois.
# audit_solver.py
import os
import uuid
import time
import json
import logging
from datetime import datetime, timezone
import requests
API_KEY = os.environ.get("CAPTCHAAI_KEY", "YOUR_API_KEY")
# Configure audit logger — separate from application logs
audit_logger = logging.getLogger("captcha_audit")
audit_logger.setLevel(logging.INFO)
# File handler with rotation
from logging.handlers import RotatingFileHandler
handler = RotatingFileHandler(
"captcha_audit.jsonl",
maxBytes=50_000_000, # 50 MB per file
backupCount=10,
)
handler.setFormatter(logging.Formatter("%(message)s"))
audit_logger.addHandler(handler)
def log_audit(record):
"""Write a structured audit record."""
audit_logger.info(json.dumps(record, default=str))
def solve_with_audit(sitekey, pageurl, captcha_type="userrecaptcha",
initiator="unknown"):
"""Solve a CAPTCHA with full audit logging."""
request_id = str(uuid.uuid4())
start = time.time()
audit_record = {
"request_id": request_id,
"timestamp": datetime.now(timezone.utc).isoformat(),
"captcha_type": captcha_type,
"target_site": pageurl,
"initiator": initiator,
"status": "submitted",
}
session = requests.Session()
try:
# Submit
resp = session.get("https://ocr.captchaai.com/in.php", params={
"key": API_KEY,
"method": captcha_type,
"googlekey": sitekey,
"pageurl": pageurl,
"json": "1",
})
result = resp.json()
if result.get("status") != 1:
audit_record.update({
"status": "submit_failed",
"error_code": result.get("request"),
"solve_time_ms": int((time.time() - start) * 1000),
})
log_audit(audit_record)
return None
task_id = result["request"]
audit_record["task_id"] = task_id
# Poll
time.sleep(15)
for _ in range(25):
poll = session.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get",
"id": task_id, "json": "1",
})
poll_result = poll.json()
if poll_result.get("status") == 1:
solve_time = int((time.time() - start) * 1000)
audit_record.update({
"status": "solved",
"solve_time_ms": solve_time,
"cost_estimate": 0.003, # Adjust per your rate
})
log_audit(audit_record)
return poll_result["request"]
if poll_result.get("request") != "CAPCHA_NOT_READY":
audit_record.update({
"status": "failed",
"error_code": poll_result.get("request"),
"solve_time_ms": int((time.time() - start) * 1000),
})
log_audit(audit_record)
return None
time.sleep(5)
audit_record.update({
"status": "timeout",
"solve_time_ms": int((time.time() - start) * 1000),
})
log_audit(audit_record)
return None
except Exception as e:
audit_record.update({
"status": "error",
"error_code": str(e)[:200],
"solve_time_ms": int((time.time() - start) * 1000),
})
log_audit(audit_record)
raise
# Usage
token = solve_with_audit(
sitekey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
pageurl="https://www.google.com/recaptcha/api2/demo",
initiator="price-scraper-v2",
)
Uma resolução bem-sucedida vira uma linha JSON como esta — fácil de filtrar com grep/jq e de enviar a qualquer agregador sem transformação prévia:
{"request_id":"a1b2c3d4-...","timestamp":"2026-04-04T14:30:00+00:00","captcha_type":"userrecaptcha","target_site":"https://www.google.com/recaptcha/api2/demo","initiator":"price-scraper-v2","status":"solved","task_id":"73829451","solve_time_ms":18432,"cost_estimate":0.003}
Como registrar em Node.js
A mesma lógica em Node.js: o registro só é gravado depois que o resultado (sucesso, falha ou timeout) é conhecido, então o log nunca fica com um estado "pendente" preso no meio do caminho.
// audit_solver.js
const fs = require('fs');
const { v4: uuidv4 } = require('uuid');
const axios = require('axios');
const API_KEY = process.env.CAPTCHAAI_KEY || 'YOUR_API_KEY';
const AUDIT_FILE = 'captcha_audit.jsonl';
function logAudit(record) {
fs.appendFileSync(AUDIT_FILE, JSON.stringify(record) + '\n');
}
async function solveWithAudit(sitekey, pageurl, initiator = 'unknown') {
const requestId = uuidv4();
const start = Date.now();
const record = {
request_id: requestId,
timestamp: new Date().toISOString(),
captcha_type: 'userrecaptcha',
target_site: pageurl,
initiator,
status: 'submitted',
};
try {
const submit = await axios.get('https://ocr.captchaai.com/in.php', {
params: {
key: API_KEY, method: 'userrecaptcha',
googlekey: sitekey, pageurl, json: '1',
},
});
if (submit.data.status !== 1) {
record.status = 'submit_failed';
record.error_code = submit.data.request;
record.solve_time_ms = Date.now() - start;
logAudit(record);
return null;
}
record.task_id = submit.data.request;
await new Promise(r => setTimeout(r, 15000));
for (let i = 0; i < 25; i++) {
const poll = await axios.get('https://ocr.captchaai.com/res.php', {
params: { key: API_KEY, action: 'get', id: submit.data.request, json: '1' },
});
if (poll.data.status === 1) {
record.status = 'solved';
record.solve_time_ms = Date.now() - start;
record.cost_estimate = 0.003;
logAudit(record);
return poll.data.request;
}
if (poll.data.request !== 'CAPCHA_NOT_READY') {
record.status = 'failed';
record.error_code = poll.data.request;
record.solve_time_ms = Date.now() - start;
logAudit(record);
return null;
}
await new Promise(r => setTimeout(r, 5000));
}
record.status = 'timeout';
record.solve_time_ms = Date.now() - start;
logAudit(record);
return null;
} catch (e) {
record.status = 'error';
record.error_code = e.message.slice(0, 200);
record.solve_time_ms = Date.now() - start;
logAudit(record);
throw e;
}
}
Como consultar os logs de auditoria
Com o arquivo JSONL em mãos, um resumo diário é só percorrer as linhas e agregar por status. Rode este script como tarefa agendada (cron, GitHub Actions) para ter um panorama sem abrir o arquivo manualmente:
import json
from collections import Counter
from datetime import date
def daily_summary(log_file, target_date=None):
"""Generate a daily summary from audit logs."""
target = target_date or date.today().isoformat()
statuses = Counter()
total_cost = 0
solve_times = []
with open(log_file) as f:
for line in f:
record = json.loads(line)
if record["timestamp"].startswith(target):
statuses[record["status"]] += 1
total_cost += record.get("cost_estimate", 0)
if record.get("solve_time_ms"):
solve_times.append(record["solve_time_ms"])
print(f"Date: {target}")
print(f"Total requests: {sum(statuses.values())}")
print(f"Statuses: {dict(statuses)}")
print(f"Estimated cost: ${total_cost:.2f}")
if solve_times:
print(f"Median solve time: {sorted(solve_times)[len(solve_times)//2]}ms")
daily_summary("captcha_audit.jsonl")
Quanto tempo guardar e onde armazenar os registros
O volume diário determina se um arquivo local com rotação já basta ou se vale migrar para um agregador dedicado:
| Volume | Tamanho do registro diário | Armazenamento mensal | Recomendação |
|---|---|---|---|
| 100 resoluções/dia | ~30 KB | ~1 MB | Arquivo local |
| 1.000 resoluções/dia | ~300 KB | ~10 MB | Arquivo local + rotação |
| 10.000 resoluções/dia | ~3 MB | ~100 MB | Enviar para um agregador de log |
| 100.000 resoluções/dia | ~30 MB | ~1 GB | Log centralizado (ELK, Datadog) |
Noventa dias costuma bastar para auditoria operacional; para exigência regulatória específica, confirme o prazo antes de configurar o backupCount. Se os workers rodam em nuvem, hospedar o log na mesma região (ex.: sa-east-1, São Paulo) reduz a latência de gravação.
Problemas comuns no log de auditoria
| Problema | Causa provável | Como corrigir |
|---|---|---|
| Arquivo de log crescendo demais | Rotação não configurada | Use RotatingFileHandler ou logrotate |
| Registros de auditoria faltando | Exceção lançada antes do log ser gravado | Grave o log dentro de um bloco finally |
| Gravação lenta em alto volume | I/O de arquivo síncrono | Use escrita assíncrona ou buffer |
| Timestamps inconsistentes | Desvio do relógio do servidor | Sincronize com NTP; grave sempre em UTC |
Perguntas frequentes
Log de auditoria substitui o log de aplicação?
Não. O log de aplicação registra o que o sistema fez internamente; o de auditoria registra o que aconteceu em cada resolução — request_id, custo, resultado. São canais com públicos e retenções diferentes.
Por quanto tempo preciso guardar os registros para atender à LGPD ou a outras normas?
Não há um número único: depende da base legal e do setor. Como ponto de partida, 90 dias cobre a maioria dos casos de debug; para exigência regulatória específica, confirme o prazo com o time de compliance.
Como exporto esses logs para uma ferramenta externa como Datadog ou ELK?
O JSONL já é o formato de entrada esperado pela maioria dos agregadores — aponte o agente de coleta (Filebeat, Fluent Bit, o agente do Datadog) para o arquivo captcha_audit.jsonl. Não é preciso reescrever a lógica de log.
O que aparece no log se a resolução falhar no meio do processo?
O status muda para failed ou timeout e o error_code grava o motivo. O solve_time_ms continua sendo calculado até o ponto da falha — mostra se o problema foi rápido (parâmetro errado) ou lento (timeout de polling).
Preciso de um banco de dados dedicado para guardar os logs?
Não, pelo menos não para começar. Um arquivo JSONL com rotação atende volumes de alguns milhares de resoluções por dia. Migre para um banco ou agregador quando o volume justificar a complexidade extra.
Artigos relacionados
Próximas etapas
Adicione rastreabilidade a cada resolução de CAPTCHA — crie sua chave de API da CaptchaAI e comece a gravar o primeiro log ainda hoje.
Guias relacionados:
- Painel de monitoramento de uso
- Registro estruturado das operações de CAPTCHA
- Verificação de saldo e recarga automática