Documentation
Offline & Retry
Queueing, retries, and unload delivery behavior
Queueing model
track()pushes events to an in-memory queue immediately- The SDK schedules a flush every ~1 second when queue is non-empty
analytics.flush()forces an immediate send attempt
analytics.track('checkout_started')
analytics.track('checkout_completed', { order_id: 'ord_123' })
analytics.flush()Delivery limits
Sizes are UTF-8 bytes of the exact serialized request body, not string length. A character such as € counts as three bytes.
| Limit | Value |
|---|---|
| Events per request or beacon | 100 |
| Foreground request body | 512 KiB (524,288 bytes) |
| Foreground requests in flight | 2; later batches wait for a free slot |
| Each unload beacon, including the envelope | 32 KiB (32,768 bytes) |
| All beacons from one unload flush | 48 KiB (49,152 bytes) |
- Foreground delivery uses an ordinary
fetch, notkeepalive. Browsers share a 64 KiB quota across keepalive requests and beacons, and refuse larger bodies. flush()resolves after every batch it started, including batches still waiting for a request slot, has finished.- An event whose own request would exceed 512 KiB can never be delivered. Ingest also rejects properties larger than 50,000 bytes. The SDK drops the event and calls
onErrorwith a message naming the limit. It is not retried or stored offline.
Retry behavior
Failed batches retry with exponential backoff + jitter using defaults:maxRetries: 3, baseDelay: 1000,maxDelay: 30000.
| Status/Error | Behavior |
|---|---|
| 429 | Retries after Retry-After, or with backoff when it is absent, capped at maxDelay |
| 5xx | Retries with backoff |
| Network errors | Retries with backoff |
| Other 4xx | No retry |
Offline persistence
- Pending events are persisted under the project-scoped
ra:<public-key>:offline_queuekey - A companion
ra:<public-key>:offline_ad_user_datamarker prevents unsafe restoration after consent changes - Stored queue is restored when the client initializes
- Restored events are batched again by the byte limits above
- Storage is capped at 1000 events
Unload behavior
When the page is hidden or unloads, the SDK sends queued events withnavigator.sendBeacon, including the autocaptured$pageleave. Each beacon and the unload total stay within the limits above. The SDK persists events for a later flush or page load when:
- They do not fit in the unload budget
- A single event is too large for a beacon (ordinary
fetchsends it later) - Beacon is unavailable, or
sendBeaconreturnsfalse - They belong to a request still in flight, which unload may cancel
A replayed event keeps its original eventId, so ingest deduplicates it. The SDK does not use synchronous XHR.
Lifecycle helpers
const pending = analytics.getQueueSize()
analytics.shutdown() // persists pending events + removes listenersCallbacks
const analytics = createClient({
publicKey: 'pk_live_xxx',
onError: (error, events) => {
console.error(error.message, events.length)
},
onSuccess: (_events, receipt) => {
console.log('newly accepted', receipt.accepted)
console.log(
'idempotent duplicates',
receipt.duplicatesInBatch + receipt.duplicatesPreviouslyAccepted
)
},
})