API rate limits and best practices
Overview
To keep the API reliable for everyone, Kyvento limits the request rate per account. This article explains the limits, the associated HTTP headers and the patterns that let your integration work efficiently and retry-safely.
The rate limit
API token access is subject to a quota per account and minute, tiered by your plan β how many requests your plan includes is stated on the pricing page and in your billing area. During the trial, the Starter quota applies.
In your integration, however, do not rely on a fixed number but on the three headers that every response carries:
X-RateLimit-Limitβ your plan's per-minute quotaX-RateLimit-Remainingβ requests left in the current minute windowX-RateLimit-Resetβ the point in time (Unix timestamp) at which the full quota is available again
These are the authoritative statement about your current quota: read X-RateLimit-Limit at runtime instead of hard-coding the number into your integration β that way your application stays correct when your plan changes or the limits are adjusted.
Once the quota is exhausted, Kyvento responds with HTTP 429, the error code RATE_LIMITED and a Retry-After header. Never treat 429 as a fault in your own logic; wait for the stated time and repeat the request β ideally with an additional random offset if several processes work in parallel.
Additional volume limits for heavy operations
The per-minute quota limits how often you call β not how much a single call produces. Three particularly expensive operations therefore carry a quota of their own. Important for integrations: these operations belong to the application interface and are not reachable via API token β a token call receives HTTP 403 with the error code REQUIRES_INTERACTIVE_SESSION. The following quotas therefore only apply to the signed-in browser session:
POST /exports/bulk-pdfβ 5 per hour. The most expensive operation: a ZIP over up to 500 PDF documents.POST /exportsandPOST /exports/datevβ 20 per hour, a shared quota for export generation.GET /searchβ 120 per minute, per user.
The plan limit plays no role there: it applies exclusively to API token access and counts for the entire account. Requests from the browser session instead run under a separate session quota of 3000 requests per minute per user. Search is also measured per user β so one user cannot bring their colleagues' search to a halt.
The quotas are counted separately from each other and from the plan limit, so an exhausted bulk-PDF quota blocks neither search nor the rest of your traffic. Retrieving exports that already exist β list, detail view and download, likewise session-only β is not throttled: waiting for completion therefore does not run into the generation limit. When a quota is exceeded, Kyvento responds as usual with 429 and a Retry-After header.
Safe retries: the idempotency key
If a write request breaks off (timeout, network error), you don't know whether it went through on the server side. Therefore, for critical POST requests, send the Idempotency-Key header with a self-generated, unique value (recommended: UUID). Kyvento remembers the result for 24 hours: a repeat with the same key returns the original response instead of executing the operation again. The same key with changed request content is rejected β this protects against programming errors.
Monetary amounts in the API
Every monetary amount leaves the API as a decimal string in euros β for example "19.90", not 1990 and not 19.9 as a number. The reason is precision: product and unit prices allow more than two decimal places, which neither integer cents nor floating-point numbers represent without loss. So read the values into a decimal type (such as decimal.Decimal or BigDecimal) rather than a float.
This applies consistently to the v1 API, the customer portal and the checkout. One exception when writing: a coupon's minimum order value (minimum_order_amount) is still expected as an integer in cents when creating and updating, while the response returns it as a decimal string. Do not, therefore, pass a value you have read straight back into a write payload.
Using pagination correctly
- Lists return
dataplusmetawithper_page,has_moreand follow-up page information. per_pageaccepts up to 1000 entries β for bulk reconciliations, choose large pages; this saves requests against your quota.- For large, changing data sets, cursor pagination (
?pagination=cursor) is recommended: you page onward stably viameta.next_cursor, even when records are created or disappear in parallel.
Best practices for economical integrations
- Webhooks instead of polling: Have changes reported to you via webhook instead of querying lists every minute β it's faster and consumes practically no quota (see Configuring webhooks).
- Watch your budget: Log
X-RateLimit-Remainingand throttle batch jobs before the limit kicks in. - Back off exponentially: On 429 and 5xx, retry at growing intervals β never in a tight loop.
- Evaluate errors machine-readably: Branch on the
error_codeof the response, not on message texts. - Spread out bulk reconciliations: Distribute large synchronizations over time instead of exhausting the per-minute quota in short bursts β this keeps budget free for concurrent daytime operations.
Next steps
- All endpoints and conventions β see API documentation and examples
- Set up push notifications β see Configuring webhooks