API-Tutorials

Lösen Sie BLS CAPTCHA mit Node.js und CaptchaAI

Ein BLS CAPTCHA ist aus Node.js in vier Schritten erledigt: Rasterbilder einsammeln, sie als Base64 an in.php übermitteln, das Ergebnis über res.php abfragen und die zurückgegebenen Zellen im Browser anklicken. Die Bilderkennung übernimmt die CaptchaAI-API, Ihr Skript kümmert sich nur um Ein- und Ausgabe.

Der Unterschied zu einer reCAPTCHA-Integration: Sie übergeben keinen Sitekey und keine Page-URL, sondern neun einzelne Bilder plus einen numerischen Anweisungscode. Wer die Terminportale von Visa-Dienstleistern kennt, kennt dieses Raster. Der folgende Ablauf setzt voraus, dass Sie die betreffende Anwendung selbst betreiben oder für Tests ausdrücklich autorisiert sind.


So ist das BLS-Raster aufgebaut

Neun Kacheln, durchnummeriert von links oben nach rechts unten:

1 | 2 | 3
---------
4 | 5 | 6
---------
7 | 8 | 9

Dazu kommt ein numerischer Anweisungscode – etwa „664“ –, der beschreibt, welche Kacheln gesucht sind. CaptchaAI wertet Bilder und Code gemeinsam aus und liefert die Indizes der passenden Zellen als Array zurück; die Zuordnung müssen Sie also nicht selbst interpretieren.

Ein Detail, das in eigenen Portierungen regelmäßig für Fehler sorgt: Die API zählt die Zellen ab 1, das JavaScript-Array im Browser beginnt bei 0. Genau deshalb steht in Schritt 4 ein - 1 im Zugriff auf die Rasterelemente.


Was Sie vorher brauchen

Voraussetzung Wert
CaptchaAI API-Schlüssel Aus dem Konto auf captchaai.com
Node.js 14+
Bibliothek axios (npm install axios)
Browser-Schicht Puppeteer (Playwright funktioniert genauso)

Abgerechnet wird bei CaptchaAI pro Thread, nicht pro Lösung. Ein Thread ist eine laufende CAPTCHA-Abfrage; innerhalb des Abrechnungsmonats gibt es keine Begrenzung der Lösungen pro Thread und keine Aufschläge nach CAPTCHA-Typ. Für ein einzelnes Skript wie dieses genügt BASIC (15 $/Monat, 5 Threads); wer mehrere Formulare parallel abarbeitet, plant eher mit STANDARD (30 $/Monat, 15 Threads). Alle Preise sind in US-Dollar angegeben.


Schritt 1: BLS-Rasterbilder und Anweisungscode auslesen

Puppeteer öffnet das Formular, liest den Anweisungscode aus dem DOM und sammelt die neun Kachel-Quellen ein. Liegt ein Bild bereits als data:-URI vor, wandert es unverändert in die Liste; andernfalls lädt axios es als Buffer und kodiert es nach Base64.

const axios = require('axios');
const puppeteer = require('puppeteer');

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com/bls-form');

// Get instruction code
const instruction = await page.$eval('.bls-instruction', (el) => el.textContent.trim());

// Get all 9 cell image URLs and convert to base64
const cellImages = await page.$$eval('.bls-grid img', (imgs) =>
  imgs.map((img) => img.src)
);

const images = [];
for (const src of cellImages) {
  if (src.startsWith('data:')) {
    images.push(src);
  } else {
    const { data } = await axios.get(src, { responseType: 'arraybuffer' });
    const b64 = Buffer.from(data).toString('base64');
    images.push(`data:image/png;base64,${b64}`);
  }
}

Prüfen Sie an dieser Stelle, ob wirklich neun Einträge im Array stehen. Nachgeladene Kacheln sind der Klassiker: Warten Sie vor dem Einsammeln explizit auf den Raster-Selektor – das erspart Ihnen später die Fehlersuche an einem API-Fehlercode.


Schritt 2: Aufgabe an die CaptchaAI-API übermitteln

Die Übermittlung ist ein POST an in.php. Die Methode heißt bls, der Anweisungscode gehört in das Feld instructions, und die Bilder werden einzeln als image_base64_1 bis image_base64_9 angehängt.

const API_KEY = 'YOUR_API_KEY';
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

const params = new URLSearchParams({
  key: API_KEY,
  method: 'bls',
  instructions: instruction,
  json: '1',
});

// Add all 9 images
images.forEach((img, i) => {
  params.append(`image_base64_${i + 1}`, img);
});

const { data: submitData } = await axios.post(
  'https://ocr.captchaai.com/in.php',
  params.toString()
);

if (submitData.status !== 1) throw new Error(submitData.request);
const taskId = submitData.request;
console.log(`Task submitted: ${taskId}`);

json: '1' sorgt dafür, dass die Antwort als JSON zurückkommt statt im älteren Textformat. Bei Erfolg steht in request die Task-ID – die brauchen Sie im nächsten Schritt.


Schritt 3: Ergebnis abfragen statt warten

Das Polling läuft gegen res.php. Die reine Lösungszeit liegt bei BLS-Rastern laut den offiziellen Werten unter 1 Sekunde – das Beispiel wartet trotzdem zunächst 5 Sekunden und fragt danach in gleichen Abständen erneut ab. Das hält die Zahl der Requests niedrig und ist gegenüber Netzwerk-Latenz robuster als eine enge Schleife.

await sleep(5000);

let selectedCells;
for (let i = 0; i < 30; i++) {
  const { data: pollData } = await axios.get('https://ocr.captchaai.com/res.php', {
    params: { key: API_KEY, action: 'get', id: taskId, json: 1 },
  });

  if (pollData.status === 1) {
    selectedCells = JSON.parse(pollData.request);
    console.log('Selected cells:', selectedCells);
    break;
  }
  if (pollData.request !== 'CAPCHA_NOT_READY') {
    throw new Error(pollData.request);
  }
  await sleep(5000);
}

Solange request den Wert CAPCHA_NOT_READY liefert, läuft die Aufgabe noch. Jede andere Antwort ist ein echter Fehler – dann bricht die Schleife sofort ab; genau das macht der throw. Zurück kommt am Ende ein JSON-Array der ausgewählten Zellen.


Schritt 4: Zellen anklicken und Formular absenden

Jetzt schließt sich der Kreis. Die Indizes aus der Antwort werden auf die Rasterelemente im DOM gemappt – abzüglich 1, weil die API ab 1 zählt. Danach wird das Formular abgesendet und der Browser geschlossen.

// Click each identified cell
const gridCells = await page.$$('.bls-grid img');
for (const cellNum of selectedCells) {
  await gridCells[cellNum - 1].click();
}

// Submit the form
await page.click('.bls-submit');
console.log(`Solved: clicked cells ${JSON.stringify(selectedCells)}`);
await browser.close();

Erwartete Ausgabe:

Selected cells: [1, 4, 7, 8]
Solved: clicked cells [1,4,7,8]

Sitzungen auf solchen Formularen sind kurzlebig: Klicken Sie die Zellen unmittelbar nach der Antwort an und senden Sie das Formular direkt ab, statt Ergebnisse für einen späteren Durchlauf zwischenzuspeichern.


Fehlercodes und ihre Ursachen

Fehlercode Ursache Vorgehen
ERROR_BAD_PARAMETERS Bilder oder Anweisungscode fehlen Alle neun Bilder plus Anweisungscode senden
CAPCHA_NOT_READY Aufgabe läuft noch Weiter abfragen, im Beispiel alle 5 Sekunden
ERROR_ZERO_BALANCE Kein Guthaben Guthaben im CaptchaAI-Konto aufladen

Ein vierter Fall taucht in keinem Fehlercode auf: ein leeres images-Array. Dann wurde das Raster ausgelesen, bevor die Kacheln geladen waren – die API sieht eine unvollständige Aufgabe und meldet fehlende Parameter.


Betrieb: Node.js-Job, Secrets und DSGVO

In deutschen Teams läuft ein solches Skript selten dauerhaft lokal – üblicher ist ein Node-Job in einer GitLab-CI-Pipeline oder in GitHub Actions. Drei Punkte entscheiden dort über den Dauerbetrieb:

  • Schlüsselverwaltung: Der API-Schlüssel gehört als maskierte CI-Variable in die Pipeline, nie ins Repository.
  • Systempakete: Headless-Chromium braucht auf einem schlanken Server – etwa einer Hetzner- oder netcup-VM – die passenden Bibliotheken; ohne sie scheitert bereits puppeteer.launch().
  • Wiederholungslogik: Ein einzelner Fehlschlag ist normal. Ein erneuter Versuch mit exponentiellem Backoff nach spätestens zwei Fehlversuchen ist stabiler als eine Endlosschleife.

Dazu ein Hinweis, der bei BLS-Formularen häufig übersehen wird: Rasterbilder, Screenshots und Debug-Logs eines Antragsformulars können personenbezogene Daten enthalten. Prüfen Sie vor dem produktiven Einsatz, welche Artefakte Ihre Pipeline speichert, wie lange sie aufbewahrt werden und auf welcher Rechtsgrundlage die Verarbeitung erfolgt. Das ist DSGVO-Sorgfalt auf Ihrer Seite, keine Eigenschaft des Solvers.


Häufige Fragen

Wie viele Threads brauche ich für parallele BLS-Abfragen?

Einen Thread pro gleichzeitig laufender Abfrage. Die neun Bilder eines Rasters zählen zusammen als eine Aufgabe, nicht als neun. Ein Skript, das ein Formular nach dem anderen abarbeitet, kommt mit einem Thread aus; erst mehrere parallele Browser-Kontexte brauchen mehr. BASIC (15 $/Monat, 5 Threads) deckt kleine Test-Suites ab, ADVANCE (90 $/Monat, 50 Threads) den Dauerbetrieb.

Was kostet eine einzelne gelöste BLS-Abfrage?

Nichts zusätzlich – abgerechnet wird der Thread, nicht die Lösung. Jeder Plan enthält unbegrenzte Lösungen pro Thread im Abrechnungsmonat, es gibt keine Tageslimits und keine Preisunterschiede zwischen CAPTCHA-Typen. Die Kosten hängen damit allein davon ab, wie viele Abfragen gleichzeitig laufen sollen.

Warum hängt die Abfrage dauerhaft bei CAPCHA_NOT_READY?

Meist, weil die Aufgabe nie sauber angelegt wurde. Prüfen Sie zuerst die Antwort von in.php: Steht dort status: 1 und eine Task-ID? Fehlt ein Bild oder der Anweisungscode, quittiert die API das mit ERROR_BAD_PARAMETERS, und die Schleife fragt anschließend eine ID ab, die es nicht gibt. Das Beispiel bricht nach 30 Durchläufen ab; ein Log-Eintrag an dieser Stelle spart viel Rätselraten.

Funktioniert derselbe Ablauf mit Playwright?

Ja. Die drei API-Aufrufe bleiben identisch, weil sie reine HTTP-Requests sind – nur die Browser-Schicht wird ausgetauscht. Aus puppeteer.launch() wird chromium.launch(), das Auslesen und Anklicken der Kacheln bleibt strukturell gleich. Mit Selenium gilt dasselbe; dort übernimmt ein expliziter Wait die Rolle der Selektor-Prüfung.

Worauf sollten Teams in der DACH-Region rechtlich achten?

Auf zwei Dinge: die Nutzungsbedingungen des jeweiligen Portals und den Umgang mit personenbezogenen Daten. Setzen Sie automatisierte Abläufe nur in Umgebungen ein, die Sie selbst betreiben oder für die eine schriftliche Freigabe vorliegt. Antragsdaten und IP-Adressen in Proxy-Logs brauchen eine dokumentierte Rechtsgrundlage und eine Löschfrist – diese Fragen klärt man vor dem ersten produktiven Lauf, nicht danach.


Verwandte Leitfäden


API-Schlüssel holen und BLS-Raster direkt aus Node.js lösen →

Kommentare sind für diesen Artikel deaktiviert.