Tapemetric

SDKs

Web (JavaScript / TypeScript)

The Tapemetric web SDK works in any modern browser, in Node 18+, and inside webviews on iOS and Android. ~6 KB gzipped, zero dependencies.

Install

npm / yarn / pnpm

bash
npm install @tapemetric/analytics
yarn add @tapemetric/analytics
pnpm add @tapemetric/analytics

CDN — <script> tag

For static HTML pages or sites without a bundler, drop in the global build from jsDelivr:

html
<style="color:#f472b6">script src="https://cdn.jsdelivr.net/npm/@tapemetric/analytics@1.0.0/dist/tapemetric.min.js"></style="color:#f472b6">script>
<style="color:#f472b6">script>
  const tm = Tapemetric.init({ apiKey: 'tm_live_yourkey' });
</style="color:#f472b6">script>

unpkg works too: https://unpkg.com/@tapemetric/analytics@1.0.0/dist/tapemetric.min.js

Pin the version (@1.0.0) in production. Both jsDelivr and unpkg accept floating versions like @1 or @latest, but those introduce a coupling between us pushing a release and your site’s behavior changing without warning.

Initialize

typescript
import Tapemetric from '@tapemetric/analytics';

const tm = Tapemetric.init({
  apiKey: process.env.NEXT_PUBLIC_TAPEMETRIC_KEY!,
  // optional config:
  apiUrl: 'https://ingest.tapemetric.com',  // override for self-hosted
  flushIntervalMs: 10_000,                  // how often to send batches
  maxBatchSize: 50,                         // max events per request
  debug: false,                             // log every event to console
  disableAutoFlush: false,                  // turn off the unload handlers
});

Tapemetric.init is idempotent — calling it twice returns the same instance.

Identify users

typescript
// After login
tm.identify('user_123', {
  plan: 'svod',           // 'svod' | 'avod' | 'tvod' | 'free'
  country: 'IN',
  // any custom traits
});

// On logout
tm.reset();

Track playback

typescript
// On play
tm.trackPlayStart(
  {
    contentId: 'aashiqana_s04e12',
    contentType: 'series',     // 'series' | 'movie' | 'short' | 'live' | 'trailer'
    contentTitle: 'Aashiqana',
    season: 4,
    episode: 12,
  },
  { positionSec: 0 }
);

// On pause / resume
tm.trackPause({ positionSec: 124.5 });
tm.trackResume({ positionSec: 124.5 });

// On bitrate change
tm.trackBitrateChange(2400, { positionSec: 200, bitrateKbps: 2400 });

// On seek
tm.trackSeek(200, 350, { positionSec: 350 });

// On buffer event ended (after the user resumes)
tm.trackBuffer(1200, { positionSec: 400 }); // 1.2s buffer

// On end / completion
tm.trackComplete({ positionSec: 1320, durationSec: 1320 });

// On error
tm.trackError('NETWORK_ERROR', 'Manifest fetch failed', /* fatal */ true);

Track revenue

typescript
// After a Razorpay or Stripe charge succeeds
tm.trackPurchase({
  amountInr: 199,
  plan: 'svod',
  orderId: 'order_xyz',
  contentId: 'pvr_premiere_2026', // optional — for TVOD rentals
});

HLS.js integration

typescript
import Hls from 'hls.js';
import Tapemetric from '@tapemetric/analytics';

const tm = Tapemetric.init({ apiKey: '...' });
const video = document.querySelector<HTMLVideoElement>('#video')!;
const hls = new Hls();

let bufferStartedAt: number | null = null;

hls.on(Hls.Events.BUFFER_APPENDED, () => {
  if (bufferStartedAt) {
    tm.trackBuffer(performance.now() - bufferStartedAt, {
      positionSec: video.currentTime,
    });
    bufferStartedAt = null;
  }
});

video.addEventListener('waiting', () => { bufferStartedAt = performance.now(); });

video.addEventListener('play', () => tm.trackPlayStart(
  { contentId: 'aashiqana_s04e12', contentType: 'series' },
  { positionSec: video.currentTime }
));
video.addEventListener('pause', () => tm.trackPause({ positionSec: video.currentTime }));
video.addEventListener('ended', () => tm.trackComplete({ positionSec: video.duration }));

Reliability

The SDK does the right thing automatically:

  • Batching — events queue up and ship every 10 seconds (configurable) or when the batch hits 50 events
  • Unload reliability — when the page hides or unloads, the SDK uses navigator.sendBeacon to flush remaining events even after the page closes
  • Failure retry — failed sends are re-queued (up to 1000 events) so a transient network glitch doesn’t lose data
  • Anonymous tracking — anonymous_id is generated client-side and stored in localStorage; it’s the only thing stored

Privacy

The SDK never touches IPs, cookies, advertising IDs, or any identifier beyond what you pass it. See the full Privacy & DPDP guide for what gets collected and what doesn’t.

TypeScript

Types ship with the package — no @types/... install needed.

Bundle size

~6 KB gzipped, zero runtime dependencies. Tree-shakeable — if you only call trackPageView, the rest is dead code.