# Script tag

Add ProductLense to any website with one script tag in the head of every page, then identify users after signup.

ProductLense is one script of about 2.3 KB gzipped. Paste it into the head of every page, replace `SITE_ID` with the id from Settings → Sites in the ProductLense dashboard, and pageviews start arriving within seconds. The script sets no cookies and loads with `defer`, so it never blocks rendering.

You rarely need to pick a guide yourself. When you add a site, the dashboard reads your homepage once, detects what it is built with (Next.js, Nuxt, SvelteKit, Astro, WordPress, Shopify, Webflow, Framer, Laravel, Django, Rails and more), and shows the code with your site id filled in, the exact file it goes in, and a ready prompt for Claude Code, Codex, Cursor or Lovable. It also tells you when the script is already on the page.

## Add the script

```html title="index.html" {4}
<head>
  <meta charset="utf-8" />
  <script>window.productlense=window.productlense||function(){(window.productlense.q=window.productlense.q||[]).push(arguments)}</script>
  <script defer data-site="SITE_ID" src="https://api.productlense.app/a.js"></script>
</head>
```

The first line is the queue stub. It lets you call `window.productlense(...)` before the deferred script has loaded; calls are kept and replayed in order once it runs. If you never call `window.productlense` from inline code, the stub is optional.

> [!IMPORTANT]
> Load the script as a classic script, exactly as above. Do not add `type="module"` or bundle it into your own JavaScript: the script reads its settings from its own tag through `document.currentScript`, which is not available to module scripts.

## Script attributes

These are every setting the script reads. All are optional except `data-site`.

| Attribute | Default | Meaning |
|---|---|---|
| `data-site` | required | The site id from Settings → Sites. |
| `data-api` | script origin | Base URL that receives events at `<base>/api/e`. Only needed when you [proxy the script](/docs/proxy/overview). |
| `data-spa` | `true` | Set `"false"` to stop recording history route changes. See [pageviews and single-page apps](/docs/track/pageviews-and-spas). |
| `data-outbound` | `true` | Set `"false"` to stop recording outbound links and file downloads. See [outbound links and downloads](/docs/track/outbound-links-and-downloads). |
| `data-exclude` | none | Comma-separated path globs that are never tracked, such as `/admin/*,/preview`. See [exclude pages](/docs/track/exclude-pages). |
| `data-dev` | `false` | Set `"true"` to send events from `localhost`. |

## Send events and identify users

After signup or login, link the visitor to your own user id. This is what lets ProductLense attribute later payments to the channel that first brought the person.

```html
<script>
  window.productlense('identify', 'user_123', { plan: 'starter' });
  window.productlense('event', 'Signup', { plan: 'starter' });
</script>
```

The user id is any non-empty string of up to 128 bytes without control characters. Use the same id you store on the customer in your billing provider; see [how payments are attributed](/docs/revenue/attribution). Event names allow letters, digits, spaces, underscores and hyphens, up to 64 characters; see [custom events](/docs/track/custom-events).

## Content Security Policy

If your site sends a Content-Security-Policy header, allow the script and the endpoint it posts to:

```text
script-src 'self' https://api.productlense.app;
connect-src 'self' https://api.productlense.app;
```

If you keep the queue stub inline, also allow it, for example with its `sha256-` hash in `script-src`.

## Verify your installation

1. Deploy, then open your site in a normal browser window.
2. Open the Live page in the ProductLense dashboard. Your visit appears within a few seconds.
3. In the browser's developer tools, the Network tab shows a `POST` to `https://api.productlense.app/api/e` with status `204`.

Nothing is sent from `localhost` unless the tag has `data-dev="true"`, and ad blockers in your own browser may block the request. If the visit does not appear, follow [Verify your installation](/docs/install/verify).
