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 lhe permite 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 rejeitando 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 confiá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 gerenciada por código, o argumento mais alargado está em links curtos como Terraform.
Limites de Taxa: um Token Bucket e um Header Retry-After
O Elido mede a API com um token bucket, com âmbito por workspace. As taxas sustentadas são 10 pedidos por segundo no Free, 30 no Pro e 100 no Business, com capacidades de burst de 20, 60 e 200. No Pro, por exemplo, um bucket cheio permite disparar 60 pedidos de uma vez antes de a taxa voltar aos 30 por segundo sustentados. Por cima existe um bucket separado por chave de API, para que uma chave ruidosa não consuma todo o workspace. A maioria dos trabalhos de criação de links cabe no burst e nunca sente o limite.
Não tem de adivinhar quando um limite é atingido. Um pedido rejeitado traz três sinais:
Retry-After- quantos segundos esperar antes de tentar de novo (atualmente1).X-RateLimit-Scope- qual bucket foi atingido:workspaceouapi_key.- O corpo JSON -
{"error":"workspace rate limit exceeded"}, que nomeia o limite.
Não existem headers com os tokens restantes, por isso controle o ritmo no cliente: mantenha a concorrência igual ou abaixo da taxa sustentada do seu plano e, ao primeiro 429, pare e aguarde Retry-After em vez de correr contra uma parede de rejeições. Um ritmo antecipado mantém o débito suave; retentar às cegas após cada rejeição desperdiça viagens e, se todos os clientes retentam no mesmo instante, fabrica uma debandada.
Quando precisar de criar muitos links, não itere o endpoint de criação individual. POST /v1/workspaces/{workspace_id}/links/bulk aceita até 100 links num pedido. Uma chamada bulk move cem links numa só viagem, enquanto cem chamadas individuais gastariam cem tokens e a maior parte do burst. É assim que a importação do Google Sheets move os links de uma campanha sem acionar o limitador.
Um 429 Too Many Requests - o status que o RFC 6585 reserva precisamente para isto - volta com um header Retry-After que indica quantos segundos esperar. Respeite-o. Esse número é o limitador a dizer quando um token estará disponível, o que é melhor informação do que qualquer palpite do seu backoff.
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 recuperando do mesmo problema não se precipite em uníssono. Até agora só o SDK de Go está publicado; os SDKs de TypeScript, Python e Ruby ainda não estão nos seus registros de pacotes, por isso, nessas linguagens, este pequeno wrapper sobre HTTP simples é todo o cliente de que precisa.
Há uma regra que liga as retentativas à seçã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 confiá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 res = await withRetry(() =>
fetch(`https://api.elido.app/v1/workspaces/${WORKSPACE_ID}/links`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ELIDO_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": `order-${order.id}-link`,
},
body: JSON.stringify({ destination_url: order.landingUrl }),
}),
);
const link = await res.json();
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 equipes 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á implementando 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. Ajuste o ritmo do cliente à taxa sustentada do seu plano 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 fetch simples, o wrapper acima cobre os primeiros dois e fornece apenas a chave:
export async function shortenForOrder(order: Order) {
const res = await withRetry(() =>
fetch(`https://api.elido.app/v1/workspaces/${WORKSPACE_ID}/links`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ELIDO_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": `order-${order.id}-link`,
},
body: JSON.stringify({
destination_url: order.landingUrl,
tags: [`order:${order.id}`],
}),
}),
);
if (res.status === 429) {
// withRetry gave up and we are still limited. Defer the job.
throw new RetryableJobError(Number(res.headers.get("retry-after")) || 1);
}
if (!res.ok) throw new Error(`create failed: ${res.status}`); // non-retryable
return res.json();
}
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
- API de encurtador de URL: um quickstart de 30 minutos em cinco linguagens
- Links curtos como Terraform: gerenciar links como código
- Webhooks versus polling para rastreamento de cliques
- Importar campanhas em massa do Google Sheets
- Ligar a API ao Claude e ao Cursor via MCP
- API gratuita de encurtamento de URL: a estrutura dos pedidos, autenticação e limites
Perguntas frequentes
Quais são os limites de taxa numa API de encurtador de URL?
No Elido, os limites são por workspace e dependem do plano, com um token bucket: 10 pedidos por segundo no Free (burst 20), 30 no Pro (burst 60) e 100 no Business (burst 200). Por cima existe um bucket separado por chave de API. Quando um limite é atingido, a API devolve 429 com os headers Retry-After e X-RateLimit-Scope, que indica qual bucket foi atingido, para o cliente saber quanto esperar. As criações em massa aceitam até 100 links por pedido, por isso o endpoint bulk é a ferramenta certa para trabalhos grandes.
Quais os erros de API que devo retentar?
Retente apenas falhas transitórias: 429 (limite de taxa atingido), 500, 502, 503 e 504. Use backoff exponencial com jitter e um limite máximo de retentativas. Nunca retente 400, 401, 403, 404 ou 409 - são falhas de validação, problemas de autenticação ou conflitos, e o mesmo pedido vai falhar novamente. Combine as retentativas com uma chave de idempotência para que uma criação reiterada não produza um link duplicado.
O que é uma chave de idempotência e quando preciso de uma?
Uma chave de idempotência é uma string opaca que envia num pedido de mutação para que o servidor possa reconhecer uma retentativa e devolver o resultado original em vez de agir duas vezes. Precisa de uma sempre que uma criação ou atualização pode ser retentada - o que é sempre, porque as redes falham e as filas de trabalhos entregam pelo menos uma vez. Derive a chave de um identificador de negócio estável, como um ID de encomenda, para que a mesma operação lógica use sempre a mesma chave.
Como evito criar links curtos duplicados?
Envie uma chave de idempotência derivada de um identificador de negócio estável em cada criação. Se a chamada for retentada, o servidor devolve o link originalmente criado em vez de gerar um segundo. Para trabalhos que podem ser retentados dias depois - além da janela de cache de idempotência - guarde também o ID do link devolvido pelo primeiro sucesso e consulte-o antes de reeditar a criação.
Quanto tempo dura uma chave de idempotência?
No Elido, a cache de idempotência tem uma duração de 24 horas, com âmbito no workspace mais a chave. Uma retentativa dentro dessa janela devolve a resposta armazenada; uma retentativa após esse período é tratada como um pedido novo e criará um novo recurso. Para trabalhos em lote de vários dias, persista o ID do link resultante no primeiro sucesso em vez de depender apenas da chave.
Experimente Elido
Cole uma URL, obtenha um link curto
Sem cadastro. O link vive 30 dias. Cadastre-se para mantê-lo para sempre.
Sem registro. Crie uma conta grátis e obtenha 50 links por mês.