Trois endpoints, un en-tête d'authentification, un corps JSON. L'API d'un raccourcisseur d'URL est l'une des intégrations les plus simples de tout backlog, et le guide de démarrage rapide vous donne un lien court fonctionnel en quelques minutes. Ce que le guide passe sous silence, c'est tout ce qui se produit quand l'intégration tourne à plein régime : le limiteur de débit qui repousse, un 503 transitoire au milieu d'un traitement par lots, une file de tâches qui livre le même message deux fois. Ratez ces points et vous vous retrouvez avec des liens en double, du travail perdu et une tempête de 429 qui empire les choses.
Cet article est le complément de durcissement pour la production du guide de démarrage rapide API. Il couvre les trois mécaniques qui séparent une démo d'une intégration fiable : les limites de débit et comment s'y adapter, quelles erreurs relancer et comment reculer, et les clés d'idempotence qui empêchent une relance de créer un second lien. Les exemples utilisent l'API Elido, mais les patterns sont identiques face à toute API de raccourcissement de lien bien construite. Si vous traitez les liens courts comme une infrastructure gérée par du code, l'argument général est développé dans les liens courts comme Terraform.
Limites de débit : un seau à jetons et trois en-têtes#
Elido régule l'API avec un seau à jetons, scopé par espace de travail. Les débits soutenus publiés sont de 10 requêtes par seconde en Free, 100 en Pro, 500 en Business, et un plafond négocié en Enterprise. Pro dispose d'une capacité de pointe de 200, ce qui signifie qu'un seau plein vous permet d'envoyer 200 requêtes d'un coup avant que le débit revienne à 100 par seconde en régime normal. La plupart des tâches de création de liens s'inscrivent dans la capacité de pointe et ne ressentent jamais la limite.
Pas besoin de deviner où vous en êtes. Chaque réponse porte trois en-têtes :
X-RateLimit-Limit- le plafond actuel par seconde.X-RateLimit-Remaining- les jetons restants dans la fenêtre courante.X-RateLimit-Reset- l'horodatage Unix auquel le seau se reremplit.
Un client bien comporté lit X-RateLimit-Remaining et ralentit avant d'atteindre zéro, plutôt que de foncer dans un mur de 429 et de réagir après coup. Un rythme proactif maintient un débit stable ; relancer de façon réactive après chaque rejet gaspille des allers-retours et, si tous les clients relancent au même instant, crée un effet de troupeau en panique.
Quand vous avez vraiment besoin de créer des milliers de liens, ne bouclez pas sur l'endpoint de création unitaire. POST /v1/links/bulk accepte jusqu'à 1000 liens en une seule requête et compte comme une seule unité face à la limite de débit. Un appel bulk déplace mille liens pour le coût d'un jeton ; mille appels unitaires brûlent mille jetons et la majeure partie de votre capacité de pointe. Le chemin bulk est celui qu'emprunte l'import depuis Google Sheets pour déplacer les liens d'une campagne entière sans déclencher le limiteur.
Un 429 Too Many Requests - le code que la RFC 6585 réserve exactement à cela - est retourné avec une valeur retry_after indiquant combien de secondes attendre. Respectez-la. Ce nombre est le limiteur qui vous indique précisément quand un jeton sera disponible, une information bien meilleure que n'importe quelle estimation que votre backoff produirait.
Relances : quels codes, et comment reculer#
Toute erreur ne mérite pas d'être relancée, et relancer la mauvaise, c'est transformer un petit incident en panne. Classez les réponses en deux catégories.
Relancez celles-ci, car elles sont transitoires : 429 (vous étiez trop rapide), et 500, 502, 503, 504 (une défaillance côté serveur ou passerelle qui peut se résoudre seule). Ne relancez pas celles-ci, car la même requête échouera à l'identique : 400 (la charge utile est invalide), 401 (le jeton est absent ou incorrect), 403 (le jeton n'a pas la portée requise), 404 (la ressource n'existe pas ou ne vous appartient pas), et 409 (un conflit de slug ou une modification à version obsolète). La première catégorie, c'est « attendez et réessayez ». La seconde, c'est « corrigez le code ou l'entrée ». Relancer un 400 en boucle serrée transforme simplement un bug en attaque par déni de service contre vous-même.
Pour les codes relançables, l'algorithme qui compte est le backoff exponentiel avec jitter. Le backoff exponentiel seul - doubler le délai à chaque tentative - synchronise quand même les clients, car tous ceux qui ont échoué au même moment relancent aussi aux mêmes instants. Ajouter de l'aléatoire les étale. L'article d'AWS sur le backoff exponentiel et le jitter est la référence canonique et montre pourquoi la version avec jitter réduit drastiquement la contention. Une version compacte en TypeScript :
const RETRYABLE = new Set([429, 500, 502, 503, 504]);
async function withRetry<T>(
call: () => Promise<Response>,
max = 5,
): Promise<Response> {
let attempt = 0;
while (true) {
const res = await call();
if (res.ok || !RETRYABLE.has(res.status) || attempt >= max) return res;
// Honor server guidance first; otherwise back off exponentially with full jitter.
const retryAfter = Number(res.headers.get("retry-after"));
const base =
Number.isFinite(retryAfter) && retryAfter > 0
? retryAfter * 1000
: Math.min(1000 * 2 ** attempt, 20_000);
const wait = Math.random() * base; // full jitter
await new Promise((r) => setTimeout(r, wait));
attempt++;
}
}
Trois choses rendent cela sûr plutôt que dangereux. Le nombre de tentatives est plafonné, donc une défaillance persistante échoue bruyamment au lieu de tourner indéfiniment. L'en-tête Retry-After est respecté quand le serveur l'envoie, et le backoff calculé n'est utilisé qu'en son absence. Et le jitter est appliqué, pour qu'une flotte de workers se remettant du même incident ne charge pas en cadence. Les SDK officiels implémentent cette même politique par défaut - @elido/sdk, elido-python et le client Go relancent exactement les cinq codes transitoires avec backoff jitterisé - ce qui est la principale raison de préférer un SDK à un client HTTP fait maison.
Il y a une règle qui relie les relances à la section suivante : relancer une création n'est sûr que si la création est idempotente. Sinon, chaque relance risque de créer un second lien.
Idempotence : comment ne pas créer de liens en double#
L'échec classique ressemble à ceci. Votre worker crée un lien court, le lien est créé, mais le 200 ne revient jamais - la connexion tombe au retour. Le worker voit un timeout, suppose un échec et relance. Vous avez maintenant deux liens pour une campagne. À l'échelle, le tableau de bord se remplit de /foo, /foo-1, /foo-2, et les doublons faussent tous les rapports en aval.
Les clés d'idempotence comblent cette lacune. Envoyez un en-tête Idempotency-Key sur une requête mutante - n'importe quelle chaîne jusqu'à 255 caractères - et le serveur stocke la réponse associée. Présentez la même clé à nouveau et vous obtenez la réponse originale, code de statut et corps inclus, sans que l'opération s'exécute deux fois. Le pattern est le même que celui que Stripe documente pour les requêtes idempotentes, et c'est la façon standard de rendre un réseau non fiable sûr pour les écritures.
Le détail qui fait ou défait tout, c'est l'origine de la clé. Ne générez pas une clé aléatoire par tentative - cela annule l'intérêt, car chaque relance ressemble alors à une nouvelle opération. Dérivez-la d'un identifiant métier stable pour que la même action logique produise toujours la même clé :
const link = await elido.links.create(
{ destinationUrl: order.landingUrl },
{ idempotencyKey: `order-${order.id}-link` },
);
Désormais, une relance du même travail porte à nouveau order-12345-link, atteint la réponse stockée et retourne le lien qui existe déjà. Exactement un lien par commande, peu importe combien de fois la file relivère le message. C'est ce qui vous permet de combiner la boucle de backoff ci-dessus avec des créations en toute sécurité : la relance et la clé d'idempotence sont deux moitiés de la même garantie.
Deux limites à garder en tête. La clé est scopée par espace de travail : la même clé dans deux espaces de travail crée deux liens, ce qui est correct pour une API multi-tenant mais surprend les équipes qui supposent que les clés sont globales. Et le cache n'est pas permanent - sur Elido il dure 24 heures, scopé sur (workspace, key). Une relance dans la fenêtre déduplique ; une relance trois jours plus tard, d'un travail bloqué qui a finalement été traité, créera un nouveau lien. Pour les traitements par lots sur plusieurs jours, ne comptez pas uniquement sur la clé. Persistez l'ID du lien retourné au premier succès et consultez-le avant de réémettre la création. L'IETF standardise cet en-tête dans le brouillon Idempotency-Key, et la mise en garde sur la fenêtre de 24 heures y est également mentionnée.
Si vous câblez une intégration API aujourd'hui et voulez qu'elle survive à ses propres relances, commencez avec un espace de travail gratuit, générez un jeton de compte de service, et mettez une clé d'idempotence sur votre toute première création plutôt que d'en ajouter une après l'apparition des doublons.
Tout assembler#
Un appel de création de qualité production, c'est les trois mécaniques empilées. Adaptez votre rythme aux en-têtes de limite de débit pour rarement atteindre un 429. Encapsulez l'appel dans un backoff jitterisé qui ne relance que les codes transitoires et respecte Retry-After. Portez une clé d'idempotence dérivée d'un ID métier pour que la relance soit sûre. Avec le SDK officiel, les deux premiers viennent gratuitement et vous ne fournissez que la clé :
import { Elido, ElidoRateLimitError } from "@elido/sdk";
const elido = new Elido({ token: process.env.ELIDO_TOKEN! });
export async function shortenForOrder(order: Order) {
try {
return await elido.links.create(
{ destinationUrl: order.landingUrl, tags: [`order:${order.id}`] },
{ idempotencyKey: `order-${order.id}-link` },
);
} catch (err) {
if (err instanceof ElidoRateLimitError) {
// SDK already retried with backoff; we are still limited. Defer the job.
throw new RetryableJobError(err.retryAfter);
}
throw err; // non-retryable: surface it
}
}
Rien de tout cela n'est exotique. C'est la même discipline que mérite toute API à forte activité d'écriture, appliquée aux liens. La récompense est une intégration qui fait ce qu'il faut sous charge au lieu de corrompre silencieusement votre inventaire de liens. Pour le versant lecture de la même API - récupérer les données de clics sans marteler le limiteur - les compromis sont dans webhooks versus polling pour le suivi des clics, et la surface complète des endpoints se trouve sur la page API et SDK et la vue d'ensemble des solutions développeurs.
Sur le blog#
Essayer Elido
Collez une URL, obtenez un lien court
Sans inscription. Lien actif 30 jours. Inscrivez-vous pour le garder pour toujours.
Gratuit, sans inscription · 2 par jour