Tre endpoint, un header di autenticazione, un corpo JSON. Un'API per abbreviatori di URL è una delle integrazioni più semplici di qualsiasi backlog, e il quickstart ti porta a un link abbreviato funzionante in pochi minuti. Quello che il quickstart salta è tutto ciò che accade quando l'integrazione gira a volume: il rate limiter che rispedisce indietro, un 503 transitorio a metà batch, una coda di job che consegna lo stesso messaggio due volte. Sbagliare questi punti significa link duplicati, lavoro perso e una tempesta di 429 che peggiora le cose.
Questo articolo è il complemento di hardening in produzione al quickstart API. Copre i tre meccanismi che separano una demo da un'integrazione affidabile: i rate limit e come regolarsi su di essi, quali errori riprovare e come fare backoff, e le chiavi di idempotenza che impediscono a un retry di creare un secondo link. Gli esempi usano l'API di Elido, ma i pattern sono gli stessi con qualsiasi API per link shortener ben costruita. Se tratti i link abbreviati come infrastruttura gestita dal codice, il caso più ampio per questo approccio è in link abbreviati come Terraform.
Rate limit: un token bucket e tre header#
Elido regola l'API con un token bucket, con scope per workspace. I tassi sostenuti pubblicati sono 10 richieste al secondo su Free, 100 su Pro, 500 su Business e un tetto negoziato su Enterprise. Pro ha una capacità di burst di 200, il che significa che un bucket pieno ti permette di sparare 200 richieste in una volta prima che il tasso torni ai 100 al secondo sostenuti. La maggior parte dei job di creazione di link rientra nel burst e non sente mai il limite.
Non devi indovinare a che punto sei. Ogni risposta porta tre header:
X-RateLimit-Limit- il tetto corrente per secondo.X-RateLimit-Remaining- i token rimasti nella finestra corrente.X-RateLimit-Reset- il timestamp Unix quando il bucket si ricarica.
Un client ben educato legge X-RateLimit-Remaining e rallenta prima di arrivare a zero, invece di correre contro un muro di 429 e reagire dopo. Il pacing proattivo mantiene la throughput fluida; il retry reattivo dopo ogni rifiuto spreca round trip e, se ogni client riprova nello stesso istante, genera un thundering herd.
Quando hai davvero bisogno di creare migliaia di link, non fare un loop sull'endpoint di creazione singola. POST /v1/links/bulk accetta fino a 1000 link in una richiesta e conta come una sola unità rispetto al rate limit. Una chiamata bulk sposta mille link al costo di un token; mille chiamate singole bruciano mille token e la maggior parte del tuo burst. Il percorso bulk è quello che usa l'importazione da Google Sheets per spostare i link di una campagna senza far scattare il limiter.
Un 429 Too Many Requests - lo stato che la RFC 6585 riserva esattamente a questo - torna con un valore retry_after che ti dice quanti secondi attendere. Rispettalo. Quel numero è il limiter che ti dice esattamente quando sarà disponibile un token - un'informazione migliore di qualsiasi stima prodotta dal tuo backoff.
Retry: quali codici e come fare backoff#
Non ogni errore vale la pena di essere riprovato, e riprovare quello sbagliato è come un piccolo guasto diventa un'interruzione. Dividi le risposte in due gruppi.
Riprova questi, perché sono transitori: 429 (sei andato troppo veloce), e 500, 502, 503, 504 (un fault lato server o gateway che potrebbe risolversi da solo). Non riprovare questi, perché la stessa richiesta fallirà in modo identico: 400 (il payload non è valido), 401 (il token manca o è sbagliato), 403 (il token non ha lo scope), 404 (la risorsa non esiste o non è tua), e 409 (un conflitto di slug o una modifica con versione obsoleta). Il primo gruppo è "aspetta e riprova." Il secondo è "correggi il codice o l'input." Riprovare un 400 in un loop stretto trasforma un bug in un attacco denial-of-service contro te stesso.
Per i codici riprovabili, l'algoritmo che conta è il backoff esponenziale con jitter. Il backoff esponenziale puro - raddoppia l'attesa a ogni tentativo - sincronizza comunque i client, perché ogni client che ha fallito nello stesso momento riprova anche negli stessi momenti. Aggiungere casualità li distribuisce. Il post di AWS su exponential backoff and jitter è il riferimento canonico e mostra perché la versione con jitter riduce drasticamente la contesa. Una versione compatta in 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++;
}
}
Tre cose rendono questo codice sicuro invece che pericoloso. Limita i tentativi, così un fault persistente fallisce in modo evidente invece di girare all'infinito. Rispetta Retry-After quando il server lo invia, ricorrendo al backoff calcolato solo quando non lo fa. E usa il jitter, così una flotta di worker che si riprendono dallo stesso problema non si precipita in modo sincronizzato. Gli SDK ufficiali implementano questa stessa politica out of the box - @elido/sdk, elido-python e il client Go riprovano esattamente i cinque codici transitori con jittered backoff - ed è il motivo principale per scegliere un SDK rispetto a un client HTTP scritto a mano.
C'è una regola che collega i retry alla sezione successiva: un retry di un create è sicuro solo se il create è idempotente. Altrimenti ogni retry rischia di creare un secondo link.
Idempotenza: come non creare link duplicati#
Il fallimento classico si presenta così. Il tuo worker crea un link abbreviato, il link viene creato, ma il 200 non torna mai - la connessione cade durante il viaggio di ritorno. Il worker vede un timeout, assume un fallimento e riprova. Ora hai due link per una campagna. Su larga scala, la dashboard si riempie di /foo, /foo-1, /foo-2, e i duplicati distorcono ogni report a valle.
Le chiavi di idempotenza chiudono quel gap. Invia un header Idempotency-Key su una richiesta mutante - qualsiasi stringa fino a 255 caratteri - e il server memorizza la risposta associata ad essa. Presenta la stessa chiave di nuovo e ricevi la risposta originale, codice di stato e body, senza che l'operazione venga eseguita due volte. Il pattern è lo stesso che Stripe documenta per le richieste idempotenti, ed è il modo standard per rendere una rete inaffidabile sicura per le scritture.
Il dettaglio che determina il successo o il fallimento è da dove viene la chiave. Non generare una chiave casuale per ogni tentativo - questo vanifica lo scopo, perché ogni retry sembrerebbe una nuova operazione. Derivala da un identificatore di business stabile così la stessa azione logica produce sempre la stessa chiave:
const link = await elido.links.create(
{ destinationUrl: order.landingUrl },
{ idempotencyKey: `order-${order.id}-link` },
);
Ora un retry dello stesso job porta di nuovo order-12345-link, trova la risposta memorizzata e restituisce il link già esistente. Esattamente un link per ordine, indipendentemente da quante volte la coda riconsegna. Questo è ciò che ti permette di combinare in modo sicuro il loop di backoff precedente con i create: il retry e la chiave di idempotenza sono due metà della stessa garanzia.
Due limiti da tenere a mente. La chiave ha scope per workspace: la stessa chiave in due workspace crea due link, il che è corretto per un'API multi-tenant ma sorprende i team che assumono che le chiavi siano globali. E la cache non dura per sempre - su Elido tiene per 24 ore con chiave su (workspace, key). Un retry entro la finestra deduplica; un retry tre giorni dopo, da un job bloccato che finalmente si svuota, creerà un link nuovo. Per i batch multi-giorno, non fare affidamento solo sulla chiave. Persisti l'ID del link restituito dal primo successo e cerca quello prima di riemettere il create. L'IETF sta standardizzando questo header nella bozza Idempotency-Key, e la limitazione della finestra di 24 ore è citata anche lì.
Se stai cablando un'integrazione API oggi e vuoi che sopravviva ai propri retry, inizia su un workspace gratuito, genera un token di service account e metti una chiave di idempotenza sul tuo primissimo create invece di aggiungerla dopo che i duplicati compaiono.
Mettere tutto insieme#
Una chiamata di create di livello produzione è i tre meccanismi impilati. Pacing sugli header del rate limit così raramente colpisci 429. Avvolgi la chiamata in un backoff con jitter che riprova solo i codici transitori e rispetta Retry-After. Porta una chiave di idempotenza derivata da un ID di business così il retry è sicuro. Con l'SDK ufficiale, i primi due vengono gratis e tu fornisci solo la chiave:
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
}
}
Niente di tutto questo è esotico. È la stessa disciplina che merita qualsiasi API ad alto volume di scritture, applicata ai link. La ricompensa è un'integrazione che fa la cosa giusta sotto carico invece di corrompere silenziosamente il tuo inventario di link. Per il lato di lettura della stessa API - estrarre i dati sui clic senza martellare il limiter - i compromessi sono in webhook vs polling per il tracciamento dei clic, e la superficie completa degli endpoint vive sulla pagina API e SDK e nella panoramica delle soluzioni per sviluppatori.
Articoli correlati sul blog#
Prova Elido
Incolla un URL, ottieni un link breve
Senza registrazione. Il link vive 30 giorni. Iscriviti per conservarlo.
Gratis, nessuna registrazione richiesta · 2 al giorno