step() call, and each pageview when trackPageviews is on, pushes an event onto an in-memory queue. The queue drains in batches, so a busy page makes a handful of requests instead of one per event. Every request goes through fetch; navigator.sendBeacon is never used.
Automatic flush triggers
1
Timer interval
A timer fires every
flushInterval milliseconds (default: 5000 ms) and sends one batch. The timer only runs in the browser.2
Batch size threshold
If the queue reaches
maxBatchSize events before the timer fires, the SDK flushes immediately. The default is 50 events per batch; the hard maximum per request is 100 events. Requests must also fit within the payload limits.3
Page hide
On
visibilitychange to hidden and on pagehide, the SDK drains the entire queue with parallel fetch requests using keepalive: true, so the browser completes them even as the page unloads.destroy() also sends one last batch after it removes the timer and listeners.
Payload limits
The ingest endpoint accepts request bodies up to 51,200 bytes (50 KiB). Each event’sproperties object can contain at most 20 top-level keys. Properties and traits must be JSON-serializable and fit within the request body limit.
The SDK batches by event count and does not split oversized request bodies. A request over the byte limit receives 413; an event with too many properties causes the request to receive 400. The SDK drops both kinds of rejected batch. Reduce maxBatchSize or the data sent with each event to stay within these limits.
The internal queue
The queue holds at most 1000 events. When a newstep() call overflows it, the oldest events are dropped to make room. When a failed batch is re-queued at the front and overflows it, the newest events are dropped instead.
With a flush every few seconds the queue stays far below this limit. If your users go offline for long stretches, lower flushInterval and maxBatchSize so the backlog drains in smaller, more frequent batches once they reconnect.
Retry behavior
There is no backoff and no retry cap; a failing batch is retried on every flush trigger until it succeeds, is dropped by a
4xx, or is pushed out of the queue.
Manual flush methods
flush()
Sends at most one batch (up to maxBatchSize events) per call using fetch. Returns a Promise that resolves once the request settles; it never rejects, and failed batches are re-queued.
sendBeacon()
Drains the entire queue in parallel fetch requests with keepalive: true, one per batch of up to maxBatchSize events (capped at 100). The name is historical: it never calls navigator.sendBeacon, because keepalive fetch can carry the API key header and still outlive the page. Returns a Promise that resolves when all requests settle; failures are re-queued. If fetch is unavailable, events are retained.
visibilitychange and pagehide are hooked for you. Call sendBeacon() yourself only if a custom unload flow bypasses those events.