Wie viele Anfragen pro Minute die LinkHarbor-API erlaubt, wie groß eine Anfrage sein darf, wie du die Header liest und richtig auf 429 reagierst.
Damit die API für alle schnell bleibt, begrenzt LinkHarbor die Zahl der Anfragen pro Minute. Die Limits hängen nicht vom Plan ab – ein Kontingent an API-Aufrufen pro Monat gibt es nicht. Dieser Artikel listet alle Grenzen und zeigt, wie dein Code sauber damit umgeht.
So wird gezählt
- Jede Anfrage fällt in genau einen Bereich (Bucket), etwa user für angemeldete Anfragen oder token für den Token-Tausch
- Gezählt wird pro voller Minute (UTC); mit jeder neuen Minute beginnt der Zähler bei null
- Je nach Bereich zählt die API pro Workspace, pro Person, pro Verbindung oder pro IP-Adresse
- Anfragen, die schon an Token, Scope oder Schema scheitern (401, 403 insufficient_scope, 400, 422 validation_failed aus der Schema-Prüfung), zählen nicht mit; spätere Fehler wie 403 feature_not_available zählen mit
Alle Limits
| Bereich (X-RateLimit-Bucket) | Limit | Gezählt pro | Gilt für |
|---|---|---|---|
| user | 600 pro Minute | Workspace bei Tokens aus API-Keys (alle Keys und KI-Werkzeuge des Workspaces zusammen); Person bei Dashboard-Anmeldung | Alle angemeldeten Anfragen an /v1, für die kein eigener Bereich gilt |
| token | 30 pro Minute | IP-Adresse | POST /v1/token |
| public | 120 pro Minute | IP-Adresse | Anfragen ohne Anmeldung an die API, etwa GET /v1/plans und GET /v1/pages/available |
| callback | 120 pro Minute | IP-Adresse | POST /v1/callbacks/sale |
| internal | 120 pro Minute | IP-Adresse | Ingest-URL der Integration „Custom (API)“ (POST /v1/ingest/{id}) |
| mcp | 120 pro Minute | KI-Verbindung (Access-Token) | MCP-Server unter /mcp |
| oauth | 300 pro Minute | IP-Adresse | OAuth-Anmeldung und Discovery für KI-Assistenten |
| auth | 30 pro Minute | IP-Adresse | Login, Registrierung, Passwort zurücksetzen (Dashboard und App) |
| refresh | 120 pro Minute | IP-Adresse | Verlängern einer Dashboard-Sitzung |
| discover | 60 pro Minute | Person, ohne Anmeldung IP-Adresse | Entdecken-Bereich der App |
| subs_read | 120 pro Minute | Person, ohne Anmeldung IP-Adresse | Abonnements lesen (App) |
| subs_write | 20 pro Minute | Person, ohne Anmeldung IP-Adresse | Abonnements ändern (App) |
Ein Budget pro Workspace
Alle Tokens aus API-Keys desselben Workspaces teilen sich die 600 Anfragen pro Minute – egal, wie viele Keys du anlegst. Auch die Werkzeuge verbundener KI-Assistenten zählen hier mit, weil sie intern die API aufrufen. Mehrere Keys erhöhen das Limit also nicht. Verteilt sich viel Last auf mehrere Programme, plane das gemeinsame Budget ein.
Die Header
- X-RateLimit-Limit
- Das Limit des Bereichs pro Minute, zum Beispiel 600.
- X-RateLimit-Remaining
- Wie viele Anfragen in der laufenden Minute noch übrig sind.
- X-RateLimit-Bucket
- Der Bereich, in den die Anfrage gefallen ist, zum Beispiel user.
- Retry-After
- Nur bei 429 rate_limited: Sekunden bis zum Beginn der nächsten Minute. Bei api_key_locked steht die Wartezeit in details.retry_after_sec.
Wenn das Limit erreicht ist
Ist das Limit erreicht, antwortet die API mit HTTP 429 und diesem Body. Der Text in message kommt in der Kontosprache (siehe API-Fehlerformat), hier auf Englisch. Verlass dich auf den Code rate_limited:
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 } - Lies Retry-After oder retry_after_sec
- Warte mindestens so viele Sekunden, plus ein wenig Zufall, damit nicht alle Prozesse gleichzeitig starten
- Wiederhole die Anfrage höchstens ein paar Mal
- Bleibt es bei 429, verteile die Arbeit über mehr Zeit
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-Tausch fehlgeschlagen: 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 abgelaufen – einmal mit neuem Token versuchen
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;
}
// Beispiel: const { items } = await lh('GET', '/v1/pages'); Tipps für große Datenmengen
- Token zwischenspeichern und erst kurz vor Ablauf nach 15 Minuten erneuern
- X-RateLimit-Remaining beobachten und bei niedrigen Werten langsamer werden
- Anfragen nacheinander statt mit Dutzenden parallelen Verbindungen schicken
- Listen einmal laden und lokal filtern, statt jede Page einzeln abzufragen
- Große Synchronisationen über mehrere Minuten verteilen
- Bei 5xx-Fehlern mit wachsenden Pausen wiederholen, bei 4xx-Fehlern nicht
Weitere Grenzen
| Was | Grenze | Antwort |
|---|---|---|
| Fehlgeschlagene Logins | Sperre nach 5, 10 und 20 Fehlversuchen pro E-Mail und IP: 60 Sekunden, 5 Minuten, 30 Minuten | 429 account_locked |
| Formular-Einsendungen | 10 pro Stunde und IP-Adresse, über alle Formulare | 429 form_rate_limited |
Fehlversuche beim Token-Tausch sperren Key und IP vorübergehend, siehe API-Schlüssel und Token. Push-Nachrichten sendest du nur im Dashboard; ihre Limits erklärt Follower und Push-Benachrichtigungen.
Die Inhalts-Limits deines Plans – Pages, Links, Blöcke, Short Links und Co. – sind keine Rate Limits, sondern feste Obergrenzen. Sie stehen unter Pläne im Vergleich.
Größe von Anfragen
Jeder Body wird gemessen, bevor die API ihn liest. Wie groß er sein darf, hängt vom Endpunkt ab:
| Endpunkte | Größter Body |
|---|---|
| Anmeldung, Registrierung und die übrigen Aufrufe ohne Anmeldung unter /v1/auth, POST /v1/token, POST /v1/oauth/exchange, die OAuth-Endpunkte für KI-Assistenten unter /oauth und die Zustimmung dazu, POST /v1/ingest/{id}, der Spenden-Checkout, die Abonnement-Aufrufe der App (Folgen, Sammlungen, Push-Geräte), Workspace-Wechsel, POST /v1/auth/logout-all, Konto löschen, die Zwei-Faktor-Einrichtung, Einladung annehmen, Empfehlungsprogramm aktivieren und die Rückkehr beim Verbinden einer Integration | 64 KB |
| Alle anderen JSON-Endpunkte, auch das Absenden eines Formulars auf einer Page | 1 MB |
| POST /v1/webhooks/{id}/verify-signature und der MCP-Server unter /mcp | 2 MB |
| Datei-Uploads (POST /v1/assets und POST /v1/pages/{id}/uploads, multipart/form-data) | 11 MB für die ganze Anfrage, die Datei selbst höchstens 10 MB |
Ist der Body größer, antwortet die API mit 413 request_too_large; details.max_bytes und details.max_kb nennen die Grenze. Eine zu große Datei bei einem Upload führt zu 413 payload_too_large. Endpunkte ohne Body, etwa die DELETE-Aufrufe, nehmen keinen Body oder nur {} an.
HTTP/1.1 413 Payload Too Large
{ "error": "request_too_large", "message": "…", "details": { "max_bytes": 65536, "max_kb": 64 }, "request_id": "…" } Auch innerhalb des Bodys gibt es Grenzen: Jeder Text hat eine Höchstlänge, jede Liste eine Höchstzahl an Einträgen, und unbekannte Felder werden abgelehnt. Die Werte stehen pro Endpunkt in der API-Referenz. Ein Verstoß führt zu 422 validation_failed, details nennt Feld, Regel und Grenze. Pfad und Query-String einer Anfrage dürfen zusammen höchstens 16 KB lang sein.
Das könnte auch helfen
Nichts gefunden?
Schreib uns dein Anliegen – unser Support hilft dir persönlich weiter.