Problèmes courants

Solutions aux problèmes les plus fréquents lors de l'utilisation de l'API FourA.

Contenu vide ou incomplet

Symptôme : L'API renvoie un statut 200 mais le champ data est vide ou ne contient pas le contenu attendu.

Cause : La page cible utilise JavaScript pour afficher le contenu après le chargement initial.

Solution : Passez du single endpoint au browser endpoint. Utilisez checkText pour vérifier que le contenu est chargé :

curl -X POST https://eu.api.foura.ai/api/browser/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/products",
    "timeout_ms": 15000,
    "checkText": "product-list"
  }'

Remarque : l'endpoint browser renvoie le contenu dans le champ body (et non data).

Pages 403 Forbidden ou de vérification

Symptôme : L'API renvoie du HTML contenant une page de vérification ou une page d'accès refusé.

Cause : Le site cible a détecté la requête comme étant automatisée et l'a bloquée.

Solution : Utilisez l'endpoint proxy pour une rotation automatique des adresses IP :

curl -X POST https://eu.api.foura.ai/api/proxy/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "maxTries": 5,
    "request": {
      "method": "GET",
      "url": "https://example.com/prices",
      "unblocker": true
    }
  }'

Si le problème persiste, augmentez maxTries pour accorder plus de tentatives à la rotation de proxy.

Un code 403 renvoyé par la cible arrive sous la forme d'un code HTTP 200 avec status: 403 dans le corps. Une erreur 403 sur l'appel lui-même, avec un en-tête X-FourA-Limit, est différente : consultez 403 Not in Your Plan.

Erreurs de timeout

Symptôme : les requêtes échouent avec une erreur de délai d'attente dépassé (timeout).

Cause : la page cible met plus de temps à charger que le délai d'attente configuré.

Solution : augmentez timeout_ms (la valeur par défaut est de 15s pour single, 30s pour browser, 45s pour proxy) :

curl -X POST https://eu.api.foura.ai/api/browser/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://slow-site.com",
    "timeout_ms": 60000
  }'

Pour les requêtes de navigateur, vérifiez également que votre valeur checkText apparaît bien sur la page. Une faute de frappe fait échouer l'appel avec checkText:<your text> not found.

403 Non inclus dans votre forfait

Symptôme : L'API renvoie une erreur 403 avec un header X-FourA-Limit et un reason contenant plan_limit_feature ou plan_limit_premium.

{
  "error": "Geo targeting (the exitCountries parameter) is not included in your plan (Free). Remove the parameter or upgrade.",
  "reason": "plan_limit_feature",
  "documentation": "https://foura.ai/prices"
}

Cause : Votre forfait n'inclut pas l'endpoint appelé ou le paramètre envoyé. plan_limit_feature couvre un endpoint exclu et exitCountries sans ciblage géographique ; plan_limit_premium couvre exitClass: premium sans sorties premium. La cible n'a jamais été contactée et rien n'a été dépensé.

Solution : Supprimez le paramètre, appelez un endpoint inclus dans votre forfait ou passez à l'offre supérieure. L'onglet Limits & Features de Usage & Limits liste ce que votre forfait inclut. Ne réessayez pas sans modification : aucun Retry-After n'est défini car attendre ne changera pas le résultat.

429 Too Many Requests

Symptôme : L'API renvoie 429.

Cause : L'une des deux vérifications a rejeté l'appel, et la response indique laquelle. Si elle contient un header X-FourA-Limit, l'une des limites de votre forfait a été atteinte : requêtes simultanées ou requêtes par minute sur cet endpoint, requêtes Browser pour la journée, ou crédits/bande passante pour la période de facturation. S'il n'y a pas un tel header, le quota partagé par minute de la plateforme pour ce service était plein, ce qui concerne le trafic global de FourA et non le vôtre.

Solution : Lisez d'abord X-FourA-Limit. Patientez lorsque la limite se réinitialise en quelques secondes, et arrêtez dans le cas contraire. Les limites de forfait qui se débloquent après une attente indiquent les secondes dans le header Retry-After et dans retry_after_seconds ; la limite partagée les indique dans retryAfter :

import time
import requests

# Plan limits that come back in hours or days, not seconds.
STOP_ON = {"plan_limit_browser_daily", "plan_limit_credits", "plan_limit_bandwidth"}

def make_request(endpoint_url, payload, retries=3):
    for i in range(retries):
        resp = requests.post(
            endpoint_url,
            headers={"X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json"},
            json=payload
        )
        if resp.status_code == 429:
            limit = resp.headers.get("X-FourA-Limit")
            if limit in STOP_ON:
                raise Exception(f"stopped by {limit}: {resp.json().get('error')}")
            header = resp.headers.get("Retry-After")
            body = resp.json()
            wait = (
                int(header) if header and header.isdigit()
                else body.get("retry_after_seconds") or body.get("retryAfter") or 2 ** i
            )
            time.sleep(wait)
            continue
        return resp
    raise Exception("Rate limit not resolved after retries")

# Example: single request
make_request(
    "https://eu.api.foura.ai/api/single/",
    {"method": "GET", "url": "https://example.com"}
)

Si le header indique plan_limit_concurrency ou plan_limit_rate, la solution consiste à limiter le nombre d'appels ouverts simultanément et le nombre d'appels démarrés par minute, plutôt que de réessayer avec plus d'insistance. Renvoyer immédiatement un lot refusé entraîne à nouveau le refus de l'ensemble du lot. Les appels refusés ne sont pas décomptés de votre limite par minute, mais s'ils continuent d'arriver à un rythme supérieur au double de cette limite, les refus se transforment en période de temporisation: le corps du 429 renvoie cooldown: true et vous invite à faire une pause de 30 secondes (retry_after_seconds: 30). Exécuter des requêtes en parallèle détaille ce modèle, et la section Usage & Limits du Dashboard affiche vos compteurs en temps réel à côté de vos limites.

503 Service Unavailable

Symptôme : L'API renvoie un statut 503.

Cause : Cela se produit dans deux cas :

  1. Le service est à pleine capacité. FourA traite déjà autant de requêtes sur ce moteur qu'il est autorisé à en exécuter simultanément, calculé sur l'ensemble du trafic global et non uniquement sur le vôtre. Service at capacity dans le champ error. La situation se résout généralement en quelques secondes.
  2. Service temporairement désactivé. Une fenêtre de maintenance est en cours. Service disabled dans le champ error.

Ces deux cas incluent un champ retryAfter dans la réponse. Aucun des deux ne correspond à une limite de forfait: les limites de votre propre forfait renvoient toujours un header X-FourA-Limit sur un code 403 ou 429, jamais avec un 503.

Solution : Attendez retryAfter secondes, puis réessayez :

import time
import requests

def make_request_with_retry(endpoint_url, payload, retries=3):
    for i in range(retries):
        resp = requests.post(
            endpoint_url,
            headers={"X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json"},
            json=payload
        )
        if resp.status_code in (429, 503):
            header = resp.headers.get("Retry-After")
            body = resp.json()
            wait = (
                int(header) if header and header.isdigit()
                else body.get("retry_after_seconds") or body.get("retryAfter") or 2 ** i
            )
            time.sleep(wait)
            continue
        return resp
    raise Exception("Request not resolved after retries")

Une erreur 503 à pleine capacité signifie que FourA est surchargé, il suffit donc d'appliquer un backoff et de réessayer. Si vous recevez plutôt un refus 429 avec X-FourA-Limit, cela vient de votre côté : réduisez le nombre de requêtes parallèles dans votre pipeline.

504 Upstream Timeout

Symptôme : L'API renvoie 504 avec {"error": "Upstream timeout"}.

Cause : Le traitement ne s'est pas terminé dans le délai imparti que vous avez défini pour la requête. Une cible lente, une résolution de challenge à froid ou une page très volumineuse peuvent en être la cause. Il ne s'agit pas d'un problème lié à votre clé, à vos paramètres ou à votre proxy.

Solution : Donnez plus de temps à l'appel, ou réessayez. FourA attend la durée de votre timeout_ms plus une petite marge, donc l'augmenter prolonge réellement l'attente :

{
  "url": "https://slow-site.com/report",
  "timeout_ms": 90000
}

Pour /api/auto/ sur une cible protégée, un premier appel à froid peut prendre des dizaines de secondes. Son timeout_ms couvre l'ensemble de la séquence et accepte jusqu'à 180000.

Lorsque /api/auto/ épuise lui-même ce délai imparti, l'appel renvoie tout de même un code HTTP 200. Le corps contient un error qui commence par time budget exhausted, et status vaut généralement 504 (une tentative précédente ayant échoué peut y laisser son propre statut à la place). Augmentez timeout_ms ou réessayez.

502 Upstream Unavailable

Symptôme : L'API renvoie 502 avec {"error": "Upstream unavailable"}, ou 503 avec {"error": "Backend service unavailable"}.

Cause : FourA a joint son propre moteur mais n'a pas pu utiliser la réponse, généralement en raison d'une instance en cours de redémarrage.

Solution : Réessayez avec un court délai d'attente. Les deux cas sont classés comme service_error, et seul success est facturé, donc une nouvelle tentative ne vous coûte rien de plus. Si le problème persiste plus d'une minute ou deux, consultez la page de statut.

Erreurs d'authentification 401

Symptôme : Chaque requête renvoie 401 Unauthorized.

Liste de vérification :

  1. Vérifiez que l'en-tête est X-API-Key: YOUR_API_KEY (et non Authorization: Bearer ou Api-Key)
  2. Vérifiez l'absence d'espaces ou de sauts de ligne superflus dans votre clé API
  3. Créez une nouvelle clé depuis le Dashboard si la clé actuelle est potentiellement compromise

400 La cible résout vers une IP privée ou réservée

Symptôme : L'API renvoie 400 avec Refusing to fetch <target>: target resolves to a private or reserved IP range avant que la requête ne quitte FourA.

Cause : Votre url résout vers une plage d'adresses IP privées, de bouclage ou réservées (RFC 5735, RFC 6598 ou blocs réservés IPv6). FourA refuse ces cibles afin que son réseau ne puisse pas être utilisé pour joindre des hôtes internes.

Solution : Récupérez une URL publique. Si vous effectuez des tests, utilisez une cible publique comme https://example.com ou https://httpbin.org/get. Si votre cible prévue est un service que vous gérez, exposez-le d'abord sur un nom d'hôte public.

{ "error": "Refusing to fetch <target>: target resolves to a private or reserved IP range. The FourA scraping API only forwards requests to public internet hosts." }

Un nom d'hôte qui ne peut pas être résolu n'est pas refusé. L'appel renvoie un statut HTTP 200 avec status: 0 et la raison (could not resolve <host>: <reason>), comme pour toute cible que FourA ne peut pas joindre, et il n'est pas facturé.

no_eligible_proxy lors de l'utilisation de exitCountries

Symptôme : un appel /api/proxy/ avec exitCountries renvoie HTTP 200 avec une enveloppe d'erreur JSON :

{
  "error": "No eligible proxy found for exit countries: CZ, GB",
  "code": "no_eligible_proxy",
  "details": { "exitCountries": ["CZ", "GB"] },
  "total": 0.084
}

Cause : Le pool de proxys actuel ne dispose d'aucun point de sortie opérationnel dont le pays visible par la cible correspond à votre liste d'autorisation. FourA ne bascule jamais vers un pays non demandé lorsque vous définissez exitCountries.

Solution : Conservez le périmètre demandé et réessayez plus tard. Le pool est actualisé environ toutes les dix minutes, de sorte qu'un pays sans correspondance actuelle en obtient souvent une dans l'heure.

import time, requests

def fetch_scoped(url, countries, max_attempts=6, wait_sec=600):
    for _ in range(max_attempts):
        r = requests.post("https://eu.api.foura.ai/api/proxy/",
            headers={"X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json"},
            json={"maxTries": 5, "exitCountries": countries,
                  "request": {"method": "GET", "url": url}}).json()
        if r.get("code") == "no_eligible_proxy":
            time.sleep(wait_sec)
            continue
        return r
    raise RuntimeError(f"no eligible exit in {countries} after {max_attempts} attempts")

Élargissez la liste des pays uniquement si les exigences de pays de votre workflow ont réellement changé. Les solutions de repli silencieuses vers d'autres pays peuvent interrompre la logique dépendante de la géolocalisation en aval.

Le corps de la réponse contient du texte illisible

Symptôme : La réponse data (ou body) contient du mojibake ou des caractères illisibles lorsque la cible utilise un charset non-UTF-8.

Cause : Par défaut, FourA décode automatiquement les corps de réponse en UTF-8 en se basant sur l'en-tête Content-Type de la cible ou sur une balise HTML <meta charset>. Si la cible indique un mauvais charset, vous obtenez du texte tronqué ou corrompu.

Solution : Pour les charges utiles binaires (images, protobuf, audio brut), définissez returnBuffer: true sur la requête. Single et Proxy renvoient alors data sous forme d'objet contenant les octets bruts, {"type": "Buffer", "data": [<byte values>]}, sans aucun transcodage de charset appliqué.

{
  "method": "GET",
  "url": "https://example.com/image.png",
  "returnBuffer": true
}

Pour les cibles texte qui déclarent incorrectement leur jeu de caractères (charset), décodez vous-même les octets bruts : effectuez la requête avec returnBuffer: true, lisez les valeurs d'octets dans data.data, puis décodez-les avec le bon charset.

HTML inattendu au lieu de JSON

Symptôme : Vous attendiez du JSON du site cible mais vous avez reçu du HTML.

Cause : La page cible peut renvoyer un contenu différent selon les headers.

Solution : Ajoutez un header Accept et activez unblocker pour obtenir des headers de navigateur réalistes :

curl -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://api.example.com/data",
    "headers": [["Accept", "application/json"]],
    "unblocker": true
  }'

Vous pouvez également définir tryJsonData sur true pour que FourA analyse automatiquement les réponses JSON.

Le corps est une page de challenge, pas le contenu

Symptôme : L'appel a réussi, status est 200, mais data (ou body) est une vérification anti-bot plutôt que la page souhaitée.

Cause : La cible a déclenché un test anti-bot auquel FourA a été confronté sans parvenir à le franchir. La réponse l'indique : Single et Proxy renvoient defense avec solved: false, et Browser renvoie defenseSolved: false avec le fournisseur dans defenses.present.

Solution : Vérifiez d'abord defense.vendor, puis appliquez un niveau supérieur. Essayez un autre profil de navigateur sur Single, passez à Proxy pour une sortie différente, ou utilisez Browser pour que le JavaScript s'exécute. Référence complète des champs et liste des fournisseurs : Vérifications de site.

Ajoutez une sous-chaîne validate.data.accept présente uniquement sur la vraie page. Une page de vérification reconnue par FourA n'est jamais considérée comme un succès : elle est renvoyée avec un en-tête X-FourA-Check-Page et n'est pas facturée. Sans validate, une page de vérification non reconnue par FourA, renvoyée avec un statut HTTP 200, est considérée comme un succès, et vous ne vous en rendez compte qu'en aval plutôt qu'au moment de l'appel.

Toujours bloqué ?

Si aucune des solutions ci-dessus ne fonctionne :

  1. Consultez la page de statut pour vérifier les incidents en cours
  2. Examinez vos métriques de requêtes dans le Tableau de bord
  3. Contactez le support à support@foura.ai avec les détails de votre requête (incluez le X-FourA-Request-Id de la réponse en échec)

Étapes suivantes

Mis à jour : 30 septembre 2026