Integrationen

Airtable + CaptchaAI: Durch die Datenbank ausgelöste CAPTCHA-Lösung

Eine Airtable-Basis wird zur Warteschlange für CAPTCHA-Aufgaben, sobald ein einziges Feld die Arbeit steuert: Steht Status auf pending, startet eine Automatisierung, übergibt Sitekey und Page-URL an CaptchaAI und schreibt das gelöste Token in denselben Datensatz zurück. Weder ein eigener Server noch ein Zwischen-Tool ist nötig – zwei JavaScript-Skripte genügen, die Airtable selbst ausführt.

Der Aufbau folgt drei Schritten: Tabelle und Trigger, Skriptaktion für den einzelnen Datensatz, Scripting-Erweiterung für den Stapellauf.

Was Airtable-Skripte leisten – und wo die Grenzen liegen

Die Rahmenbedingungen der Plattform bestimmen, wie großzügig Sie das Polling auslegen dürfen:

  • Automatisierungs-Skripte laufen serverseitig, also auch ohne geöffnete Basis – mit 30 Sekunden Laufzeit pro Ausführung.
  • Die Scripting-Erweiterung im Apps-Bereich läuft im Browser-Tab: gut für Stapelläufe, verlangt aber die geöffnete Basis.
  • Netzwerkaufrufe erfolgen über fetch; in einzelnen Skriptkontexten heißt der Aufruf remoteFetchAsync.
  • Der API-Schlüssel gehört als Eingabevariable über input.config() in die Konfiguration, nicht fest in den Code.

Das 30-Sekunden-Limit ist entscheidend, denn reCAPTCHA v2 löst CaptchaAI typischerweise in unter 60 Sekunden. Kürzen Sie das Polling im Beispielskript auf rund 20 Sekunden Wartezeit und zwei bis drei Abfragen; längere Fälle gehören in den Stapellauf.

Praxisbeispiel: Lieferantenportale im DACH-Handel

Ein norddeutscher Großhändler pflegt in Airtable ein Register mit rund 40 Lieferantenportalen: Ansprechpartner, Abrufintervall, letzte Preisliste. Der wöchentliche Abruf läuft auf einem kleinen Hetzner-Server – doch vor dem Login mehrerer Portale steht ein reCAPTCHA v2.

Statt eines zweiten Werkzeugs übernimmt die vorhandene Basis diese Aufgabe: Der Wochenlauf setzt jeden fälligen Datensatz auf pending, die Automatisierung besorgt das Token, der Abrufjob liest es aus, sobald Status auf solved steht. Wichtig dabei: Die Zugänge sind vertraglich vereinbart, und da Kontaktdaten personenbezogen sein können, gehört der Datenfluss ins Verarbeitungsverzeichnis.

Tabelle anlegen: Feldnamen müssen zum Skript passen

Legen Sie eine Tabelle namens CAPTCHA Tasks an. Die Feldnamen bleiben englisch, weil updateRecordAsync sie wörtlich adressiert – auch die Auswahlwerte des Status-Feldes samt Schreibweise:

Feld Typ Zweck
URL URL Adresse der Zielseite
Sitekey Einzeiliger Text reCAPTCHA-Sitekey der Seite
Status Einfachauswahl pending, solving, solved, failed
Token Langer Text gelöstes Token
Solved At Datum/Uhrzeit Zeitstempel der Lösung
Error Einzeiliger Text Fehlermeldung im Fehlerfall

Deutsche Beschriftungen sind möglich, solange die Bezeichner im Skript unverändert bleiben.

Schritt 1: Automatisierung mit Status-Trigger anlegen

In Airtable:

  1. Öffnen Sie den Bereich Automatisierungen
  2. Klicken Sie auf Automatisierung erstellen
  3. Vergeben Sie einen sprechenden Namen, etwa „CAPTCHA bei neuem Datensatz lösen“

Trigger einrichten

Wählen Sie als Auslöser Wenn Datensatz Bedingungen erfüllt:

  • Tabelle: CAPTCHA Tasks
  • Bedingung: Status ist pending

Der Status-Trigger ist bewusst allgemeiner als ein „neuer Datensatz“-Auslöser: Er greift auch beim manuellen Zurücksetzen eines gescheiterten Datensatzes. Ein erneuter Lauf kostet einen Klick. Drei Feinheiten sparen später Nacharbeit:

  • Zweite Bedingung Sitekey ist nicht leer – verhindert Leerläufe bei unvollständigen Zeilen
  • Eine Ansicht statt der ganzen Tabelle als Quelle, wenn nur ein Teilbestand automatisiert wird
  • Beim Zurücksetzen auch Error leeren, damit keine alte Meldung neben einem frischen Token steht

Schritt 2: Skriptaktion mit dem CaptchaAI-Aufruf

Ergänzen Sie eine Aktion → Skript ausführen:

// 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}`);
}

Das Skript folgt dem zweistufigen API-Muster: in.php nimmt die Aufgabe mit method=userrecaptcha, googlekey und pageurl entgegen und liefert eine Task-ID, res.php gibt das Token zurück. Lautet die Antwort CAPCHA_NOT_READY, ist die Aufgabe noch in Arbeit – jede andere Rückmeldung beendet den Durchlauf als echter Fehler, statt einen falschen Sitekey minutenlang weiterzupollen.

Eingabevariablen zuordnen

Ordnen Sie im linken Bereich der Skriptaktion die drei Variablen zu:

  • recordId – Datensatz-ID aus dem Trigger-Schritt
  • sitekey – Feld Sitekey des Datensatzes
  • pageurl – Feld URL des Datensatzes

Legen Sie hier auch den API-Schlüssel als Eingabevariable an: Jede Person mit Zugriff auf die Basis kann den Skripttext lesen.

Schritt 3: Stapellauf über die Scripting-Erweiterung

Für Altbestände oder den wöchentlichen Sammellauf eignet sich die Scripting-Erweiterung aus dem Apps-Bereich. Sie arbeitet alle Datensätze mit dem Status pending nacheinander ab:

// 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');

Weil die Schleife sequenziell läuft, belegt der Stapellauf genau einen Thread. Mehr Parallelität erreichen Sie, indem Sie den Bestand auf mehrere Ansichten aufteilen und je Ansicht einen Lauf starten.

Threads planen: Was der Durchsatz kostet

CaptchaAI rechnet pro gleichzeitigem Thread ab – nicht pro Lösung. Ein Thread ist eine Aufgabe in Bearbeitung; ist sie fertig, nimmt derselbe Thread die nächste an. Tageslimits oder Aufschläge nach CAPTCHA-Typ gibt es nicht. Alle Preise in US-Dollar:

  • BASIC (15 $/Monat, 5 Threads) – für einige Hundert Datensätze pro Tag, verteilt über den Tag
  • STANDARD (30 $/Monat, 15 Threads) – wenn mehrere Sammelläufe parallel starten
  • ADVANCE (90 $/Monat, 50 Threads) – für mehrere Basen und enge Zeitfenster

Bei einer Obergrenze von unter 60 Sekunden je reCAPTCHA v2 schafft ein Thread rechnerisch bis zu 60 Lösungen pro Stunde, fünf Threads also rund 300 – praktisch etwas weniger, weil Airtable zwischen Trigger und Skriptstart Zeit braucht. Entscheidend ist die Spitze, nicht die Tagesmenge.

Typische Fehlerbilder

Symptom Ursache Lösung
Automatisierung startet nicht Auswahlwert weicht ab (Schreibweise, Leerzeichen) Statuswert exakt auf pending setzen, Trigger-Bedingung prüfen
fetch is not defined In manchen Skriptkontexten trägt der Aufruf einen anderen Namen remoteFetchAsync statt fetch verwenden
Skript bricht nach 30 Sekunden ab Laufzeitlimit der Automatisierung erreicht Wartezeit erhöhen, Iterationen reduzieren oder in den Stapellauf verlagern
Datensatz-Update schlägt fehl Feldname im Skript weicht von der Tabelle ab Bezeichner inklusive Solved At und Error abgleichen
Token wird erzeugt, aber von der Zielseite abgelehnt Sitekey, Page-URL oder Sitzungskontext passen nicht Parameter erneut erfassen, erst unmittelbar vor dem Absenden lösen

Häufige Fragen

Wie lange bleibt ein gelöstes Token brauchbar?

Rund zwei Minuten. reCAPTCHA-v2-Tokens laufen nach etwa 120 Sekunden ab. Lösen Sie deshalb erst, wenn die Zielanfrage ansteht, und tragen Sie das Token direkt in das Formularfeld ein. Ein Token-Vorrat in der Airtable-Spalte ist schnell wertlos.

Läuft die Automatisierung auch ohne geöffnete Basis?

Ja. Automatisierungs-Skripte laufen auf Airtable-Servern. Die Scripting-Erweiterung dagegen führt Ihr Skript im Browser-Tab aus – wird die Basis geschlossen, bricht der Stapellauf ab.

Welche CAPTCHA-Typen deckt CaptchaAI in solchen Workflows ab?

reCAPTCHA v2 samt Invisible-, Callback- und Enterprise-Varianten, reCAPTCHA v3, Cloudflare Turnstile, Cloudflare Challenge, GeeTest v3, Bild- und Rasterbild-CAPTCHAs sowie BLS; dazu CaptchaFox (Beta), Friendly Captcha (Beta) und Lemin (Beta). Nicht unterstützt sind hCaptcha und FunCaptcha; GeeTest v4 ist als „bald verfügbar“ angekündigt. Im Skript ändert sich dafür vor allem der method-Parameter.

Wie viele Threads brauche ich für 500 Datensätze pro Tag?

Bei verteilten Einträgen genügt BASIC (15 $/Monat, 5 Threads) mit Puffer. Eng wird es, wenn Importe große Mengen auf einmal auf pending setzen – dann entscheidet die gewünschte Bearbeitungsdauer über den Plan.

Dürfen die verarbeiteten Daten überhaupt in Airtable liegen?

Das entscheidet Ihre eigene Datenschutzprüfung. Ziel-URLs und Kontaktdaten können personenbezogen sein; klären Sie Rechtsgrundlage, Auftragsverarbeitung und Speicherort, bevor eine Basis produktiv geht. Tokens sind ohnehin kurzlebig – die Spalte Token sollte regelmäßig geleert werden.

Verwandte Leitfäden

Kommentare sind für diesen Artikel deaktiviert.