Warum ein Proxy-Request keine Versuche mehr übrig hat

Das Problem

Ein POST /api/proxy/-Aufruf liefert einen Fehler und keine Daten zurück. Die Meldung ist kurz und hat immer dieselbe Struktur:

{
  "error": "Download maxTry limit reached",
  "total": 34.812,
  "request": { "...": "..." }
}

Dieser Satz liest sich identisch, egal ob jeder Exit blockiert war, jeder Exit tot war oder FourA die echte Seite bei fast jedem Versuch abgerufen hat und deine eigenen validate-Regeln sie verworfen haben. Diese drei Fälle erfordern gegensätzliche Korrekturen.

Die Antwort: attemptReport

Jeder Proxy-Aufruf, der seine Versuche ausgeführt hat und fehlgeschlagen ist, enthält neben dem Fehler ein attemptReport-Objekt, einschließlich eines Aufrufs, der mit "error": "Timeout" endete. Ein Hostname, der nicht aufgelöst werden kann, schlägt vor jedem Versuch mit status: 0 und ohne Bericht fehl. Es zählt, worauf die Versuche tatsächlich gestoßen sind:

{
  "error": "Download maxTry limit reached",
  "attemptReport": {
    "total": 25,
    "noResponse": 0,
    "defense": 0,
    "contentRejected": 25,
    "statusRejected": 0,
    "other": 0,
    "vendors": [],
    "profilesTried": ["default"],
    "summary": "25 attempt(s): 25 returned HTTP 200 with no defense present and were rejected only by your validate.data - the page was fetched, your content rule did not match it"
  },
  "total": 34.812
}

Der error String bleibt bewusst unverändert, damit ein darauf abgleichender Client weiterhin funktioniert. Lies attemptReport.summary für eine einzeilige Antwort oder die Zählerwerte, wenn du darauf verzweigen möchtest.

Felder

Feld Typ Was gezählt wird
total Integer Durchgeführte Versuche
noResponse Integer Der Exit hat nicht geantwortet oder die Seite hat nicht rechtzeitig geantwortet
defense Integer Die Seite hat geantwortet und ein Bot-Check-Anbieter wurde in der Antwort erkannt
contentRejected Integer HTTP 200, kein Bot-Check, nur durch dein validate.data abgewiesen
statusRejected Integer Die Seite hat geantwortet, kein Bot-Check, durch dein validate.status abgewiesen
other Integer Geantwortet und keines der obigen Kriterien
vendors string[] Jeder im Task erkannte Bot-Check-Anbieter
profilesTried string[] Die vom Task gesendeten Browserprofile in Reihenfolge der ersten Nutzung. default bedeutet, dass der Request genau wie von dir definiert gesendet wurde.
summary String Ein aus den Zählern generierter Satz. Sicher für Logs oder zur Anzeige für Nutzer.

Auswertung

contentRejected ist hoch

Die Seiten sind angekommen. Deine validate.data Regel hat nicht darauf zugetroffen.

Diesen Fall kannst du selbst beheben, und er wird von allen anderen Signalen verdeckt: Die Requests sehen in jeder Metrik wie Fehlschläge aus, obwohl FourA die ganze Zeit echten Inhalt geliefert hat. Rufe die Seite einmal über POST /api/single/ ganz ohne validate ab, prüfe die tatsächliche Rückgabe und passe deine Regel daran an.

Eine häufige Ursache ist eine einzelne Regel für eine Gruppe uneinheitlicher Seiten. Ein Selektor, der nur auf Artikelseiten und nicht auf Videoseiten existiert, schlägt bei jeder Videoseite fehl, dauerhaft und mit vollen Kosten.

statusRejected ist hoch

Die Seite hat geantwortet und deine validate.status Regel hat abgelehnt. Wenn diese Statuscodes 401, 403, 429 oder 503 sind, weist die Seite den Client ab, anstatt zu melden, dass die Seite nicht existiert. Versuche:

  • Ein anderes Browserprofil (browser, os, version im inneren request Objekt)
  • exitCountries, falls der Inhalt region-locked ist
  • POST /api/browser/, falls die Ablehnung JavaScript zur Freigabe erfordert

defense ist hoch

In den Antworten wurde ein Bot-Check erkannt, und vendors gibt den Namen an. Siehe Site checks für Informationen dazu, was FourA derzeit löst und was es nur meldet. Wenn der Anbieter auf diesem Endpoint nicht gelöst wird, leite den Aufruf an POST /api/browser/ oder POST /api/auto/ weiter.

noResponse ist hoch

Die Exits haben überhaupt nicht geantwortet, daher liegen keine Erkenntnisse über das Ziel vor. Erhöhe maxTries, erhöhe timeout_ms und prüfe, ob die URL aus dem öffentlichen Internet auflösbar ist.

other ist hoch

Die Seite hat mit einem Status geantwortet, es wurde kein Bot-Check erkannt und der Versuch schlug aus einem anderen Grund fehl, etwa durch eine validate.headers Regel. Ein Ziel, das langsamer als dein timeout_ms ist, zählt unter noResponse.

Browser-Profil-Rotation

Wenn eine Website den von FourA gesendeten Browser ablehnt, beharrt Proxy nicht darauf und versucht eine andere Familie aus dem public profile catalogue. Es kostet keinen zusätzlichen Versuch: Die Rotation ändert, was ein Retry sendet, nicht, ob einer stattfindet.

Proxy merkt sich außerdem pro Website die Familie, die dort zuletzt funktionierte, nachdem die Website den Standard abgelehnt hatte. Ein späterer Request an diese Website kann direkt beim ersten Versuch mit dieser Familie starten, und FourA wechselt zur nächsten Familie, sobald die Website auch diese ablehnt. Ein mobiler Browser wird nur für Websites gespeichert, die JSON zurückgeben.

Über profilesTried siehst du den Ablauf. Ein einzelner Eintrag bedeutet, dass jeder Versuch denselben Browser gesendet hat: default entspricht dem Request wie übergeben, und eine Katalog-ID ist die Familie, die FourA für diese Website bereits gelernt hatte. Mehrere Einträge bedeuten, dass die Rotation aktiv war und die Website jeden davon abgelehnt hat. Das unterscheidet sich davon, überhaupt nicht rotiert zu haben.

Bei einer erfolgreichen Proxy-Response erscheint ein profile-Feld nur dann, wenn die Rotation einen Browser gewählt hat, den du nicht angefordert hast:

{
  "status": 200,
  "data": "<!doctype html>...",
  "proxy": "A1B2C3",
  "profile": "...",
  "total": 4.108
}

Der Wert ist eine Katalog-ID von GET /api/profiles. Fehlt sie, wurde der Request exakt wie eingegeben gesendet. Ist sie vorhanden, funktionierte ein anderer Browser als der von dir angegebene. Übergib diese ID bei Folgeaufrufen als profile, anstatt den fehlgeschlagenen erneut zu senden. Das Dashboard Playground erledigt das für dich mit Carry.

Ein explizites profile, browser, os oder version in deinem Request wird niemals überschrieben. Das gilt auch für Requests mit eigenem User-Agent- oder Cookie-Header, da ein Clearance an die Signatur gebunden ist, die es erhalten hat.

Im Code auslesen

import requests

API = "https://eu.api.foura.ai"
H = {"X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json"}

r = requests.post(f"{API}/api/proxy/", headers=H, json={
    "maxTries": 5,
    "request": {
        "method": "GET",
        "url": "https://example.com/product/42",
        "validate": {"data": {"accept": ["Add to cart"]}},
    },
}).json()

if "error" in r:
    rep = r.get("attemptReport", {})
    print(rep.get("summary", r["error"]))

    if rep.get("contentRejected", 0) > rep.get("total", 0) / 2:
        # The pages arrived. The validate rule is what threw them away.
        raise SystemExit("validate.data did not match the real page")
    if rep.get("defense", 0):
        print("bot check met:", ", ".join(rep.get("vendors", [])))
Aktualisiert: 30. September 2026