Limites de débit

Chaque requête d'API FourA passe par trois vérifications avant d'atteindre un moteur : les limites de votre propre forfait, puis l'allocation partagée de la plateforme pour l'endpoint appelé, puis l'allocation partagée de la plateforme pour l'ensemble du trafic. Chaque vérification peut refuser une requête de manière autonome, et chacune répond avec un corps de réponse différent.

Les trois vérifications, dans l'ordre

  1. Limites du forfait. Ce que votre forfait autorise : les endpoints et paramètres inclus, le nombre de requêtes simultanées autorisées par endpoint, le volume par minute, le nombre de requêtes de navigateur par jour, ainsi que les crédits et la bande passante disponibles sur la période de facturation.
  2. Limite globale de la plateforme. Tout ce que l'hôte d'API appelé traite à cet instant, quel que soit l'endpoint ciblé par le trafic. Un refus à ce niveau renvoie "service": "api".
  3. Limite de la plateforme par endpoint. Trafic sur le service single, proxy ou browser appelé.

Votre forfait est évalué en premier, et cet ordre constitue une règle contractuelle plutôt qu'un détail d'implémentation. Les quotas partagés étant une ressource commune, une requête que la plateforme allait de toute façon refuser ne doit pas les consommer avant d'être rejetée. Un compte qui envoie bien plus que ce que son forfait autorise est bloqué avant d'impacter les ressources partagées.

Les vérifications 2 et 3 comptabilisent le trafic total de FourA, pas le vôtre. Interprétez un refus de l'une ou l'autre comme « FourA est saturé », et non comme « vous avez envoyé trop de requêtes ». La vérification 1 concerne uniquement votre compte, et aucun autre événement sur la plateforme ne l'affecte.

Un refus émanant de l'une des vérifications partagées restitue à votre compte l'ensemble des quotas comptabilisés lors de l'admission, aussi bien le quota par minute que l'emplacement journalier du navigateur, car la requête n'a jamais atteint de backend. Cela n'entre pas non plus en compte dans la pause de nouvelle tentative décrite dans Requests per minute : c'est la capacité de FourA qui a provoqué le refus, pas votre forfait.

POST /api/auto/ ne réserve aucun emplacement dédié. Les sous-appels Single, Proxy et Browser qu'il effectue pour vous passent les trois vérifications comme n'importe quelle autre requête. Ainsi, un lot parallèle d'appels automatiques est décompté de votre forfait via ses sous-appels. (Vos totaux de requêtes et votre taux de réussite comptabilisent l'appel automatique lui-même, une seule fois ; les sous-appels sont présentés comme ses tentatives.)

Limites du forfait

Une limite de forfait répond avec un en-tête X-FourA-Limit indiquant quelle limite a refusé l'appel. Le même code figure dans le corps sous reason, vous permettant d'effectuer un branchement conditionnel sans lire les en-têtes. Chaque corps de réponse lié à une limite de forfait contient error, reason et documentation ; les autres champs dépendent de la limite concernée.

X-FourA-Limit Statut Limite atteinte
plan_limit_feature 403 L'endpoint appelé, ou le paramètre exitCountries, n'est pas inclus dans votre forfait
plan_limit_premium 403 exitClass: premium n'est pas inclus dans votre forfait
plan_limit_concurrency 429 Requêtes simultanées sur cet endpoint
plan_limit_rate 429 Requêtes par minute sur cet endpoint
plan_limit_browser_daily 429 Requêtes de navigateur pour la journée
plan_limit_credits 429 Crédits facturés pour la période de facturation
plan_limit_bandwidth 429 Bande passante pour la période de facturation

Les valeurs associées à chaque limite dépendent de votre forfait, et l'onglet Limits & Features de Usage & Limits les affiche à côté de votre utilisation en direct. Ne les écrivez pas en dur : chaque refus indique le plafond qui l'a provoqué.

Une requête refusée ne consomme rien. Le résultat est rate_limit, et seul success est facturé.

Endpoint ou paramètre non inclus dans le forfait

Une erreur 403 avec plan_limit_feature signifie que l'appel demandait un élément non inclus dans votre forfait. La vérification s'exécute avant tout décompte, de sorte que l'appel refusé n'impacte pas vos compteurs de débit ou quotidiens.

{
  "error": "The browser endpoint is not included in your plan (Free). Upgrade to use it.",
  "reason": "plan_limit_feature",
  "documentation": "https://foura.ai/prices"
}

Le même code et le même statut répondent à un appel POST /api/proxy/ qui définit exitCountries sur un forfait sans ciblage géographique. La chaîne error nomme le paramètre :

{
  "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"
}

plan_limit_premium a le même format pour exitClass: premium sur un forfait sans sorties premium. FourA peut également traiter une telle requête depuis le pool standard et renvoyer exitClass: standard dans la réponse, gérez donc les deux cas. Aucun des deux ne consomme de sortie premium. Voir exitClass.

Aucun code 403 ne définit Retry-After. Attendre ne changera pas la réponse.

Simultaneous requests

La simultanéité est comptée par endpoint : votre forfait applique un plafond pour Single, un pour Proxy et un pour Browser. La requête qui dépasse la limite renvoie une erreur 429 avec Retry-After: 1 :

{
  "error": "Concurrency limit reached: your plan allows 50 simultaneous proxy request(s). Retry when an in-flight request finishes.",
  "reason": "plan_limit_concurrency",
  "documentation": "https://foura.ai/prices",
  "limit": 50,
  "in_flight": 51,
  "retry_after_seconds": 1
}

in_flight compte également la requête refusée, sa valeur dépasse donc d'au moins une unité celle de limit.

La solution consiste à limiter votre propre parallélisme plutôt qu'à multiplier les tentatives. Répondre à une erreur 429 en renvoyant immédiatement le même lot génère une autre 429 pour chaque appel qu'il contient. Consultez Exécuter des requêtes en parallèle pour découvrir un modèle d'implémentation.

Requêtes par minute

Single et Proxy appliquent un quota par minute, mesuré sur une minute glissante. Seules les requêtes acceptées sont prises en compte : une requête refusée est décomptée, ainsi un compte qui émet régulièrement un peu plus de requêtes que son quota autorisé verra son quota servi plutôt que de voir presque toutes ses requêtes refusées.

{
  "error": "Rate limit reached: your plan allows 600 single requests per minute. Cool down and retry.",
  "reason": "plan_limit_rate",
  "documentation": "https://foura.ai/prices",
  "limit_per_minute": 600,
  "current_rate": 613,
  "retry_after_seconds": 17
}

retry_after_seconds indique le temps d'attente avant qu'une nouvelle request ne soit acceptée, si vous n'envoyez rien d'autre entre-temps : au moins 1 seconde et au maximum 120. Le header Retry-After contient la même valeur.

Réessayer les requests refusées plus rapidement suit une règle spécifique. Lorsque les requests refusées par ce quota au cours de la minute glissante dépassent le double de l'allocation, l'appel est refusé avec une pause de 30 secondes à la place :

{
  "error": "Rate limit cooldown: 1250 requests over your plan's 600 single requests per minute were refused in the last minute and kept arriving. Pause for 30 seconds.",
  "reason": "plan_limit_rate",
  "documentation": "https://foura.ai/prices",
  "limit_per_minute": 600,
  "current_rate": 540,
  "refused_last_minute": 1250,
  "cooldown": true,
  "retry_after_seconds": 30
}

Les refus pendant la pause ne sont pas comptabilisés, la pause se termine donc d'elle-même au fil de la minute, même pour un client qui continue de réessayer. Pour distinguer la pause du quota ordinaire, lisez cooldown plutôt que le texte error.

Browser requests per day

Browser n'a pas de quota par minute. La limite de son forfait est un nombre de browser requests par jour, comptabilisées à partir de minuit UTC, et le compteur inclut chaque browser request acceptée, pas uniquement celles qui réussissent.

{
  "error": "Daily browser limit reached: your plan allows 300 browser requests per day (resets at midnight UTC).",
  "reason": "plan_limit_browser_daily",
  "documentation": "https://foura.ai/prices",
  "limit_per_day": 300,
  "used_today": 301
}

Ce refus ne comporte aucun header retry_after_seconds ni Retry-After, car l'attente se compte en heures plutôt qu'en secondes. Considérez-le comme un arrêt et planifiez la prochaine exécution pour minuit UTC.

Crédits pour la période de facturation

Seuls les crédits facturés comptent, ce qui correspond uniquement aux requêtes réussies. Lorsque le total facturé atteint les crédits disponibles pour cette période, les requêtes suivantes sont refusées jusqu'à la réinitialisation de la période ou l'achat de crédits supplémentaires.

{
  "error": "Credit limit reached: 75,000 of 75,000 credits used this billing period. Upgrade your plan or wait for the reset.",
  "reason": "plan_limit_credits",
  "documentation": "https://foura.ai/prices",
  "used": 75000,
  "hard_stop": 75000,
  "retry_after_seconds": 86400,
  "resets_at": "2026-10-01T00:00:00.000Z"
}

hard_stop correspond au nombre de crédits facturés à partir duquel les requêtes s'arrêtent pour cette période. Lisez-le directement depuis le body au lieu de le calculer : il inclut déjà les éventuels crédits achetés en plus du forfait.

Bande passante pour la période de facturation

Les forfaits dotés d'une limite de bande passante refusent les requêtes dès que le trafic standard atteint ce plafond pour la période en cours. Le trafic premium dispose de son propre quota et n'est pas comptabilisé dans cette limite. La bande passante achetée est traitée de la même manière que la bande passante incluse, et la chaîne error indique ce qui est disponible pour vous, et non ce que le forfait seul contient.

{
  "error": "Bandwidth limit reached: 50 GB is available to you this billing period.",
  "reason": "plan_limit_bandwidth",
  "documentation": "https://foura.ai/prices",
  "used_bytes": 53687091200,
  "limit_bytes": 53687091200,
  "retry_after_seconds": 86400,
  "resets_at": "2026-10-01T00:00:00.000Z"
}

Sur les deux limites de période, retry_after_seconds est plafonné à 24 heures ; resets_at est l'instant exact où la période est réinitialisée.

Champs des limites de forfait

Champ Type Présent sur Description
error string tous Message lisible par l'humain, incluant la limite qui vous est appliquée
reason string tous plan_limit_ plus le nom de la limite. Même valeur que le header X-FourA-Limit.
documentation string tous Lien vers la page des forfaits
retry_after_seconds number simultanéité, taux, crédits, bande passante Temps d'attente requis. Même valeur que le header Retry-After.
limit number simultanéité Requests simultanées autorisées par le forfait sur cet endpoint
in_flight number simultanéité Requests en cours d'exécution sur cet endpoint pour votre compte, incluant celle refusée
limit_per_minute number taux Requests par minute autorisées par le forfait sur cet endpoint
current_rate number taux Requests comptabilisées dans la minute glissante, incluant celle refusée
refused_last_minute number pause de taux Requests refusées par le quota par minute dans la minute glissante. Présent uniquement sur la pause de 30 secondes.
cooldown boolean pause de taux true lors de la pause de 30 secondes pour nouvelle tentative trop rapide. Absent lors d'un refus standard par minute.
limit_per_day number navigateur par jour Requests de navigateur autorisées par jour selon le forfait
used_today number navigateur par jour Requests de navigateur comptabilisées aujourd'hui, incluant celle refusée
used number crédits Crédits facturés jusqu'ici pour cette période
hard_stop number crédits Crédits facturés à partir desquels les requests s'arrêtent pour cette période
used_bytes number bande passante Trafic standard consommé jusqu'ici pour cette période, en octets. Le trafic premium n'est pas inclus.
limit_bytes number bande passante Octets disponibles pour cette période
resets_at string crédits, bande passante Horodatage ISO 8601 de la fin de la période

Les limites de forfait utilisent retry_after_seconds. Les limites de plateforme ci-dessous utilisent retryAfter. Un gestionnaire de retry doit lire les deux, ou lire le header Retry-After, que seules les limites de forfait définissent.

Limites de la plateforme

Les vérifications de la plateforme surveillent deux indicateurs par service et un autre sur l'ensemble des services :

  • Simultanéité : combien de requests FourA exécute en même temps.
  • RPM : combien de requests FourA a reçues au cours des 60 dernières secondes.

Ces deux compteurs sont partagés par tous les utilisateurs de ce service. current et limits dans les responses ci-dessous décrivent la plateforme, pas votre compte. Si vous souhaitez connaître vos propres métriques, lisez in_flight depuis une réponse de limite de forfait, ou ouvrez Usage & Limits dans le dashboard.

429: Dépassement de RPM

{
  "error": "Rate limit exceeded",
  "status": 429,
  "service": "single",
  "retryAfter": 5,
  "current": {
    "concurrency": 12,
    "rpm": 3000
  },
  "limits": {
    "maxConcurrency": 500,
    "maxRpm": 3000
  }
}

Le service a atteint son quota de requêtes autorisées pour la dernière minute. Attendez retryAfter secondes.

503: Concurrency Exceeded

{
  "error": "Service at capacity",
  "status": 503,
  "service": "proxy",
  "retryAfter": 2,
  "current": {
    "concurrency": 500,
    "rpm": 1200
  },
  "limits": {
    "maxConcurrency": 500,
    "maxRpm": 3000
  }
}

Le service exécute actuellement le nombre maximal de requests autorisées simultanément. La situation se rétablit en quelques secondes.

Service désactivé

Lorsqu'un service est temporairement mis hors ligne pour maintenance, l'API renvoie une erreur 503 avec un message d'erreur différent :

{
  "error": "Service disabled",
  "status": 503,
  "service": "single",
  "retryAfter": 60,
  "current": { "concurrency": 0, "rpm": 0 },
  "limits": { "maxConcurrency": 500, "maxRpm": 3000 }
}

Il ne s'agit pas d'un rate limit. Le service est temporairement indisponible. Verifiez la valeur de retryAfter et reessayez apres ce nombre de secondes. Cela se resout generalement en quelques minutes.

Les deux formats de 503 comportent les memes cles, effectuez donc votre branchement conditionnel sur la chaine error et jamais sur la presence des champs. Service disabled correspond a la maintenance, Service at capacity a la concurrence.

Sur le format de maintenance, current.concurrency et current.rpm valent toujours 0 : la requete a ete rejetee avant toute mesure.

Champs des limites de la plateforme

Champ Type Description
error string Message d'erreur lisible par l'utilisateur
status number Code de statut HTTP (429 ou 503)
service string Service ayant refuse l'appel : single, proxy, browser ou api
retryAfter number Temps d'attente recommande en secondes avant de reessayer
current.concurrency number Requetes executees par le service a l'echelle de la plateforme lors du refus
current.rpm number Requetes traitees par le service a l'echelle de la plateforme au cours des 60 dernieres secondes
limits.maxConcurrency number Limite de concurrence du service a l'echelle de la plateforme
limits.maxRpm number Limite par minute du service a l'echelle de la plateforme

Traiter chaque refus avec un seul helper

Retry-After est defini sur les limites de forfait qui valent la peine d'attendre, retry_after_seconds est present dans leur corps, et retryAfter se trouve dans les corps de la plateforme. Lisez ces trois elements dans cet ordre, et interrompez le traitement pour les limites de forfait qu'aucune attente ne pourra debloquer :

import time
import requests

# Plan limits that a short wait never clears.
STOP_ON = {
    "plan_limit_feature",
    "plan_limit_premium",
    "plan_limit_browser_daily",
    "plan_limit_credits",
    "plan_limit_bandwidth",
}

def wait_seconds(resp, attempt):
    header = resp.headers.get("Retry-After")
    if header and header.isdigit():
        return int(header)
    try:
        body = resp.json()
    except ValueError:
        return 2 ** attempt
    return body.get("retry_after_seconds") or body.get("retryAfter") or 2 ** attempt

def fetch(url, api_key, max_retries=5):
    for attempt in range(max_retries):
        resp = requests.post(
            "https://eu.api.foura.ai/api/single/",
            headers={"X-API-Key": api_key, "Content-Type": "application/json"},
            json={"method": "GET", "url": url},
        )

        limit = resp.headers.get("X-FourA-Limit")
        if limit in STOP_ON:
            raise RuntimeError(f"stopped by plan limit {limit}: {resp.json().get('error')}")

        if resp.status_code in (429, 503):
            time.sleep(wait_seconds(resp, attempt))
            continue

        return resp

    raise RuntimeError("Max retries exceeded")

Un quota quotidien ne se réinitialise pas avant plusieurs heures, et un quota de période ne revient pas avant plusieurs jours, traitez-les donc comme un arrêt plutôt que comme une simple attente. Lisez resets_at dans le body si vous souhaitez planifier la prochaine exécution.

Conseils

  • Plafonnez le nombre de requests simultanées en cours au lieu de réessayer un lot refusé. Une tempête de retries transforme un 429 en une multitude d'erreurs.
  • Lisez X-FourA-Limit en premier. Il vous indique en une seule chaîne si la limite vient de votre forfait ou de la plateforme, et aucun refus de la plateforme ne le définit.
  • Ne codez pas les valeurs en dur. Chaque response liée à une limite de forfait indique le plafond qui a provoqué le refus, et Usage & Limits les affiche toutes.
  • retryAfter pour les limites de la plateforme est fixe selon le type : 2 secondes pour la concurrence, 5 pour le RPM, 60 pour la maintenance.
  • Effectuez une correspondance sur error pour distinguer les deux types d'erreurs 503. Les deux formats contiennent current et limits, donc une simple vérification de présence de ces champs interprétera une maintenance comme un problème de concurrence.
  • Un 403 avec X-FourA-Limit concerne votre forfait, et non le site cible. Le site cible n'a jamais répondu.

Le port proxy a ses propres limites

Tout ce qui précède concerne l'API JSON. Le trafic envoyé via proxy.foura.ai est soumis à un ensemble distinct de limites de forfait, exprimées dans une unité différente : tunnels ouverts simultanément, ouvertures de tunnel par minute, et trafic standard pour la période de facturation. Ces refus arrivent sous forme de statut HTTP avec un header X-Foura-Error plutôt qu'avec un body JSON, car une requête CONNECT ne comporte pas de body pour en contenir un. Consultez Proxy Port pour le tableau des statuts et How Your Plan Is Metered pour savoir sur quel pool sont décomptés les gigaoctets du port.

Pages connexes

Mis à jour : 30 septembre 2026