Quantos pedidos por minuto a API do LinkHarbor permite, que tamanho pode ter um pedido, como ler os cabeçalhos e como tratar corretamente um 429.
Para manter a API rápida para todos, o LinkHarbor limita o número de pedidos por minuto. Os limites não dependem do seu plano – não existe uma quota mensal de chamadas à API. Este artigo lista todos os limites e mostra como o seu código os pode tratar de forma limpa.
Como são contados os pedidos
- Cada pedido cai exatamente num bucket, por exemplo user para pedidos autenticados ou token para a troca de token
- A contagem é feita por minuto completo (UTC); o contador volta a zero em cada novo minuto
- Consoante o bucket, a API conta por espaço de trabalho, por pessoa, por ligação ou por endereço IP
- Os pedidos que já falham no token, no scope ou no esquema (401, 403 insufficient_scope, 400, 422 validation_failed da verificação do esquema) não contam; erros posteriores, como 403 feature_not_available, contam
Todos os limites
| Bucket (X-RateLimit-Bucket) | Limite | Contado por | Aplica-se a |
|---|---|---|---|
| user | 600 por minuto | Espaço de trabalho para tokens de chaves de API (todas as chaves e ferramentas de IA do espaço de trabalho em conjunto); pessoa para sessões iniciadas no painel | Todos os pedidos autenticados a /v1 que não têm bucket próprio |
| token | 30 por minuto | Endereço IP | POST /v1/token |
| public | 120 por minuto | Endereço IP | Pedidos à API sem autenticação, como GET /v1/plans e GET /v1/pages/available |
| callback | 120 por minuto | Endereço IP | POST /v1/callbacks/sale |
| internal | 120 por minuto | Endereço IP | URL de ingestão da integração «Custom (API)» (POST /v1/ingest/{id}) |
| mcp | 120 por minuto | Ligação de IA (token de acesso) | Servidor MCP em /mcp |
| oauth | 300 por minuto | Endereço IP | Início de sessão OAuth e descoberta para assistentes de IA |
| auth | 30 por minuto | Endereço IP | Início de sessão, registo, reposição da palavra-passe (painel e app) |
| refresh | 120 por minuto | Endereço IP | Prolongar uma sessão do painel |
| discover | 60 por minuto | Pessoa, endereço IP sem sessão iniciada | A secção Descobrir da app |
| subs_read | 120 por minuto | Pessoa, endereço IP sem sessão iniciada | Ler as páginas que segue (app) |
| subs_write | 20 por minuto | Pessoa, endereço IP sem sessão iniciada | Alterar as páginas que segue (app) |
Um orçamento por espaço de trabalho
Todos os tokens de chaves de API do mesmo espaço de trabalho partilham os 600 pedidos por minuto – independentemente de quantas chaves criar. As ferramentas dos assistentes de IA ligados também contam aqui, porque chamam a API internamente. Por isso, mais chaves não aumentam o limite. Se distribuir muita carga por vários programas, planeie em função do orçamento partilhado.
Os cabeçalhos
- X-RateLimit-Limit
- O limite do bucket por minuto, por exemplo 600.
- X-RateLimit-Remaining
- Quantos pedidos restam no minuto atual.
- X-RateLimit-Bucket
- O bucket em que o pedido caiu, por exemplo user.
- Retry-After
- Só com 429 rate_limited: segundos até ao início do minuto seguinte. Com api_key_locked, o tempo de espera está em details.retry_after_sec.
Quando o limite é atingido
Quando o limite é atingido, a API responde com HTTP 429 e este corpo. O texto de message está no idioma da conta (ver Formato de erro e códigos de erro), aqui em inglês; o código rate_limited mantém-se:
HTTP/1.1 429 Too Many Requests
Retry-After: 37
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 0
X-RateLimit-Bucket: user
{ "error": "rate_limited", "message": "Too many requests. Please try again in a moment.", "retry_after_sec": 37 } - Leia Retry-After ou retry_after_sec
- Espere pelo menos esses segundos, mais um pouco de aleatoriedade, para que nem todos os processos recomecem no mesmo momento
- Repita o pedido no máximo algumas vezes
- Se continuar a receber 429, distribua o trabalho por mais tempo
const BASE = 'https://dock.at-harbor.com';
let cached = null; // { token, expiresAt }
async function getToken() {
if (cached && Date.now() < cached.expiresAt - 60_000) return cached.token;
const res = await fetch(BASE + '/v1/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
key_id: process.env.LH_KEY_ID,
key_secret: process.env.LH_KEY_SECRET,
}),
});
if (!res.ok) throw new Error('Falha na troca do token: HTTP ' + res.status);
const data = await res.json();
cached = { token: data.token, expiresAt: Date.now() + data.expires_in * 1000 };
return cached.token;
}
async function lh(method, path, body, attempt = 0) {
const headers = { Authorization: 'Bearer ' + (await getToken()) };
if (body !== undefined) headers['Content-Type'] = 'application/json';
const res = await fetch(BASE + path, {
method,
headers,
body: body === undefined ? undefined : JSON.stringify(body),
});
if (res.status === 429 && attempt < 3) {
const wait = Number(res.headers.get('Retry-After') ?? 5);
await new Promise((r) => setTimeout(r, (wait + Math.random() * 2) * 1000));
return lh(method, path, body, attempt + 1);
}
if (res.status === 401 && attempt === 0) {
cached = null; // token expirado – tentar mais uma vez com um token novo
return lh(method, path, body, attempt + 1);
}
const data = await res.json().catch(() => null);
if (!res.ok) throw new Error((data && data.error) + ' (HTTP ' + res.status + ')');
return data;
}
// Exemplo: const { items } = await lh('GET', '/v1/pages'); Dicas para grandes volumes de dados
- Guarde o token em cache e renove-o só pouco antes de expirar, ao fim de 15 minutos
- Acompanhe X-RateLimit-Remaining e abrande quando o valor ficar baixo
- Envie os pedidos um após outro, em vez de usar dezenas de ligações paralelas
- Carregue as listas uma vez e filtre-as localmente, em vez de consultar cada página individualmente
- Distribua as sincronizações grandes por vários minutos
- Repita os erros 5xx com pausas crescentes, mas não os erros 4xx
Outros limites
| O quê | Limite | Resposta |
|---|---|---|
| Inícios de sessão falhados | Bloqueio após 5, 10 e 20 tentativas falhadas por endereço de e-mail e IP: 60 segundos, 5 minutos, 30 minutos | 429 account_locked |
| Submissões de formulários | 10 por hora e endereço IP, em todos os formulários | 429 form_rate_limited |
As tentativas falhadas na troca de token bloqueiam temporariamente a chave e o IP, veja Chaves de API e tokens de acesso. As notificações push só são enviadas no painel; os respetivos limites estão explicados em Seguidores e notificações push.
Os limites de conteúdo do seu plano – páginas, links, blocos, links curtos, etc. – não são rate limits, mas sim limites fixos. Estão listados em Comparação de planos.
Tamanho do pedido
Cada corpo é medido antes de a API o ler. O tamanho permitido depende do endpoint:
| Endpoints | Corpo máximo |
|---|---|
| Início de sessão, registo e as restantes chamadas sem autenticação em /v1/auth, POST /v1/token, POST /v1/oauth/exchange, os endpoints OAuth para assistentes de IA em /oauth e o respetivo consentimento, POST /v1/ingest/{id}, o checkout do bloco «Apoiar», as chamadas de subscrição da app (seguir, coleções, dispositivos push), a mudança de espaço de trabalho, POST /v1/auth/logout-all, a eliminação de uma conta, a configuração de dois fatores, a aceitação de um convite, a ativação do programa de recomendação e o passo de regresso ao ligar uma integração | 64 KB |
| Todos os outros endpoints JSON, incluindo o envio de um formulário numa página | 1 MB |
| POST /v1/webhooks/{id}/verify-signature e o servidor MCP em /mcp | 2 MB |
| Carregamentos de ficheiros (POST /v1/assets e POST /v1/pages/{id}/uploads, multipart/form-data) | 11 MB para o pedido completo, o próprio ficheiro no máximo 10 MB |
Se o corpo for maior, a API responde com 413 request_too_large; details.max_bytes e details.max_kb indicam o limite. Um ficheiro demasiado grande num carregamento leva a 413 payload_too_large. Os endpoints sem corpo, como as chamadas DELETE, não aceitam corpo ou aceitam apenas {}.
HTTP/1.1 413 Payload Too Large
{ "error": "request_too_large", "message": "…", "details": { "max_bytes": 65536, "max_kb": 64 }, "request_id": "…" } Também há limites dentro do corpo: cada texto tem um comprimento máximo, cada lista um número máximo de entradas, e os campos desconhecidos são recusados. Os valores estão indicados por endpoint na referência da API. Uma violação leva a 422 validation_failed, e details indica o campo, a regra e o limite. O caminho e a query string de um pedido podem ter, em conjunto, no máximo 16 KB.
Também lhe pode ser útil
Não encontrou o que procura?
Diga-nos de que precisa e a nossa equipa de suporte ajuda-o pessoalmente.