Erreurs API
Comment gérer les erreurs de l'API FourA.
Format de réponse d'erreur
L'API renvoie des objets JSON plats pour toutes les erreurs. Il n'y a pas d'objet error imbriqué ni de codes d'erreur.
{
"error": "Invalid API key"
}
Certaines erreurs incluent des champs supplémentaires comme status, service, retryAfter, current, ou limits au premier niveau :
{
"error": "Rate limit exceeded",
"status": 429,
"service": "single",
"retryAfter": 5,
"current": { "concurrency": 12, "rpm": 3000 },
"limits": { "maxConcurrency": 500, "maxRpm": 3000 }
}
Suivre une requête
Chaque réponse de l'API (succès ou erreur) inclut un header X-FourA-Request-Id avec un UUID pour cet appel. Enregistrez-le de votre côté. Si vous devez demander au support ce qui s'est passé avec une requête spécifique, cet identifiant nous permet de la retrouver.
curl -i -X POST https://eu.api.foura.ai/api/single/ \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"method": "GET", "url": "https://example.com"}'
# HTTP/1.1 200 OK
# X-FourA-Request-Id: 9f1c4e6c-7b2a-4d3e-8a1f-2c9d8e4a3b15
# Content-Type: application/json
# ...
Types d'erreurs
400 : Bad Request
Le corps de la request ne contient pas les champs requis, contient des valeurs invalides ou indique une cible que l'API refuse de récupérer.
{
"error": "Invalid request body format"
}
La même erreur 400 couvre également la protection SSRF. Si votre url est résolu vers une plage d'adresses IP privée, de bouclage ou autrement réservée (RFC 5735, RFC 6598, blocs IPv6 réservés), la requête est rejetée avant de quitter le réseau de FourA :
{
"error": "Target <ip> resolves to a private/reserved IP"
}
Un JSON malformé dans le corps est refusé de la même manière, avant qu'aucun champ ne soit lu :
{
"error": "Invalid JSON in request body"
}
Les champs proxy et ignoreProxies ont leurs propres erreurs 400. Tous deux prennent les ID de proxy opaques retournés par les réponses précédentes, donc tout le reste échoue au décodage :
| Message | Ce qui s'est passé |
|---|---|
Invalid proxy format |
La valeur proxy n'est pas un ID de proxy émis par FourA. Une adresse de proxy brute atterrit ici. |
Invalid ignoreProxies format |
L'une des entrées dans ignoreProxies n'est pas un ID de proxy. |
Proxy not found |
L'ID s'est décodé proprement mais ne résout plus vers une sortie active. Choisissez-en un nouveau. |
Correction : Vérifiez que votre request inclut tous les champs requis, que les URL utilisent http:// ou https://, que l'hôte résout vers une adresse publique, et que toute valeur proxy est un ID copié textuellement depuis une réponse précédente.
401 : Unauthorized
Votre clé d'API est manquante ou invalide.
Clé manquante :
{
"error": "Missing API key. Include X-API-Key header."
}
Clé invalide :
{
"error": "Invalid API key"
}
Solution : Vérifiez que votre header X-API-Key contient une clé valide. Générez une nouvelle clé depuis le Tableau de bord si nécessaire.
429 : Rate Limited
Vous avez envoyé trop de requêtes sur une courte période.
{
"error": "Rate limit exceeded",
"status": 429,
"service": "single",
"retryAfter": 5,
"current": { "concurrency": 12, "rpm": 3000 },
"limits": { "maxConcurrency": 500, "maxRpm": 3000 }
}
Solution : Attendez le nombre de secondes indiqué dans retryAfter avant d'envoyer d'autres requêtes. Consultez Rate Limits pour plus de détails.
500: Erreur serveur
Un problème est survenu de notre côté.
Solution : Réessayez la requête après un court délai. Si l'erreur persiste, consultez la page de statut ou contactez le support avec le X-FourA-Request-Id de la réponse en échec.
502: Upstream indisponible
FourA a atteint son propre moteur mais n'a pas pu utiliser la réponse.
{
"error": "Upstream unavailable",
"details": "..."
}
Solution : Réessayez avec un délai d'attente court. Le problème vient de notre côté, cela ne vous coûte donc rien : le résultat est service_error et seul success est facturé.
504 : Upstream Timeout
Le moteur n'a pas terminé dans le temps imparti pour cette requête.
{
"error": "Upstream timeout",
"details": "the backend did not finish inside the time budget for this request"
}
Une erreur 504 concerne la durée du travail, et non votre clé, vos paramètres ou votre proxy. Les cibles lentes, les résolutions de défis à froid et les pages volumineuses en sont les causes habituelles.
Solution : Augmentez timeout_ms sur la request (Single accepte jusqu'à 120000, Browser jusqu'à 120000, Auto jusqu'à 180000), ou réessayez. FourA attend le budget que vous avez déclaré plus une petite marge, donc demander plus de temps vous fait véritablement gagner du temps.
503 : Service désactivé ou à capacité maximale
Une erreur 503 signifie que le service est temporairement indisponible pour maintenance ou que vous avez atteint la limite de concurrence. Les deux responses incluent un champ retryAfter. La forme de concurrence inclut également current et limits.
{
"error": "Service disabled",
"status": 503,
"retryAfter": 60
}
Solution : Patientez retryAfter secondes, puis réessayez. La page de statut liste les fenêtres de maintenance actives.
Une troisième variante 503 n'a pas de retryAfter. Cela signifie que le moteur derrière votre endpoint redémarrait lorsque votre appel est arrivé :
{
"error": "Backend service unavailable",
"backend_status": 503
}
Réessayez après une seconde ou deux.
Lecture des échecs de /api/auto/
POST /api/auto/ répond avec HTTP 200 à chaque exécution de l'échelle, même lorsque chaque barreau a échoué. Le véritable résultat se trouve dans le body :
{
"status": 0,
"error": "all attempts failed",
"attempts": 7,
"meta": { "rung": "fail", "solved": false, "attempts": 7, "credits": 47 }
}
Ne créez donc pas de branchement sur le statut de transport pour Auto. Lisez plutôt status et error dans le body. Un véritable non-200 de /api/auto/ signifie que FourA a rejeté l'appel avant le début de l'échelle : 401, 400, 429 ou 503, tous documentés ci-dessus.
Échecs côté cible dans un 200 OK
Tous les échecs ne se manifestent pas par un statut HTTP non-2xx. Lorsque le site cible renvoie HTTP 200 avec une charge utile d'erreur, FourA vous remet toujours le body mais classe la requête comme application_error. Lorsque la cible renvoie un statut non-2xx que vos règles validate n'acceptent pas, le résultat est application_fail et le body est transmis sans modification.
Les deux cas sont facturables comme si la requête avait fonctionné au niveau du réseau. La référence Résultats couvre la taxonomie complète.
Encodage de la réponse
FourA décode automatiquement les corps de réponse en UTF-8. Si la cible sert windows-1251, gbk, shift_jis, iso-8859-*, ou tout autre charset déclaré dans le header Content-Type ou une balise HTML <meta charset>, vous recevez une chaîne UTF-8 propre dans le champ data (single, proxy) ou body (browser).
Pour les charges utiles binaires (images, protobuf, audio brut), définissez returnBuffer: true sur la requête. Le body est renvoyé sous forme de tampon base64 sans aucun transcodage de charset appliqué.
Stratégie de retry
Une politique de retry pratique :
import time
import requests
def make_request(url, payload, api_key, max_retries=3):
for attempt in range(max_retries):
resp = requests.post(
url,
headers={"X-API-Key": api_key, "Content-Type": "application/json"},
json=payload,
)
if resp.status_code == 200:
return resp.json()
body = resp.json() if resp.headers.get("content-type", "").startswith("application/json") else {}
retry_after = body.get("retryAfter", 2 ** attempt)
request_id = resp.headers.get("X-FourA-Request-Id", "?")
if resp.status_code in (429, 503):
time.sleep(retry_after)
continue
if resp.status_code >= 500: # 500, 502, 503, 504 are all ours to fix
time.sleep(2 ** attempt)
continue
# 400/401/404 won't fix themselves
raise RuntimeError(f"{resp.status_code} (request {request_id}): {body.get('error')}")
raise RuntimeError(f"Exhausted {max_retries} retries")
Articles liés
- Rate limits: Détails sur la simultanéité et les RPM
- Résultats des requests: Explication des sept valeurs de résultat
- Problèmes courants: Symptômes, causes, correctifs
- Défenses anti-bot: Quand le body est une page de challenge plutôt qu'une erreur