Drei Endpunkte, ein Auth-Header, ein JSON-Body. Eine URL-Shortener-API ist eine der einfachsten Integrationen auf jeder Backlog-Liste, und das Quickstart-Tutorial bringt dich in wenigen Minuten zu einem funktionierenden Kurzlink. Was das Quickstart-Tutorial überspringt, ist alles, was passiert, wenn die Integration unter Last läuft: der Rate Limiter, der zurückdrückt; ein transientes 503 mitten im Batch; eine Job-Queue, die dieselbe Nachricht zweimal liefert. Fehler dabei erzeugen doppelte Links, verlorene Arbeit und einen 429-Sturm, der die Lage verschlimmert.
Dieser Beitrag ist der Produktionshärtungs-Begleiter zum API-Quickstart. Er behandelt die drei Mechanismen, die eine Demo von einer zuverlässigen Integration trennen: Rate Limits und wie du dich dagegen schrittweise verhältst, welche Fehler du erneut versuchst und wie du Backoff betreibst, sowie Idempotency-Keys, die einen Retry daran hindern, einen zweiten Link zu erstellen. Die Beispiele verwenden Elidos API, aber die Muster gelten für jede sauber aufgebaute Link-Shortener-API. Wenn du Kurzlinks als Infrastruktur behandelst, die du per Code verwaltest, findest du den weiteren Kontext dazu in Kurzlinks als Terraform.
Rate Limits: ein Token-Bucket und drei Header#
Elido dosiert die API mit einem Token-Bucket, der per Workspace gilt. Die veröffentlichten nachhaltigen Raten sind 10 Anfragen pro Sekunde im Free-Plan, 100 im Pro-Plan, 500 im Business-Plan und eine verhandelte Obergrenze im Enterprise-Plan. Pro hat eine Burst-Kapazität von 200 - ein voller Bucket erlaubt also 200 Anfragen auf einmal, bevor sich die Rate wieder auf die nachhaltigen 100 pro Sekunde einpendelt. Die meisten Link-Erstellungs-Jobs passen in den Burst und spüren das Limit gar nicht.
Du musst nicht raten, wo du stehst. Jede Antwort enthält drei Header:
X-RateLimit-Limit- die aktuelle Sekundenobergrenze.X-RateLimit-Remaining- verbleibende Token im aktuellen Fenster.X-RateLimit-Reset- der Unix-Timestamp, wann der Bucket wieder aufgefüllt wird.
Ein wohlerzogener Client liest X-RateLimit-Remaining und verlangsamt sich, bevor er null erreicht - anstatt in eine Wand aus 429-Fehlern zu rasen und erst danach zu reagieren. Proaktives Pacing hält den Durchsatz gleichmäßig; reaktives Wiederholen nach jeder Ablehnung verschwendet Round Trips und erzeugt bei gleichzeitigem Retry aller Clients eine Thundering Herd.
Wenn du wirklich Tausende von Links erstellen musst, loope nicht über den Single-Create-Endpunkt. POST /v1/links/bulk akzeptiert bis zu 1000 Links in einer Anfrage und zählt als eine einzelne Einheit gegen das Rate Limit. Ein Bulk-Aufruf bewegt tausend Links für den Preis eines Tokens; tausend Einzelaufrufe verbrauchen tausend Token und den Großteil deines Bursts. Der Bulk-Pfad ist der Weg, über den der Google-Sheets-Import die Links einer ganzen Kampagne bewegt, ohne den Limiter auszulösen.
Ein 429 Too Many Requests - der Status, den RFC 6585 genau dafür reserviert - kommt mit einem retry_after-Wert zurück, der dir sagt, wie viele Sekunden du warten sollst. Respektiere ihn. Diese Zahl ist der Limiter, der dir genau mitteilt, wann ein Token verfügbar sein wird - bessere Information als jede Schätzung, die dein Backoff produzieren würde.
Retries: Welche Codes, und wie du Backoff betreibst#
Nicht jeder Fehler ist es wert, erneut versucht zu werden, und den falschen Fehler zu wiederholen ist, wie aus einem kleinen Ausfall ein Ausfall wird. Sortiere die Antworten in zwei Stapel.
Wiederhole diese, weil sie transient sind: 429 (du warst zu schnell), sowie 500, 502, 503, 504 (ein server- oder gateweyseitiger Fehler, der sich von selbst klären könnte). Wiederhole diese nicht, weil dieselbe Anfrage identisch scheitern wird: 400 (das Payload ist ungültig), 401 (der Token fehlt oder ist falsch), 403 (dem Token fehlt der Scope), 404 (die Ressource existiert nicht oder gehört dir nicht) und 409 (ein Slug-Konflikt oder ein Bearbeitungskonflikt mit veralteter Version). Der erste Stapel bedeutet „warte und versuche es erneut". Der zweite bedeutet „behebe den Code oder die Eingabe". Ein 400 in einer engen Schleife zu wiederholen macht aus einem Bug einen Denial-of-Service-Angriff auf sich selbst.
Für die wiederholbaren Codes ist der relevante Algorithmus exponentielles Backoff mit Jitter. Reines exponentielles Backoff - die Wartezeit jedes Versuchs verdoppeln - synchronisiert dennoch Clients, weil jeder Client, der zum selben Moment scheiterte, auch zu denselben Momenten wiederholt. Zufälligkeit hinzuzufügen verteilt sie. AWSs Artikel zu Exponential Backoff and Jitter ist die kanonische Referenz und zeigt, warum die Jitter-Variante den Contention drastisch reduziert. Eine kompakte TypeScript-Version:
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++;
}
}
Drei Dinge machen das sicher statt gefährlich. Es begrenzt Versuche, sodass ein anhaltender Fehler laut scheitert statt ewig zu drehen. Es respektiert Retry-After, wenn der Server ihn sendet, und greift nur dann auf berechnetes Backoff zurück, wenn er das nicht tut. Und es jittert, sodass eine Flotte von Workern, die sich vom selben Einbruch erholen, nicht im Gleichschritt ansturmt. Die offiziellen SDKs implementieren diese Policy sofort aus der Box - @elido/sdk, elido-python und der Go-Client wiederholen genau die fünf transienten Codes mit jittertem Backoff - was der Hauptgrund ist, ein SDK einem selbst gebauten HTTP-Client vorzuziehen.
Es gibt eine Regel, die Retries mit dem nächsten Abschnitt verbindet: Ein Retry eines Create ist nur sicher, wenn das Create idempotent ist. Andernfalls riskiert jeder Retry einen zweiten Link.
Idempotenz: Wie du keine doppelten Kurzlinks erstellst#
Der klassische Fehler sieht so aus. Dein Worker erstellt einen Kurzlink, der Link wird erstellt, aber die 200-Antwort kommt nie an - die Verbindung bricht auf dem Rückweg ab. Der Worker sieht einen Timeout, nimmt Fehler an und wiederholt. Jetzt hast du zwei Links für eine Kampagne. In großem Maßstab füllt sich das Dashboard mit /foo, /foo-1, /foo-2, und die Duplikate verzerren jeden nachgelagerten Report.
Idempotency-Keys schließen diese Lücke. Sende einen Idempotency-Key-Header bei einer mutierenden Anfrage - einen beliebigen String bis zu 255 Zeichen - und der Server speichert die Antwort dagegen. Präsentiere denselben Key erneut und du bekommst die ursprüngliche Antwort zurück, Status-Code und Body, ohne dass die Operation zweimal ausgeführt wird. Das Muster ist dasselbe, das Stripe für idempotente Anfragen dokumentiert, und es ist der Standardweg, um ein unzuverlässiges Netzwerk für Schreiboperationen sicher zu machen.
Das Detail, das den Unterschied macht, ist, woher der Key kommt. Generiere keinen zufälligen Key pro Versuch - das unterläuft den Sinn, weil jeder Retry dann wie eine neue Operation aussieht. Leite ihn von einer stabilen Geschäfts-ID ab, sodass dieselbe logische Aktion stets denselben Key erzeugt:
const link = await elido.links.create(
{ destinationUrl: order.landingUrl },
{ idempotencyKey: `order-${order.id}-link` },
);
Jetzt trägt ein Retry desselben Jobs order-12345-link erneut, trifft die gespeicherte Antwort und gibt den bereits existierenden Link zurück. Genau ein Link pro Bestellung, egal wie oft die Queue wieder ausliefert. Das ist es, was den oben stehenden Backoff-Loop sicher mit Creates kombinierbar macht: der Retry und der Idempotency-Key sind zwei Hälften derselben Garantie.
Zwei Grenzen im Hinterkopf behalten. Der Key ist per Workspace gültig: derselbe Key in zwei Workspaces erstellt zwei Links - das ist korrekt für eine mandantenfähige API, überrascht aber Teams, die Keys als global annehmen. Und der Cache ist nicht unbegrenzt - bei Elido hält er 24 Stunden, verschlüsselt nach (workspace, key). Ein Retry innerhalb des Fensters dedupliziert; ein Retry drei Tage später, von einem feststeckenden Job, der endlich abgearbeitet wird, erstellt einen frischen Link. Verlasse dich bei mehrtägigen Batches nicht allein auf den Key. Persistiere die vom ersten Erfolg zurückgegebene Link-ID und schlage sie nach, bevor du erneut einen Create absetzt. Die IETF standardisiert diesen Header im Idempotency-Key-Draft, und der 24-Stunden-Fenster-Vorbehalt wird dort ebenfalls erwähnt.
Wenn du heute eine API-Integration verdrahtest und willst, dass sie ihre eigenen Retries überlebt: Starte mit einem kostenlosen Workspace, generiere ein Service-Account-Token und setze einen Idempotency-Key auf deinen allerersten Create - anstatt ihn nachzurüsten, wenn die Duplikate auftauchen.
Alles zusammengefügt#
Ein produktionsreifer Create-Aufruf stapelt die drei Mechanismen. Pace gegen die Rate-Limit-Header, damit du selten ein 429 siehst. Hülle den Aufruf in jittertes Backoff, das nur die transienten Codes wiederholt und Retry-After respektiert. Trage einen Idempotency-Key, der von einer Geschäfts-ID abgeleitet ist, sodass der Retry sicher ist. Mit dem offiziellen SDK kommen die ersten beiden gratis - du lieferst nur den Key:
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
}
}
Das ist nichts Exotisches. Es ist dieselbe Disziplin, die jede schreibintensive API verdient - angewandt auf Links. Die Belohnung ist eine Integration, die unter Last das Richtige tut, anstatt still dein Link-Inventar zu beschädigen. Für die Lese-Seite derselben API - Click-Daten herausziehen, ohne den Limiter zu belasten - sind die Kompromisse in Webhooks vs. Polling für Click-Tracking behandelt, und die vollständige Endpunkt-Oberfläche findest du auf der Seite API und SDKs sowie in der Entwickler-Lösungsübersicht.
Weitere Artikel im Blog#
Elido testen
URL einfügen, kurzer Link in Sekunden
Kein Konto nötig. Link bleibt 30 Tage aktiv. Konto erstellen, um ihn dauerhaft zu behalten.
Kostenlos, keine Anmeldung erforderlich · 2 pro Tag