Pourquoi une requête proxy a épuisé ses tentatives
Le problème
Un appel POST /api/proxy/ renvoie une erreur et aucune donnée. Le message est court et a toujours la même forme :
{
"error": "Download maxTry limit reached",
"total": 34.812,
"request": { "...": "..." }
}
Cette phrase se lit de la même façon, que chaque sortie ait été bloquée, que chaque sortie ait été inaccessible, ou que FourA ait récupéré la vraie page à presque chaque tentative et que vos propres règles validate l'aient rejetée. Ces trois cas nécessitent des correctifs opposés.
La réponse : attemptReport
Chaque réponse Proxy en échec contient un objet attemptReport à côté de l'erreur. Il comptabilise ce à quoi les tentatives ont réellement été confrontées :
{
"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
}
La chaine error est deliberement inchangee, afin qu'un client qui effectue une correspondance dessus continue de fonctionner. Lisez attemptReport.summary pour une reponse en une seule ligne, ou les compteurs si vous souhaitez effectuer des branchements conditionnels.
Champs
| Champ | Type | Ce qui est compte |
|---|---|---|
total |
integer | Tentatives effectuees |
noResponse |
integer | Le point de sortie n'a jamais repondu, le site n'a donc jamais ete atteint |
defense |
integer | Le site a repondu et un fournisseur de verification anti-bot a ete reconnu dans cette reponse |
contentRejected |
integer | HTTP 200, aucun test anti-bot, rejete uniquement par votre validate.data |
statusRejected |
integer | Le site a repondu, aucun test anti-bot, rejete par votre validate.status |
other |
integer | A repondu, et aucun des cas ci-dessus |
vendors |
string[] | Chaque fournisseur de verification anti-bot reconnu au cours de la tache |
profilesTried |
string[] | Les profils de navigateur envoyes par la tache, par ordre de premiere utilisation. default signifie que la requete a ete emise exactement comme vous l'avez ecrite. |
summary |
string | Une phrase generee a partir des compteurs. Peut etre journalisee ou affichee a un utilisateur en toute securite. |
Interpreation des resultats
contentRejected est eleve
Les pages sont bien arrivees. Votre regle validate.data n'a pas trouve de correspondance.
C'est le cas que vous pouvez corriger vous-meme, et c'est celui que tous les autres signaux masquent : les requetes semblent echouer selon toutes les metriques, alors que FourA fournissait du contenu reel depuis le debut. Recuperez la page une fois via POST /api/single/ sans aucun validate, examinez ce qui est reellement renvoye, et reecrivez la regle en consequence.
Une cause frequente est l'application d'une regle unique a un ensemble de pages heterogenes. Un selecteur present sur les pages d'articles mais absent des pages video echoue a chaque fois qu'il arrive sur une page video, indefiniment, au cout reel complet.
statusRejected est eleve
Le site a repondu et votre regle validate.status a rejete la reponse. Si ces statuts sont 401, 403, 429 ou 503, le site refuse le client plutot que d'indiquer que la page n'existe pas. Essayez :
- Un autre profil de navigateur (
browser,os,versionsur l'objet internerequest) exitCountriessi le contenu est restreint par regionPOST /api/browser/si le refus necessite l'execution de JavaScript pour etre resolu
defense est eleve
Un test anti-bot a ete reconnu dans les reponses, et vendors indique lequel. Consultez Anti-Bot Defenses pour savoir ce que FourA neutralise aujourd'hui et ce qu'il se contente de signaler. Si le fournisseur ne fait pas partie de ceux geres sur cet endpoint, basculez l'appel vers POST /api/browser/ ou POST /api/auto/.
noResponse est eleve
Les points de sortie n'ont pas repondu du tout, aucune information n'a donc pu etre obtenue sur la cible. Augmentez maxTries, augmentez timeout_ms, et verifiez que l'URL est resolue depuis l'Internet public.
other est eleve
A repondu, et n'entre dans aucune des categories ci-dessus. Comparez total_time a votre timeout_ms : une cible plus lente que votre budget imparti apparait ici.
Rotation des profils de navigateur
Lorsqu'un site refuse le navigateur envoyé par FourA, Proxy cesse d'insister et essaie une autre famille issue du catalogue public de profils. Cela ne consomme aucune tentative supplémentaire: la rotation modifie ce qu'un nouvel essai envoie, sans jamais impacter son exécution.
profilesTried vous permet de voir ce comportement. Une seule entrée signifie que la request est partie telle quelle à chaque fois. Plusieurs entrées indiquent que la rotation s'est exécutée et que le site a refusé chacune d'entre elles, ce qui diffère d'une absence totale de rotation.
Sur une réponse Proxy réussie, un champ profile apparaît uniquement lorsque la rotation a choisi un navigateur que vous n'aviez pas demandé:
{
"status": 200,
"data": "<!doctype html>...",
"proxy": "A1B2C3",
"profile": "...",
"total": 4.108
}
La valeur correspond à un identifiant de catalogue issu de GET /api/profiles. Son absence signifie que la request est partie exactement comme elle a été rédigée. Sa présence indique que le navigateur qui a fonctionné n'est pas celui que vous avez saisi, vous devez donc transmettre cet identifiant sous la forme profile lors des appels suivants plutôt que de rejouer celui qui a échoué. Le dashboard Playground gère cela pour vous avec l'option Carry.
Une valeur explicite pour profile, browser, os ou version sur votre request n'est jamais écrasée. Il en va de même pour une request comportant votre propre header User-Agent ou Cookie, car une validation est liée à la signature qui l'a obtenue.
Reading It in Code
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", [])))
Contenu associé
- API Endpoints : Référence complète des requêtes et réponses Proxy
- Anti-Bot Defenses : Fournisseurs,
defenseet rejeu d'un clearance - Request Outcomes : Classification et facturation des réponses rejetées
- Common Issues : Autres problèmes et solutions
- Choosing the Right Endpoint : Dans quels cas le Proxy n'est pas l'outil adapté