Veröffentlicht von Markus Wals

Jeder Request zählt: Lastmanagement, 429 und Timeouts in der weclapp API

429 API Backoff Lastmanagement Timeouts weclapp

Titelgrafik: Jeder Request zählt. weclapp API, Das Handbuch, Teil 01: Last, 429 und Timeouts im Griff.

Kurz gesagt: Die weclapp API hat kein starres Rate Limit, sondern ein Lastmanagement: Requests über der Grenze warten bis zu 30 Sekunden in einer Queue, erst danach folgt HTTP 429. Behandle 429 mit Exponential Backoff, wiederhole automatisch nur GET, HEAD und OPTIONS und stimme deine Timeouts so ab, dass das Server-Limit knapp unter dem Client-Timeout liegt.

Dieser Artikel ist für dich, wenn du eine Integration gegen die weclapp API baust oder betreibst. Wir fassen den Vortrag von Christian Lanig (weclapp) am Community Day 2026 zusammen und ergänzen, was in den Folien fehlt: einen Client in JavaScript, der richtig auf Last reagiert, und Timeouts, die zueinander passen.

Suchst du nur die Grundlagen, erklärt unsere Seite Hat die weclapp API ein Rate Limit? Queue, 429 und Wait-Header in wenigen Minuten. Arbeitest du in Python, findest du Backoff, Wait-Header und abgestimmte Timeouts in weclappy 1.0 fertig umgesetzt.

Das Wichtigste in Kürze

  • Jeder Request zählt. Unnötige Requests kosten Effizienz, bei dir und bei allen anderen Integrationen auf demselben Mandanten.
  • Last ist Request-Zeit, nicht Request-Zahl. Sie ergibt sich aus Anzahl, Verarbeitungsdauer und Parallelität der Requests.
  • 429 ist ein Signal. Wiederhole nicht sofort, sondern mit Exponential Backoff, Jitter und Deckel.
  • Writes wiederholst du nicht blind. Bei POST, PUT und DELETE liest du erst nach, ob die Änderung angekommen ist.
  • Timeouts müssen zueinander passen. Die Doku empfiehlt mindestens eine Minute, ein Client-Timeout von 30 Sekunden ist eine Falle.

Die ERP-API ist ein Produktivsystem

„Jeder Request zählt.“ Mit diesem Satz hat Lanig seinen Vortrag eröffnet. Gemeint ist nicht, dass einzelne Requests teuer wären. Unnötige Requests kosten Effizienz, bei dir und bei allen anderen Integrationen auf demselben Mandanten.

Lanig beginnt beim Kontext: Die ERP-API ist der Hub zwischen Onlineshop, Logistik, CRM, BI und weiteren Anwendungen. Schlechte API-Nutzung trifft deshalb ganze Prozesse, nicht nur die eine Integration. Die Grenze für parallele Requests gilt laut API-Doku je Mandant. Der nächtliche BI-Abgleich und der Scan am Packtisch teilen sich also dieselbe Kapazität.

Trotzdem setzt weclapp kein starres Rate Limit, und das soll laut Doku auch so bleiben. Ein Lastmanagement schützt Stabilität, Verfügbarkeit und Fairness, normale Nutzung soll davon nichts merken. Requests über der Grenze werden deshalb nicht gleich abgewiesen, sondern warten in einer Queue, derzeit bis zu 30 Sekunden. Erst danach antwortet die API mit HTTP 429 Too Many Requests. Solange die Wartezeit unter dieser Grenze bleibt, kosten dich kurze Spitzen also Zeit, aber keine Fehler.

Grafik: Ein 429 kommt nicht sofort. Bei normaler Last antwortet die API direkt, bei hoher Last wartet der Request in der Queue und kommt später mit 200 OK zurück, erst bei anhaltender Überlast folgt nach bis zu 30 Sekunden Wartezeit HTTP 429 Too Many Requests.

Fazit: Die API wartet zuerst und weist erst nach bis zu 30 Sekunden Wartezeit mit 429 ab.

Request-Sekunden: was Last wirklich ist

Nicht jeder Request kostet gleich viel. Die Last ergibt sich laut Lanig aus der Anzahl der Requests, ihrer Verarbeitungsdauer und der Parallelität. Sein Rechenbeispiel betrachtet ein Fenster von 60 Sekunden mit Lastfaktor 3, also 180 verfügbaren Request-Sekunden:

Szenario laut Vortrag Rechnung Request-Sekunden Ergebnis im Beispiel
Geringe Last 500 Requests × 100 ms 50 unkritisch
Solide Last 200 Requests × 400 ms × 2 Threads 160 unkritisch
Starke Last 500 Requests × 100 ms × 4 Threads 200 20 Sekunden Wartezeit

Grafik: API-Last realistisch einschätzen. Rechenbeispiel aus dem Vortrag von Christian Lanig, weclapp: Bei einem Budget von 180 Request-Sekunden sind 500 × 100 ms = 50 und 200 × 400 ms × 2 Threads = 160 unkritisch, 500 × 100 ms × 4 Threads = 200 führt zu 20 Sekunden Wartezeit.

Die 180 sind ein Rechenbeispiel, kein veröffentlichter Grenzwert. Das Prinzip dahinter steht aber auch in der API-Doku: Maßstab ist die effektive Request-Zeit. 500 Requests à 100 ms sind dort 50 Request-Sekunden und unkritisch. Kritisch werden viele rechenintensive Requests, die über mehrere Minuten laufen.

Für dein eigenes Design folgt daraus eine Reihenfolge. Spare zuerst Requests ein, senke dann ihre Dauer und dreh erst zuletzt an der Parallelität. Wo die Einsparung liegt, ordnet Lanig nach Symptomen:

Symptom Ursache Lösung
Lastspitzen Vollabgleich Delta-Sync
gleichartige Aufrufe Polling Webhooks
viele Einzelrequests N+1-Abfragen referencedEntities

Dazu kommen die Anti-Patterns, die er am häufigsten sieht: Vollabfragen im Intervall, permanentes Polling, N+1-Requests und parallele Schreibzugriffe, die miteinander konkurrieren. Sein Fazit: Viele Requests sind nicht dasselbe wie viel Nutzen.

Fazit: Spare zuerst Requests ein, senke dann ihre Dauer und dreh erst zuletzt an der Parallelität.

429 ist ein Signal

Wer auf einen 429 sofort wiederholt, schickt genau die Last erneut, die ihn ausgelöst hat. Lanig setzt Exponential Backoff dagegen: erst 2 Sekunden warten, dann 4, dann länger. So bekommt das System Luft, und die API-Doku verlangt genau das.

Grafik: 429 ist ein Signal. Sofortige Retries erhöhen die Last weiter, Exponential Backoff mit 2 und 4 Sekunden Pause entlastet das System, bis die Antwort 200 kommt.

Code zeigen die Folien nicht, deshalb hier ein kompakter Client in JavaScript (Node 18 oder neuer):

const BASE = "https://<dein-mandant>.weclapp.com/webapp/api/v2/";
const SAFE = new Set(["GET", "HEAD", "OPTIONS"]);
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

function retryAfterMs(res) {
  const seconds = Number(res.headers.get("retry-after")); // weclapp sendet ihn aktuell nicht, defensiv für später
  return Number.isFinite(seconds) && seconds > 0 ? seconds * 1000 : null;
}

function backoffMs(attempt, { baseMs = 2000, capMs = 60000 } = {}) {
  const ceiling = Math.min(capMs, baseMs * 2 ** attempt); // 2 s, 4 s, 8 s ... bis 60 s
  return ceiling / 2 + Math.random() * (ceiling / 2);     // Jitter: 50 bis 100 Prozent
}

function logQueue(method, path, res, started) {
  const totalMs = Date.now() - started;
  const waitMs = Number(res.headers.get("x-weclapp-wait-ms") ?? 0);
  const entry = {
    method,
    path: path.split("?")[0], // keine Filter und IDs aus der Query ins Log
    status: res.status,
    totalMs,
    waitMs,
    ownMs: Math.max(0, totalMs - waitMs), // Bearbeitungszeit ohne Queue
    reason: res.headers.get("x-weclapp-wait-reason") ?? "-",
  };
  (waitMs >= 2000 ? console.warn : console.log)(JSON.stringify(entry));
}

async function weclapp(path, { method = "GET", body, maxRetries = 5 } = {}) {
  for (let attempt = 0; ; attempt++) {
    const started = Date.now();
    const res = await fetch(BASE + path, {
      method,
      headers: {
        AuthenticationToken: process.env.WECLAPP_TOKEN,
        Accept: "application/json",
        ...(body ? { "Content-Type": "application/json" } : {}),
        "User-Agent": "meine-integration/1.0",
        "X-Weclapp-Request-Timeout-Ms": "110000", // Server gibt vor dem Client auf
      },
      body: body ? JSON.stringify(body) : undefined,
      signal: AbortSignal.timeout(120_000), // Client-Timeout: 120 s
    });
    logQueue(method, path, res, started);

    const retry = res.status === 429 && SAFE.has(method) && attempt < maxRetries;
    if (!retry) return res; // über Writes entscheidet der aufrufende Code
    await res.body?.cancel();
    await sleep(Math.min(retryAfterMs(res) ?? backoffMs(attempt), 60_000));
  }
}

Fünf Entscheidungen stecken in diesen Zeilen:

  • Jitter: Der Zufallsanteil sorgt dafür, dass mehrere Worker nach einem 429 nicht exakt gleichzeitig wiederkommen.
  • Deckel: Jede einzelne Pause ist auf 60 Sekunden begrenzt, und nach fünf Versuchen gibt der Client den 429 an den aufrufenden Code zurück.
  • Retry-After: weclapp sendet aktuell keinen Retry-After-Header, deshalb bestimmt der Backoff die Pause. Der Client wertet den Header trotzdem defensiv aus: Käme er künftig, hätte er Vorrang.
  • Timeouts: Der Client-Timeout liegt bei 120 Sekunden, das Server-Limit per Header 10 Sekunden darunter. Warum, steht im Abschnitt zu Timeouts.
  • Nur sichere Methoden: Automatisch wiederholt werden nur GET, HEAD und OPTIONS, also Methoden, bei denen ein zweiter Versuch nichts verdoppeln kann.

Bei POST, PUT und DELETE ist das anders. Die Doku beschreibt beim 429, dass die Anfrage abgewiesen wurde, aber nicht, ob ein Write davor schon begonnen hat. Ein blinder Retry kann aus einer verlorenen Antwort eine doppelte Buchung machen. Prüf deshalb über eine stabile Geschäftskennung nach, ob die Änderung angekommen ist, und entscheide dann. Wie weclappy das in Python löst, steht im Artikel zu weclappy 1.0.

Fazit: Wiederhole nach einem 429 mit Backoff, aber nur Reads automatisch.

Last sichtbar machen

Musste ein Request warten, trägt die Antwort zwei Header, bei Erfolg genauso wie beim 429. Ohne Wartezeit fehlen sie.

Header Bedeutung
X-Weclapp-Wait-Ms wie viele Millisekunden der Request vor der Bearbeitung gewartet hat
X-Weclapp-Wait-Reason warum: concurrency (zu viele parallele Requests), load (hohe Gesamtlast) oder beides

In Lanigs Beispiel kommt ein GET /party/id/123 mit X-Weclapp-Wait-Ms: 1000 und X-Weclapp-Wait-Reason: load zurück. Die Funktion logQueue im Client oben schreibt genau diese Werte in eine Logzeile pro Response und zieht die Wartezeit von der Gesamtzeit ab. So siehst du, ob ein Request von sich aus langsam war oder in der Queue stand. Werte die Zeilen als Perzentile aus statt nur als Durchschnitt: Der Mittelwert verdeckt gerade die Requests, die an der 30-Sekunden-Grenze kratzen.

Grafik: weclapp macht Last sichtbar. Auf GET /party/id/123 antwortet die API mit X-Weclapp-Wait-Ms: 1000 und X-Weclapp-Wait-Reason: load. Antwortzeit minus Wartezeit ergibt die reine Verarbeitungszeit.

Der Grund im Header sagt dir auch, wie du reagierst. Bei concurrency reicht es, weniger Requests gleichzeitig zu schicken. Bei load hilft nur weniger Gesamtvolumen, oder du verteilst schwere Aufgaben über mehr Zeit, wie es die Doku empfiehlt. Steigt die Wartezeit, drosselst du am besten schon vor dem ersten 429.

Fazit: Die beiden Wait-Header zeigen dir, ob ein Request langsam war oder in der Queue stand, und was du dagegen tust.

Timeouts abstimmen

Zu kurze Client-Timeouts lösen laut Lanig eine Kette aus: vorzeitige Abbrüche, mehr Retries, höhere Last und eine schlechtere Erfahrung für alle, die mit dem System arbeiten. Sein Leitsatz: „Nicht der schnellste Timeout gewinnt, sondern der am besten abgestimmte.“ Die Doku empfiehlt Timeouts von mindestens einer Minute, denn ein Request kann bis zu 30 Sekunden in der Queue stehen und braucht danach noch seine Bearbeitungszeit.

Ein Client-Timeout von genau 30 Sekunden ist deshalb eine Falle. Dein Client legt auf, während der Request noch wartet oder gerade bearbeitet wird. Bei einem Read ist das ärgerlich, bei einem Write gefährlich: Der Server kann die Änderung trotzdem übernehmen, und dein zweiter Versuch landet in einem Doppelversand oder einem Optimistic-Lock-Fehler.

Grafik: Timeouts abstimmen. Ein zu kurzer Client-Timeout führt zu Abbrüchen, Retries und mehr Last. Abgestimmt heißt: Client-Timeout ab 60 Sekunden, dazu die Header X-Weclapp-Wait-Timeout-Ms und X-Weclapp-Request-Timeout-Ms.

Für den umgekehrten Fall, dass dein Client nicht so lange warten will, gibt es zwei Request-Header:

Header Bedeutung
X-Weclapp-Wait-Timeout-Ms maximale Zeit bis zum Start der Bearbeitung
X-Weclapp-Request-Timeout-Ms Best-Effort-Limit für den gesamten Request inklusive Wartezeit

Beide Header können Timeouts nur verkürzen, nie über die Serverlimits hinaus verlängern. Ungültige, negative und Nullwerte ignoriert die API. Sind beide gesetzt, begrenzt der Request-Timeout auch die Wartezeit in der Queue. Überschreitet ein Request das Limit aus X-Weclapp-Request-Timeout-Ms, antwortet die API mit HTTP 400 und dem Fehlertyp request_timeout, eine klare Antwort statt eines abgerissenen Requests.

curl --compressed \
  -H "AuthenticationToken: <dein-token>" \
  -H "Accept: application/json" \
  -H "X-Weclapp-Wait-Timeout-Ms: 5000" \
  -H "X-Weclapp-Request-Timeout-Ms: 15000" \
  "https://<dein-mandant>.weclapp.com/webapp/api/v2/party?pageSize=1&properties=id"

Daraus ergeben sich unsere Richtwerte. Sie sind eine Empfehlung von uns, keine Vorgabe von weclapp, und entsprechen den Standardwerten von weclappy. Die Regel dahinter: Das Server-Limit liegt knapp unter dem Client-Timeout, damit die request_timeout-Antwort ankommt, bevor dein Client auflegt. Ohne diesen Abstand geben beide Seiten im selben Moment auf, und du weißt nicht, was passiert ist.

Einsatz Client-Timeout Wait-Timeout-Header Request-Timeout-Header
Hintergrundjob, Sync, Export, Writes 120 Sekunden 30.000 ms 110.000 ms
Interaktive Abfrage, ein Mensch wartet 20 Sekunden 5.000 ms 15.000 ms

Die zweite Zeile weicht bewusst von der Minuten-Empfehlung der Doku ab: Wenn ein Mensch auf die Antwort wartet, ist eine klare Absage nach 15 Sekunden besser als eine Minute Stillstand. Der Server bricht dann selbst ab und meldet es, dein Client wartet nur lange genug, um diese Meldung zu bekommen. Writes behandelst du anders, auch mit request_timeout: Die Doku lässt offen, ob ein Write danach zurückgerollt oder trotzdem gespeichert wurde. Wiederhole ihn deshalb nicht automatisch, sondern lies nach.

Fazit: Das Server-Limit liegt knapp unter dem Client-Timeout, und Writes liest du nach, statt sie zu wiederholen.

Checkliste

  1. Zähle die Calls pro Vorgang und schätze ihre Request-Sekunden.
  2. Streiche überflüssige Calls, bevor du an der Parallelität drehst.
  3. Ersetze Vollabgleich durch Delta-Sync, Polling durch Webhooks und N+1-Abfragen durch referencedEntities.
  4. Behandle 429 mit Exponential Backoff, Jitter und Deckel.
  5. Wiederhole automatisch nur GET, HEAD und OPTIONS, bei Writes liest du erst nach.
  6. Logge X-Weclapp-Wait-Ms und X-Weclapp-Wait-Reason pro Response und drossle, bevor der 429 kommt.
  7. Setze den Client-Timeout auf mindestens 60 Sekunden und das Server-Limit per Header knapp darunter. Kürzer nur bei interaktiven Reads.

Lanig schließt mit drei Sätzen, die als Zusammenfassung taugen: 429 ist ein Signal. Messen statt raten. Und die beste Integration sendet nur die Requests, die sie wirklich braucht.

Fazit: Messen statt raten, und nur die Requests senden, die du wirklich brauchst.

Weiterlesen und nachbauen

Die Requests aus diesem Artikel kannst du mit der kostenlosen Postman Collection 4 weclapp direkt gegen deinen Mandanten ausprobieren. Welche Filter weclapp still ignoriert und wie du das prüfst, steht in Die stillen Fehler der weclapp API. Adaptive Parallelität, Retry-Budgets und Paginierung im Detail findest du im Artikel zu weclappy 1.0.

Willst du eine Integration auf Last und Fehlerfälle prüfen lassen, schauen wir gemeinsam drauf: wals.pro/termin.

Quellen und Grenzen

  • Vortrag von Christian Lanig (weclapp) zur weclapp API am Community Day 2026 und die API-Dokumentation von weclapp, im Artikel ohne Link (Stand der Veröffentlichung, 21.10.2026).
  • Der JavaScript-Client, die Richtwerte für Timeouts und die Empfehlung zu Writes sind unsere Ergänzungen, keine Vorgaben von weclapp.

Die 180 Request-Sekunden sind ein Rechenbeispiel aus dem Vortrag, kein veröffentlichter Grenzwert. Die Queue-Wartezeit von 30 Sekunden gilt derzeit und kann sich ändern.

Häufige Fragen

Hat die weclapp API ein Rate Limit?

Nein, kein starres. weclapp setzt ein Lastmanagement ein: Requests über der Grenze warten derzeit bis zu 30 Sekunden in einer Queue. Erst danach antwortet die API mit HTTP 429 Too Many Requests.

Wie gehe ich mit einem 429 um?

Wiederhole nicht sofort, sondern mit Exponential Backoff: erst 2 Sekunden warten, dann 4, dann länger, mit Jitter und einem Deckel von 60 Sekunden. Setzt der Server einen Retry-After-Header, hat er Vorrang.

Darf ich Schreibzugriffe nach einem 429 automatisch wiederholen?

Nein. Automatisch wiederholst du nur GET, HEAD und OPTIONS. Bei POST, PUT und DELETE prüfst du über eine stabile Geschäftskennung nach, ob die Änderung angekommen ist, bevor du entscheidest.

Welchen Client-Timeout sollte ich setzen?

Die Doku empfiehlt mindestens eine Minute. Unsere Richtwerte sind 120 Sekunden für Hintergrundjobs und Writes und 20 Sekunden für interaktive Abfragen, jeweils mit einem Server-Limit knapp darunter.

Woran erkenne ich, dass ein Request in der Queue stand?

Musste ein Request warten, trägt die Antwort die Header X-Weclapp-Wait-Ms und X-Weclapp-Wait-Reason. Der Grund ist concurrency (zu viele parallele Requests), load (hohe Gesamtlast) oder beides.