Browser SDK
The Zeotap browser SDK is a lightweight JavaScript library (~2.7 KB gzipped) that captures events from your website and sends them to the Zeotap ingest API. Its API mirrors analytics.js so you can drop it in alongside or replace an existing tracker with minimal changes.
Install
Script Tag
Add this snippet to your website’s <head> to start sending events:
<script>
!function(){var s=window.zeotap=window.zeotap||[];
if(!s.initialized){s.methods=["load","track","identify",
"page","screen","group","alias","setConsent","reset"];
s.factory=function(m){return function(){
var a=Array.prototype.slice.call(arguments);a.unshift(m);
s.push(a);return s}};for(var i=0;i<s.methods.length;i++){
var m=s.methods[i];s[m]=s.factory(m)}
var e=document.createElement("script");e.type="text/javascript";
e.async=!0;e.src="https://content.zeotap.com/composable-sdk/v1/zeotap.min.js";
var n=document.getElementsByTagName("script")[0];
n.parentNode.insertBefore(e,n);
s.load("YOUR_WRITE_KEY",{apiHost:"https://events.zeotap.com"});
}();
</script>
Replace YOUR_WRITE_KEY with the key from Events > Event Sources in your workspace. The snippet registers a method queue so any calls made before the main bundle finishes loading are replayed once it is ready. A page view is automatically recorded on initialization (autoPage defaults to true).
You can also copy a pre-filled snippet from Events > Event Sources in the UI.
npm
npm install @zeotap/browser-sdk
Then initialize in your app:
import { AnalyticsClient } from "@zeotap/browser-sdk";
const analytics = new AnalyticsClient("zeotap");
analytics.load("YOUR_WRITE_KEY", {
apiHost: "https://events.zeotap.com",
});
// Track events
analytics.track("Button Clicked", { label: "signup" });
// Identify users
analytics.identify("user-123", { email: "user@example.com" });
// Set consent
analytics.setConsent({ analytics: true, marketing: false });
API
load(writeKey, options?)
Initialize the SDK. Must be called before any tracking methods. Subsequent calls are no-ops.
| Option | Type | Default | Description |
|---|---|---|---|
apiHost | string | — | Ingest API host (required) |
flushAt | number | 10 | Flush the queue after this many events accumulate |
flushInterval | number | 5000 | Flush the queue every N milliseconds |
autoPage | boolean | true | Automatically call page() after load() |
debug | boolean | false | Enable verbose console logging |
track(event, properties?)
Record a named user action.
zeotap.track("Order Completed", {
orderId: "order-456",
revenue: 99.99,
currency: "USD"
});identify(userId?, traits?)
Associate the current user with traits. Persists userId in localStorage.
zeotap.identify("user-123", {
email: "user@example.com",
plan: "pro"
});page(category?, name?, properties?)
Record a page view. Called automatically on load() unless autoPage: false.
zeotap.page("Docs", "Getting Started");screen(category?, name?, properties?)
Record a screen view (mobile-hybrid apps).
zeotap.screen("Onboarding", "Welcome");group(groupId, traits?)
Associate the current user with a group (organization, team, workspace).
zeotap.group("org-789", {
name: "Acme Inc",
plan: "enterprise"
});alias(newId, previousId?)
Alias a new user ID to a previous anonymous or identified ID. Useful at sign-up time.
zeotap.alias("user-123");setConsent(consent)
Set the user’s consent state. All subsequent events carry context.consent with the current state. Pass exactly one of the three forms below — they are mutually exclusive.
TCF v2 consent string (EU/GDPR) — the server automatically derives consent categories from the TCF purpose consent bits:
zeotap.setConsent({ tcfString: "CPXxRfAPXxRfAAfKAB..." });US Privacy string (CCPA) — the server derives advertising/marketing opt-out status:
zeotap.setConsent({ usPrivacy: "1YNN" });Pre-parsed categories — use when your CMP already provides resolved boolean values:
zeotap.setConsent({
analytics: true,
marketing: false,
advertising: false,
functional: true,
});Each call replaces the previous consent state entirely. Forwarding rules use these values to gate delivery based on required consent categories, and every event lands in the warehouse with structured consent columns (consent_string, us_privacy, consent_categories).
reset()
Clear the current user identity (user ID, anonymous ID, queue, consent state). Call this on logout so subsequent events are attributed to a new anonymous session.
zeotap.reset();flush()
Immediately send any queued events. The SDK flushes automatically when flushAt events accumulate or flushInterval elapses, so you usually don’t need to call this — it’s useful before intentionally terminating a session (e.g., right before window.location changes).
How it Works
Identity
- Anonymous ID — generated on first
load()(UUID v4) and persisted inlocalStorage. Survives across sessions and is only regenerated onreset(). - User ID — set via
identify()and persisted inlocalStorage. Every subsequent event carries both IDs when available.
Queue and flush
Events are buffered in an in-memory FIFO queue. The queue is bounded (500 events by default) — when full, the oldest event is dropped to prevent unbounded memory growth on clients that go offline for long periods. The queue is flushed when one of:
flushAtevents accumulate (default 10)flushIntervalmilliseconds pass (default 5 seconds)- The page is hidden (
pagehide/visibilitychange) — flushed vianavigator.sendBeaconso events are delivered even as the user navigates away flush()is called explicitly
Page visibility handling
The SDK uses pagehide and visibilitychange === "hidden" (not beforeunload) so page navigation is reliably captured on mobile Safari and does not block the browser’s Back-Forward Cache. This is the modern, BFCache-friendly approach.
Transport and retry
Each flush sends a POST to the configured apiHost at /v1/batch with an X-Write-Key header. The sendBeacon fallback for page-hide flushes uses a ?writeKey= query parameter instead, because sendBeacon cannot set custom headers. Failed requests are retried up to 3 times with exponential backoff. After exhaustion, the events are dropped and a debug log is emitted if debug: true.
Payload Format
Events follow the Segment spec:
{
"type": "track",
"messageId": "uuid-v4",
"userId": "user-123",
"anonymousId": "uuid-v4",
"event": "Order Completed",
"properties": { "orderId": "456", "revenue": 99.99 },
"context": {
"library": { "name": "@zeotap/browser-sdk", "version": "0.1.0" },
"page": {
"url": "https://example.com/checkout",
"title": "Checkout",
"referrer": "https://example.com/cart",
"path": "/checkout",
"search": ""
},
"userAgent": "Mozilla/5.0 ...",
"locale": "en-US",
"consent": { "tcfString": "CPXx..." }
},
"timestamp": "2026-04-10T12:34:56.789Z",
"sentAt": "2026-04-10T12:34:56.789Z"
}
See Sending events for the full Segment-compatible schema.
Next Steps
- SDK Playground — Try the SDK interactively in your browser
- Event sources — Create and manage write keys
- Event forwarding — Route SDK events to destinations and warehouses
- Consent management — Configure consent categories
- Debugging — Verify events from the SDK are arriving