# Limiter le débit des appels à une API tierce depuis une extension WordPress

> Une extension qui synchronise les stocks d'un client avec un fournisseur externe s'est fait suspendre sa clé API pour excès de requêtes. Voici comment poser un compteur par fenêtre glissante.

- Auteur : Clément Hadrot
- Publié le : 2022-05-15
- Mis à jour le : 2022-05-15
- Catégorie : Extensions
- URL : https://wpmoderne.dev.wordpress-developpement.fr/extensions/limiter-debit-appels-api-tierce-extension/

## L’essentiel

- Une fenêtre glissante compte les appels sur une durée mobile, pas sur un créneau fixe
- Un compteur basé sur les transients suffit sans infrastructure additionnelle
- Il vaut mieux ralentir son propre traitement que de perdre l'accès à l'API

Une extension de synchronisation d'inventaire connecte une boutique WooCommerce à l'ERP d'un grossiste en fournitures de bureau. Chaque modification de stock côté ERP déclenche un appel vers l'API du grossiste pour récupérer le détail de l'article concerné. En période de forte activité, plusieurs centaines de modifications peuvent survenir en quelques minutes, et l'extension les traite au fil de l'eau, sans aucune limitation, jusqu'au jour où le grossiste suspend la clé API du client pour dépassement répété de son quota contractuel de 60 requêtes par minute.

Rétablir l'accès a nécessité un appel au support du fournisseur et une promesse écrite de mise en conformité. La correction technique retenue introduit un compteur de requêtes par fenêtre glissante, qui ralentit le traitement de l'extension elle-même plutôt que de risquer un nouveau dépassement.

## Pourquoi une fenêtre glissante plutôt qu'un simple compteur par minute

Un compteur naïf remis à zéro chaque minute pile laisse un angle mort classique : rien n'empêche d'envoyer 60 requêtes à la 59e seconde d'une minute, puis 60 autres à la première seconde de la minute suivante, soit 120 requêtes en l'espace de deux secondes, largement au-dessus du débit réellement toléré par le fournisseur. Une fenêtre glissante évite ce problème en comptant les requêtes sur les soixante dernières secondes réelles, quel que soit l'instant de la mesure.

## Implémentation avec l'API objet

> L'essentiel à retenir : Une fenêtre glissante compte les appels sur une durée mobile, pas sur un créneau fixe ; Un compteur basé sur les transients suffit sans infrastructure additionnelle ; Il vaut mieux ralentir son propre traitement que de perdre l'accès à l'API

```
function appel_api_grossiste_autorise(): bool {
    $cle_compteur = 'grossiste_appels_' . floor( time() / 10 ); // fenêtres de 10 secondes
    $fenetres_a_verifier = array();

    for ( $i = 0; $i < 6; $i++ ) {
        $fenetres_a_verifier[] = 'grossiste_appels_' . ( floor( time() / 10 ) - $i );
    }

    $total = 0;
    foreach ( $fenetres_a_verifier as $cle ) {
        $total += (int) get_transient( $cle );
    }

    return $total < 60;
}

function enregistrer_appel_api_grossiste(): void {
    $cle = 'grossiste_appels_' . floor( time() / 10 );
    $valeur_actuelle = (int) get_transient( $cle );

    set_transient( $cle, $valeur_actuelle + 1, 70 );
}
```

Cette implémentation découpe le temps en fenêtres de dix secondes et additionne les six dernières fenêtres, ce qui approxime une fenêtre glissante de soixante secondes avec une granularité suffisante pour un usage pratique, sans la complexité d'un algorithme de type *token bucket* complet.

## Intégrer la limitation dans le flux de traitement

```
function synchroniser_article_avec_grossiste( int $article_id ) {
    if ( ! appel_api_grossiste_autorise() ) {
        // On reporte le traitement plutôt que de risquer un dépassement de quota.
        as_schedule_single_action(
            time() + 15,
            'synchro_article_grossiste_differee',
            array( 'article_id' => $article_id ),
            'synchro-grossiste'
        );
        return;
    }

    enregistrer_appel_api_grossiste();

    $reponse = wp_remote_get( construire_url_grossiste( $article_id ) );
    // Traitement de la réponse...
}

add_action( 'synchro_article_grossiste_differee', 'synchroniser_article_avec_grossiste' );
```

Plutôt que de faire attendre le processus PHP en cours avec un `sleep()`, ce qui bloquerait inutilement une requête et consommerait un worker PHP pendant l'attente, le traitement reporté s'appuie sur Action Scheduler pour replanifier l'appel quinze secondes plus tard, période durant laquelle le quota aura eu le temps de se libérer.

### Ce qu'il faut éviter

- Ne jamais fixer la limite locale exactement au quota contractuel : viser 90 % du quota annoncé laisse une marge pour d'autres intégrations qui utiliseraient la même clé API en parallèle, par exemple un script de reporting exécuté manuellement.
- Ne pas oublier que sur une installation multisite, plusieurs sites peuvent partager la même clé API sans le savoir : le compteur doit alors être partagé, pas recalculé indépendamment par site.
- Vérifier les en-têtes de réponse renvoyés par l'API, quand ils existent, comme `X-RateLimit-Remaining` : ce sont des informations plus fiables que n'importe quel compteur local reconstruit côté client.

## Lire les en-têtes du fournisseur quand ils existent

```
$reponse = wp_remote_get( $url );
$restant = wp_remote_retrieve_header( $reponse, 'x-ratelimit-remaining' );

if ( '' !== $restant && (int) $restant < 5 ) {
    // On ralentit volontairement même si notre propre compteur pense avoir de la marge.
    as_schedule_single_action( time() + 30, 'synchro_article_grossiste_differee', array( 'article_id' => $article_id ) );
    return;
}
```

Cette double vérification, compteur local et en-tête distant quand il est disponible, réduit encore le risque d'un dépassement, en particulier si le compteur local dérive légèrement à cause d'un redémarrage du serveur ou d'un cache d'objet non persistant qui perd son état entre deux requêtes.

> Un quota d'API n'est pas une limite théorique à ignorer tant qu'on ne l'a pas atteinte, c'est une clause contractuelle dont le dépassement se paie en accès coupé.

## En résumé

Un compteur par fenêtre glissante basé sur des transients suffit, dans la grande majorité des cas, à protéger une extension d'un dépassement de quota auprès d'un fournisseur tiers, sans nécessiter d'infrastructure dédiée. Combiné à une lecture des en-têtes de débit quand le fournisseur les expose, et à un report des appels via Action Scheduler plutôt qu'une attente bloquante, ce mécanisme évite la sanction la plus coûteuse pour un client : la suspension pure et simple de sa clé d'accès.
