Combien de requêtes par minute l’API LinkHarbor autorise, quelle taille peut avoir une requête, comment lire les en-têtes et bien gérer un 429.
Pour que l’API reste rapide pour tout le monde, LinkHarbor limite le nombre de requêtes par minute. Les limites ne dépendent pas de votre offre – il n’y a pas de quota mensuel d’appels API. Cet article liste toutes les limites et montre comment votre code peut les gérer proprement.
Comment les requêtes sont comptées
- Chaque requête tombe dans exactement un compartiment, par exemple user pour les requêtes authentifiées ou token pour l’échange de jeton
- Le comptage se fait par minute pleine (UTC) ; le compteur repart de zéro à chaque nouvelle minute
- Selon le compartiment, l’API compte par espace de travail, par personne, par connexion ou par adresse IP
- Les requêtes qui échouent déjà sur le jeton, la portée ou le schéma (401, 403 insufficient_scope, 400, 422 validation_failed issu du contrôle de schéma) ne comptent pas ; les erreurs ultérieures comme 403 feature_not_available comptent
Toutes les limites
| Compartiment (X-RateLimit-Bucket) | Limite | Compté par | S’applique à |
|---|---|---|---|
| user | 600 par minute | Espace de travail pour les jetons de clés API (toutes les clés et outils IA de l’espace de travail ensemble) ; personne pour les connexions au tableau de bord | Toutes les requêtes authentifiées vers /v1 qui n’ont pas leur propre compartiment |
| token | 30 par minute | Adresse IP | POST /v1/token |
| public | 120 par minute | Adresse IP | Requêtes à l’API sans authentification, comme GET /v1/plans et GET /v1/pages/available |
| callback | 120 par minute | Adresse IP | POST /v1/callbacks/sale |
| internal | 120 par minute | Adresse IP | URL d’ingestion de l’intégration « Custom (API) » (POST /v1/ingest/{id}) |
| mcp | 120 par minute | Connexion IA (jeton d’accès) | Serveur MCP sous /mcp |
| oauth | 300 par minute | Adresse IP | Connexion OAuth et découverte pour les assistants IA |
| auth | 30 par minute | Adresse IP | Connexion, inscription, réinitialisation du mot de passe (tableau de bord et app) |
| refresh | 120 par minute | Adresse IP | Prolongation d’une session du tableau de bord |
| discover | 60 par minute | Personne, adresse IP si non connecté | La rubrique Découvrir de l’app |
| subs_read | 120 par minute | Personne, adresse IP si non connecté | Lecture des pages suivies (app) |
| subs_write | 20 par minute | Personne, adresse IP si non connecté | Modification des pages suivies (app) |
Un budget par espace de travail
Tous les jetons issus de clés API d’un même espace de travail se partagent les 600 requêtes par minute – quel que soit le nombre de clés créées. Les outils des assistants IA connectés comptent aussi, car ils appellent l’API en interne. Plus de clés n’augmentent donc pas la limite. Si une forte charge est répartie sur plusieurs programmes, tenez compte du budget partagé.
Les en-têtes
- X-RateLimit-Limit
- La limite du compartiment par minute, par exemple 600.
- X-RateLimit-Remaining
- Le nombre de requêtes restantes dans la minute en cours.
- X-RateLimit-Bucket
- Le compartiment dans lequel la requête est tombée, par exemple user.
- Retry-After
- Uniquement avec 429 rate_limited : secondes jusqu’au début de la minute suivante. Avec api_key_locked, le temps d’attente figure dans details.retry_after_sec.
Quand la limite est atteinte
Une fois la limite atteinte, l’API répond HTTP 429 avec ce corps ; le texte de message est dans la langue du compte (voir Format et codes d’erreur), ici en anglais, le code rate_limited reste le même :
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 } - Lisez Retry-After ou retry_after_sec
- Attendez au moins ce nombre de secondes, plus un peu d’aléatoire pour que tous les processus ne repartent pas au même moment
- Réessayez la requête quelques fois au maximum
- Si vous recevez sans cesse 429, étalez le travail sur plus de temps
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('Token exchange failed: 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; // jeton expiré – réessayer une fois avec un nouveau jeton
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;
}
// Exemple : const { items } = await lh('GET', '/v1/pages'); Astuces pour les gros volumes de données
- Mettez le jeton en cache et ne le renouvelez que peu avant son expiration au bout de 15 minutes
- Surveillez X-RateLimit-Remaining et ralentissez quand la valeur devient basse
- Envoyez les requêtes l’une après l’autre plutôt que sur des dizaines de connexions parallèles
- Chargez les listes une fois et filtrez-les localement au lieu d’interroger chaque page séparément
- Étalez les grosses synchronisations sur plusieurs minutes
- Réessayez les erreurs 5xx avec des pauses croissantes, mais pas les erreurs 4xx
Autres limites
| Quoi | Limite | Réponse |
|---|---|---|
| Connexions échouées | Verrouillage après 5, 10 et 20 échecs par adresse e-mail et IP : 60 secondes, 5 minutes, 30 minutes | 429 account_locked |
| Envois de formulaire | 10 par heure et par adresse IP, tous formulaires confondus | 429 form_rate_limited |
Les échecs lors de l’échange de jeton verrouillent temporairement la clé et l’IP, voir Clés API et jetons. Vous n’envoyez les notifications push que dans le tableau de bord ; leurs limites sont expliquées dans Abonnés et notifications push.
Les limites de contenu de votre offre – pages, liens, blocs, liens courts, etc. – ne sont pas des limites de débit mais des plafonds fixes. Elles figurent dans Comparatif des offres.
Taille des requêtes
Chaque corps est mesuré avant que l’API ne le lise. Sa taille maximale dépend de l’endpoint :
| Endpoints | Corps maximal |
|---|---|
| Connexion, inscription et les autres appels non authentifiés sous /v1/auth, POST /v1/token, POST /v1/oauth/exchange, les endpoints OAuth des assistants IA sous /oauth et leur consentement, POST /v1/ingest/{id}, le paiement du bloc Soutenir, les appels d’abonnement de l’app (suivi, collections, appareils push), le changement d’espace de travail, POST /v1/auth/logout-all, la suppression d’un compte, la configuration de la double authentification, l’acceptation d’une invitation, l’activation du programme de parrainage et l’étape de retour lors de la connexion d’une intégration | 64 Ko |
| Tous les autres endpoints JSON, y compris l’envoi d’un formulaire sur une page | 1 Mo |
| POST /v1/webhooks/{id}/verify-signature et le serveur MCP sous /mcp | 2 Mo |
| Téléversements de fichiers (POST /v1/assets et POST /v1/pages/{id}/uploads, multipart/form-data) | 11 Mo pour toute la requête, le fichier lui-même au maximum 10 Mo |
Si le corps est plus grand, l’API répond 413 request_too_large ; details.max_bytes et details.max_kb indiquent la limite. Un fichier trop volumineux lors d’un téléversement entraîne 413 payload_too_large. Les endpoints sans corps, comme les appels DELETE, n’acceptent aucun corps ou seulement {}.
HTTP/1.1 413 Payload Too Large
{ "error": "request_too_large", "message": "…", "details": { "max_bytes": 65536, "max_kb": 64 }, "request_id": "…" } Il existe aussi des limites à l’intérieur du corps : chaque texte a une longueur maximale, chaque liste un nombre maximal d’éléments, et les champs inconnus sont refusés. Les valeurs sont listées par endpoint dans la référence de l’API. Une violation entraîne 422 validation_failed, et details nomme le champ, la règle et la limite. Le chemin et la chaîne de requête d’une requête peuvent compter ensemble au maximum 16 Ko.
Cela pourrait aussi vous être utile
Vous n’avez rien trouvé ?
Dites-nous ce dont vous avez besoin, et notre équipe support vous aidera personnellement.