Três endpoints, um header de autenticação, um corpo JSON. Uma API de encurtador de URL é uma das integrações mais simples em qualquer backlog, e o quickstart permite-lhe ter um link curto funcional em poucos minutos. O que o quickstart omite é tudo o que acontece quando a integração opera em volume: o limitador de taxa a rejeitar pedidos, um 503 transitório a meio de um lote, uma fila de trabalhos que entrega a mesma mensagem duas vezes. Erre nesses pontos e obterá links duplicados, trabalho perdido e uma tempestade de 429 que torna tudo ainda pior.
Este artigo é o complemento de endurecimento para produção do quickstart de API. Aborda os três mecanismos que separam uma demo de uma integração fiável: limites de taxa e como controlar o ritmo contra eles, quais os erros a retentar e como fazer backoff, e chaves de idempotência que impedem uma retentativa de criar um segundo link. Os exemplos usam a API do Elido, mas os padrões são os mesmos em qualquer API de encurtador bem construída. Se trata os links curtos como infraestrutura gerida por código, o argumento mais alargado está em links curtos como Terraform.
Limites de Taxa: um Token Bucket e Três Headers#
O Elido mede a API com um token bucket, com âmbito por workspace. As taxas sustentadas publicadas são 10 pedidos por segundo no Free, 100 no Pro, 500 no Business, e um teto negociado no Enterprise. O Pro tem uma capacidade de burst de 200, o que significa que um bucket cheio permite disparar 200 pedidos de uma só vez antes de a taxa estabilizar nos 100 por segundo sustentados. A maioria dos trabalhos de criação de links cabe no burst e nunca sente o limite.
Não precisa de adivinhar onde está. Cada resposta inclui três headers:
X-RateLimit-Limit- o teto atual por segundo.X-RateLimit-Remaining- tokens restantes na janela atual.X-RateLimit-Reset- o timestamp Unix quando o bucket se reabastece.
Um cliente bem comportado lê X-RateLimit-Remaining e abranda antes de atingir zero, em vez de colidir com uma barreira de 429 e reagir após o facto. O controlo de ritmo proativo mantém o débito fluido; o retrying reativo após cada rejeição desperdiça viagens de ida e volta e, se todos os clientes retentam ao mesmo instante, cria uma manada trovoada.
Quando genuinamente precisa de criar milhares de links, não itere o endpoint de criação individual. POST /v1/links/bulk aceita até 1000 links num único pedido e conta como uma única unidade contra o limite de taxa. Uma chamada de bulk move mil links pelo custo de um token; mil chamadas individuais consomem mil tokens e a maior parte do seu burst. O endpoint de bulk é a forma como a importação do Google Sheets move links de uma campanha inteira sem acionar o limitador.
Um 429 Too Many Requests - o status que o RFC 6585 reserva precisamente para isto - é devolvido com um valor retry_after que indica quantos segundos esperar. Respeite-o. Esse número é o limitador a informá-lo exatamente quando um token estará disponível, o que é melhor do que qualquer estimativa que o seu backoff produziria.
Retentativas: Quais Códigos e Como Fazer Backoff#
Nem todos os erros valem a pena retentar, e retentar o errado é como uma falha pequena se torna uma interrupção. Divida as respostas em dois grupos.
Retente estes, porque são transitórios: 429 (foi demasiado rápido), e 500, 502, 503, 504 (uma falha do lado do servidor ou do gateway que pode resolver-se por si). Não retente estes, porque o mesmo pedido vai falhar de forma idêntica: 400 (o payload é inválido), 401 (o token está ausente ou errado), 403 (o token não tem o scope), 404 (o recurso não existe ou não é seu), e 409 (conflito de slug ou edição com versão obsoleta). O primeiro grupo é "aguarde e tente novamente." O segundo é "corrija o código ou o input." Retentar um 400 em ciclo fechado transforma um bug num ataque de negação de serviço contra si próprio.
Para os códigos retentáveis, o algoritmo que importa é o backoff exponencial com jitter. O backoff exponencial simples - duplicar a espera a cada tentativa - ainda sincroniza clientes, porque todos os que falharam no mesmo momento também retentam nos mesmos momentos. Adicionar aleatoriedade dispersa-os. O artigo da AWS sobre exponential backoff and jitter é a referência canónica e mostra porque a versão com jitter reduz dramaticamente a contenção. Uma versão compacta em 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++;
}
}
Três aspetos tornam isto seguro em vez de perigoso. Limita as tentativas, para que uma falha persistente falhe de forma clara em vez de girar indefinidamente. Respeita Retry-After quando o servidor o envia, recorrendo ao backoff calculado apenas quando não o faz. E aplica jitter, para que um conjunto de workers a recuperar do mesmo problema não se precipite em uníssono. Os SDKs oficiais implementam esta mesma política de raiz - @elido/sdk, elido-python e o cliente Go retentam exatamente os cinco códigos transitórios com backoff jitterizado - o que é a principal razão para usar um SDK em vez de um cliente HTTP feito à mão.
Há uma regra que liga as retentativas à secção seguinte: uma retentativa de criação só é segura se a criação for idempotente. Caso contrário, cada retentativa arrisca um segundo link.
Idempotência: Como Não Criar Links Duplicados#
A falha clássica tem este aspeto. O seu worker cria um link curto, o link é criado, mas o 200 nunca chega de volta - a ligação cai na viagem de retorno. O worker vê um timeout, assume falha e retenta. Agora tem dois links para uma campanha. Em escala, o dashboard enche-se de /foo, /foo-1, /foo-2, e os duplicados distorcem todos os relatórios a jusante.
As chaves de idempotência fecham essa lacuna. Envie um header Idempotency-Key num pedido de mutação - qualquer string até 255 caracteres - e o servidor guarda a resposta associada a ela. Apresente a mesma chave novamente e recebe a resposta original de volta, código de status e corpo, sem a operação ser executada duas vezes. O padrão é o mesmo que a Stripe documenta para pedidos idempotentes, e é a forma padrão de tornar uma rede não fiável segura para escritas.
O detalhe que decide o sucesso ou falha é a origem da chave. Não gere uma chave aleatória por tentativa - isso anula o propósito, porque cada retentativa parece então uma operação nova. Derive-a de um identificador de negócio estável para que a mesma ação lógica produza sempre a mesma chave:
const link = await elido.links.create(
{ destinationUrl: order.landingUrl },
{ idempotencyKey: `order-${order.id}-link` },
);
Agora uma retentativa do mesmo trabalho carrega order-12345-link novamente, encontra a resposta armazenada e devolve o link que já existe. Exatamente um link por encomenda, independentemente de quantas vezes a fila reentrega. É isto que permite combinar o ciclo de backoff acima com criações de forma segura: a retentativa e a chave de idempotência são duas metades da mesma garantia.
Dois limites a ter em conta. A chave tem âmbito por workspace: a mesma chave em dois workspaces cria dois links, o que é correto para uma API multi-tenant mas surpreende equipas que assumem que as chaves são globais. E a cache não é permanente - no Elido dura 24 horas com âmbito em (workspace, key). Uma retentativa dentro da janela desduplicica; uma retentativa três dias depois, de um trabalho bloqueado que finalmente processou, criará um link novo. Para lotes de vários dias, não confie apenas na chave. Persista o ID do link devolvido pelo primeiro sucesso e consulte-o antes de reeditar a criação. O IETF tem estado a normalizar este header no rascunho Idempotency-Key, e a ressalva da janela de 24 horas está também mencionada lá.
Se está a implementar uma integração de API hoje e quer que sobreviva às suas próprias retentativas, comece num workspace gratuito, gere um token de conta de serviço e coloque uma chave de idempotência na sua primeira criação em vez de a acrescentar depois de os duplicados aparecerem.
Juntar Tudo#
Uma chamada de criação de qualidade para produção é os três mecanismos empilhados. Controle o ritmo contra os headers de limite de taxa para raramente atingir 429. Envolva a chamada em backoff jitterizado que retenta apenas os códigos transitórios e respeita Retry-After. Carregue uma chave de idempotência derivada de um ID de negócio para que a retentativa seja segura. Com o SDK oficial, os primeiros dois vêm incluídos e fornece apenas a chave:
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
}
}
Nada disto é exótico. É a mesma disciplina que qualquer API com muitas escritas merece, aplicada a links. A recompensa é uma integração que se comporta corretamente sob carga em vez de corromper silenciosamente o seu inventário de links. Para o lado de leitura da mesma API - extrair dados de cliques sem sobrecarregar o limitador - os compromissos estão em webhooks versus polling para rastreamento de cliques, e a superfície completa de endpoints está na página de API e SDKs e na visão geral de soluções para programadores.
Relacionado no Blog#
Experimente Elido
Cole uma URL, obtenha um link curto
Sem cadastro. O link vive 30 dias. Cadastre-se para mantê-lo para sempre.
Grátis, sem necessidade de registo · 2 por dia