Skip to Content
Browser Tracking
SDKs

Browser Tracking

Lightweight client-side library (~2KB) for tracking impressions, clicks, and conversions in the browser.

Installation

The browser tracking library is included in the notpixel package:

npm i notpixel

Import from the /browser subpath:

import { createTracker } from 'notpixel/browser';

Create a Tracker

import { createTracker } from 'notpixel/browser'; const tracker = createTracker({ publisherId: 'pub_10565', baseURL: 'https://api.notpixel.ai', // optional timeoutMs: 5000, // optional debug: false, // optional });

Track Impressions

Track when a user sees an ad:

await tracker.impression(impressionId);

Auto-Track on Visibility

Use trackOnVisible to automatically track when an element enters the viewport:

import { trackOnVisible } from 'notpixel/browser'; const adElement = document.getElementById('ad-container'); const cleanup = trackOnVisible(adElement, impressionId, tracker, { threshold: 0.5, // 50% visible (default) once: true, // track only once (default) }); // Call cleanup() to stop observing cleanup();

Uses IntersectionObserver for efficient viewport detection. Falls back to immediate tracking if not available.

Track Clicks

Track when a user clicks an ad:

await tracker.click(impressionId, { metadata: { source: 'chat' }, // optional });

Click Handler Helper

Create a click handler with optional navigation:

import { createClickHandler } from 'notpixel/browser'; const handleClick = createClickHandler(impressionId, tracker, { href: 'https://advertiser.com/landing', newTab: true, // default metadata: { source: 'sidebar' }, }); // Use in event listener button.addEventListener('click', handleClick); // Or in React <button onClick={handleClick}>Learn More</button>

Track Conversions

Track conversion events (signups, purchases, etc.):

await tracker.conversion(impressionId, { type: 'signup', // required valueCents: 0, // optional externalId: 'order-1', // optional, for deduplication metadata: {}, // optional });

Conversion Types

Common conversion types:

TypeDescription
signupUser registration
purchaseCompleted purchase
leadLead form submission
trialTrial started
installApp install

React Integration

import { useEffect, useRef } from 'react'; import { createTracker, trackOnVisible, createClickHandler } from 'notpixel/browser'; const tracker = createTracker({ publisherId: 'pub_10565' }); function AdComponent({ ad, impressionId }) { const ref = useRef<HTMLDivElement>(null); useEffect(() => { if (!ref.current) return; return trackOnVisible(ref.current, impressionId, tracker); }, [impressionId]); const handleClick = createClickHandler(impressionId, tracker, { href: ad.cta.url, }); return ( <div ref={ref} className="ad"> <span>{ad.disclosure}</span> <h3>{ad.headline}</h3> {ad.body && <p>{ad.body}</p>} {ad.disclaimer && <p>{ad.disclaimer}</p>} <button onClick={handleClick}>{ad.cta.label}</button> </div> ); }

Confirm a production web impression

observeAdView(element, onQualified) calls back once when at least half of the element stays in the viewport for a continuous second in a visible tab. Use the returned cleanup function when removing the element. Call confirmAdView(ad.opportunityId) inside that callback on the verified website. Use the returned impressionId, ctaUrl and expiresAt to update your card and enable its CTA. The same opportunity can be retried without charging twice. AdCard and AdBanner handle this automatically.

How It Works

The library uses navigator.sendBeacon for same-origin tracking. Cross-origin tracking uses fetch with keepalive so the request can continue when the page closes:

User Action → sendBeacon (preferred) → NotPixel API ↓ (fallback) fetch with keepalive

sendBeacon ensures tracking data is sent even if the user navigates away immediately after clicking.

Check Ad Expiration

Ads expire after 60 minutes. Use isAdExpired to check before displaying:

import { isAdExpired, AD_EXPIRATION_MS } from 'notpixel/browser'; // Check if ad is expired if (isAdExpired(ad)) { // Fetch fresh ad from your backend const freshAd = await fetchAdFromServer(); } // Calculate remaining time const remainingMs = ad.expiresAt - Date.now(); const remainingMinutes = Math.floor(remainingMs / 60000);

Always check expiration before displaying an ad. Expired ads may have depleted budgets or outdated content.

Debug Mode

Enable logging to troubleshoot:

const tracker = createTracker({ publisherId: 'pub_10565', debug: true, }); // Console: [notpixel] Beacon sent: /v1/impression {...}

API Reference

tracker.feedback(impressionId, options)

Send { action: 'hide' } to record a dismissal, or { action: 'report', reason, details? } to submit a report. Report reasons are misleading, unsafe, inappropriate, and policy_violation. A report waits for an HTTP response and returns { ok: false, error } on failure. Supply a stable eventId UUID when retrying the same report.

const eventId = crypto.randomUUID(); const result = await tracker.feedback(impressionId, { action: 'report', reason: 'misleading', details: 'Incorrect claim', eventId, }); if (result.ok) dismissCurrentAd();

createTracker(options)

OptionTypeDefaultDescription
publisherIdstring—Your publisher ID
baseURLstringhttps://api.notpixel.aiAPI base URL
timeoutMsnumber5000Request timeout
debugbooleanfalseEnable logging

trackOnVisible(element, impressionId, tracker, options)

OptionTypeDefaultDescription
thresholdnumber0.5Visibility threshold (0-1)
oncebooleantrueTrack only once

Returns a cleanup function.

createClickHandler(impressionId, tracker, options)

OptionTypeDefaultDescription
hrefstring—URL to navigate to
newTabbooleantrueOpen in new tab
metadataobject—Custom metadata

Returns an event handler function.

isAdExpired(ad)

Returns true if the ad’s expiresAt timestamp has passed.

AD_EXPIRATION_MS

Constant: 3600000 (60 minutes in milliseconds).

Last updated on
Was this helpful?