Se uma automação no Airtable trava porque o formulário exige reCAPTCHA, dá para resolver sem sair da base: uma Automation chama a API da CaptchaAI e grava o token de volta no registro. Este guia mostra as duas formas: gatilho por registro (Automation) e lote (Scripting extension).
Quando vale a pena automatizar o CAPTCHA no Airtable
Compensa quando a base já concentra as URLs a processar — por exemplo, editais públicos ou portais de parceiros com login antes da coleta. A Automation detecta o registro, o script envia os parâmetros, a API devolve o token e o registro é atualizado sozinho.
Antes de começar
- Conta na CaptchaAI com chave de API válida
- Base no Airtable com permissão para criar Automations e, para lotes, acesso à aba Apps
- Tabela CAPTCHA Tasks configurada conforme a seção abaixo
Monte a tabela de tarefas
Crie uma tabela CAPTCHA Tasks com estes campos — os nomes precisam bater com o script da próxima etapa:
| Nome do campo | Tipo | Finalidade |
|---|---|---|
| URL | URL | URL da página de destino |
| Sitekey | Texto de linha única | sitekey do reCAPTCHA |
| Status | Seleção única | valores: pending, solving, solved, failed |
| Token | Texto longo | Token do CAPTCHA resolvido |
| Solved At | Data/hora | Data e hora em que o CAPTCHA foi resolvido |
| Error | Texto de linha única | Mensagem de erro, se a resolução falhar |
Passo 1: crie a automação no Airtable
- Abra a guia Automações
- Clique em Criar automação
- Dê um nome, por exemplo "Resolver CAPTCHA no novo registro"
Gatilho
Escolha Quando o registro corresponde às condições, aponte para CAPTCHA Tasks e defina Status é pending. Cobre registros novos e reenvios: para reprocessar um item que falhou, volte o Status para pending.
Passo 2: envie o CAPTCHA para a CaptchaAI
Adicione uma ação → Executar um script e cole o código abaixo. Em resumo, o script:
- muda o Status para solving assim que começa
- envia a tarefa ao endpoint
in.phpe consulta o resultado emres.php - grava o token e o horário de resolução de volta no registro
// Airtable Automation Script — Solve CAPTCHA via CaptchaAI
// Input configuration (set in the left panel):
// - recordId: Record ID from trigger
// - sitekey: Sitekey field from trigger
// - pageurl: URL field from trigger
const config = input.config();
const recordId = config.recordId;
const sitekey = config.sitekey;
const pageurl = config.pageurl;
const API_KEY = 'YOUR_API_KEY'; // Use input.config() for security
// Update status to "solving"
const table = base.getTable('CAPTCHA Tasks');
await table.updateRecordAsync(recordId, {
'Status': { name: 'solving' },
});
try {
// Step 1: Submit task to CaptchaAI
const submitUrl = `https://ocr.captchaai.com/in.php?key=${API_KEY}&method=userrecaptcha&googlekey=${encodeURIComponent(sitekey)}&pageurl=${encodeURIComponent(pageurl)}&json=1`;
const submitResponse = await fetch(submitUrl);
const submitResult = await submitResponse.json();
if (submitResult.status !== 1) {
throw new Error(`Submit failed: ${submitResult.request}`);
}
const taskId = submitResult.request;
console.log(`Task submitted: ${taskId}`);
// Step 2: Poll for result (wait 15 seconds first)
await new Promise(resolve => setTimeout(resolve, 15000));
let token = null;
for (let i = 0; i < 20; i++) {
const pollUrl = `https://ocr.captchaai.com/res.php?key=${API_KEY}&action=get&id=${taskId}&json=1`;
const pollResponse = await fetch(pollUrl);
const pollResult = await pollResponse.json();
if (pollResult.status === 1) {
token = pollResult.request;
break;
}
if (pollResult.request !== 'CAPCHA_NOT_READY') {
throw new Error(`Solve failed: ${pollResult.request}`);
}
await new Promise(resolve => setTimeout(resolve, 5000));
}
if (!token) {
throw new Error('Polling timeout — CAPTCHA not solved in time');
}
// Step 3: Update record with solved token
await table.updateRecordAsync(recordId, {
'Status': { name: 'solved' },
'Token': token,
'Solved At': new Date().toISOString(),
'Error': '',
});
console.log(`CAPTCHA solved for record ${recordId}`);
} catch (error) {
// Update record with error
await table.updateRecordAsync(recordId, {
'Status': { name: 'failed' },
'Error': error.message,
});
console.error(`Failed: ${error.message}`);
}
Configure as variáveis do script
| Variável | Origem no gatilho |
|---|---|
recordId |
ID do registro |
sitekey |
campo Sitekey |
pageurl |
campo URL |
O mesmo padrão resolve imagem (method: base64) e reCAPTCHA v3, já que userrecaptcha cobre v2, v2 Enterprise, v3 e v3 Enterprise.
Passo 3: processe o lote inteiro com a Scripting extension
Para dar conta de muitos registros de uma vez, use a Scripting extension do Airtable, no painel Apps. A lógica é a mesma do script de automação, com duas diferenças:
- percorre todos os registros com Status pending de uma vez, em vez de reagir a um só
- roda sob demanda, quando você abre o painel Apps e executa o script
// Batch CAPTCHA Solver — Airtable Scripting Extension
const API_KEY = 'YOUR_API_KEY';
const table = base.getTable('CAPTCHA Tasks');
// Get all pending records
const query = await table.selectRecordsAsync({
fields: ['URL', 'Sitekey', 'Status'],
});
const pendingRecords = query.records.filter(
r => r.getCellValueAsString('Status') === 'pending'
);
output.text(`Found ${pendingRecords.length} pending CAPTCHAs`);
for (const record of pendingRecords) {
const sitekey = record.getCellValueAsString('Sitekey');
const pageurl = record.getCellValueAsString('URL');
if (!sitekey || !pageurl) {
output.text(`Skipping ${record.id} — missing sitekey or URL`);
continue;
}
output.text(`Solving for: ${pageurl}`);
await table.updateRecordAsync(record.id, {
'Status': { name: 'solving' },
});
try {
// Submit
const submitResp = await fetch(
`https://ocr.captchaai.com/in.php?key=${API_KEY}&method=userrecaptcha&googlekey=${encodeURIComponent(sitekey)}&pageurl=${encodeURIComponent(pageurl)}&json=1`
);
const submitData = await submitResp.json();
if (submitData.status !== 1) throw new Error(submitData.request);
// Poll
await new Promise(r => setTimeout(r, 15000));
let token = null;
for (let i = 0; i < 20; i++) {
const pollResp = await fetch(
`https://ocr.captchaai.com/res.php?key=${API_KEY}&action=get&id=${submitData.request}&json=1`
);
const pollData = await pollResp.json();
if (pollData.status === 1) { token = pollData.request; break; }
if (pollData.request !== 'CAPCHA_NOT_READY') throw new Error(pollData.request);
await new Promise(r => setTimeout(r, 5000));
}
if (!token) throw new Error('Timeout');
await table.updateRecordAsync(record.id, {
'Status': { name: 'solved' },
'Token': token,
'Solved At': new Date().toISOString(),
});
output.text(`✓ Solved: ${pageurl}`);
} catch (e) {
await table.updateRecordAsync(record.id, {
'Status': { name: 'failed' },
'Error': e.message,
});
output.text(`✗ Failed: ${e.message}`);
}
}
output.text('Batch processing complete');
Automation nativa ou Scripting extension: qual escolher
| Critério | Automation (gatilho por registro) | Scripting extension (lote) |
|---|---|---|
| Quando roda | A cada novo registro com Status pending | Sob demanda, quando você executa o script |
| Volume por execução | Um registro por vez | Todos os registros pending da tabela |
| Onde configurar | Aba Automações | Painel Apps |
| Melhor para | Fluxo contínuo, poucos registros por vez | Reprocessar filas grandes de uma só vez |
Perguntas frequentes
A CaptchaAI cobra por solve ou por thread?
Por thread simultânea, não por solve:
- BASIC: US$ 15/mês, 5 threads em paralelo
- Solves ilimitados dentro de cada thread contratada
Esse fluxo funciona com reCAPTCHA v3, ou só v2?
Sim, funciona com os dois: userrecaptcha cobre reCAPTCHA v2, v2 Enterprise, v3 e v3 Enterprise. Só troque a sitekey salva no campo Sitekey.
Como guardo a chave de API sem deixá-la exposta no script?
Use uma variável de entrada no painel da ação de script — nunca escreva a chave no código. Na Scripting extension, peça a chave com input.textAsync().
Os registros salvos no Airtable entram nas regras da LGPD?
Se a tabela guardar dados pessoais junto com o token, sim. Antes de publicar:
- defina prazo de retenção para os registros
- restrinja o acesso ao painel a quem precisa dele
Não é orientação jurídica; em casos sensíveis, envolva o compliance.
Erros comuns e como corrigir
| Problema | Causa | Correção |
|---|---|---|
| A automação não dispara | Registro não bate com a condição Status = pending | Confira se o Status é exatamente pending |
fetch is not defined |
Airtable Scripting às vezes exige remoteFetchAsync |
Troque fetch por remoteFetchAsync |
| Script estourou o tempo limite | Scripts de automação têm limite de 30 s | Reduza as iterações de polling e aumente a espera inicial para 20 s |
| A atualização do registro falha | Nome de campo não bate com a tabela | Confirme se os nomes em updateRecordAsync batem com a tabela |
| Chave de API exposta no script | Chave de API escrita direto no código | Use input.config() com uma variável de entrada secreta |
Artigos relacionados
Veja também como resolver o callback do reCAPTCHA v2 pela API, reCAPTCHA v2 e Turnstile no mesmo site e como funciona o callback do reCAPTCHA v2.
Próximos passos
Pegue sua chave de API da CaptchaAI e configure a automação ainda hoje. O mesmo padrão vale para o Script do Google Apps, o Zapier e o Make (Integromat).