Skip to Content
StreamsBrowser SDK

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.

OptionTypeDefaultDescription
apiHoststringIngest API host (required)
flushAtnumber10Flush the queue after this many events accumulate
flushIntervalnumber5000Flush the queue every N milliseconds
autoPagebooleantrueAutomatically call page() after load()
debugbooleanfalseEnable 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 in localStorage. Survives across sessions and is only regenerated on reset().
  • User ID — set via identify() and persisted in localStorage. 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:

  • flushAt events accumulate (default 10)
  • flushInterval milliseconds pass (default 5 seconds)
  • The page is hidden (pagehide / visibilitychange) — flushed via navigator.sendBeacon so 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

Last updated on