Installation guides
Verify your installation
Check that ProductLense records your visits, and fix the common reasons a pageview does not arrive.
After adding the script, confirm three things: the script loads, the browser sends events, and ProductLense accepts them. This page walks through each check and lists every reason an event can be refused or silently skipped.
The three checks
- Open your site in a normal browser window (not a private window with strict blocking, and not on
localhost). - In the browser's developer tools, open the Network tab and reload. Look for
a.jsfromapi.productlense.appwith status200, then aPOSTtohttps://api.productlense.app/api/e. The script batches events for up to one second, so the request appears shortly after load or when you leave the page. - Open the Live page in the ProductLense dashboard. Your visit appears within a few seconds.
A 204 response means ProductLense accepted the batch. Anything else carries a JSON error with a message:
{ "ok": false, "error": { "code": "invalid_input", "message": "hostname not allowed for this site" } }If the request is refused
| Status and message | Cause | Fix |
|---|---|---|
400 hostname not allowed for this site | The page's hostname is not one of the site's hostnames. | Add it in Settings → Sites. A bare example.com also admits www.example.com; *.example.com admits every subdomain and the apex. |
400 origin not allowed for this site | The request's Origin (or Referer) is a hostname the site does not list. | Same fix: add the hostname the page is served from. |
400 s must be a site uuid | data-site is mistyped or still SITE_ID. (Without any data-site, the script sends nothing.) | Copy the id again from Settings → Sites. |
404 unknown site | The site id does not exist, or the site was deleted. | Use the id of an existing site. |
413 body exceeds 8 KB | A batch with very large props. | Keep props small: at most 2 KB per event. |
429 quota_exceeded | The account used its monthly events plus the 10% grace band. | Upgrade, or wait for the next month; Retry-After gives the seconds until it starts. See plans and limits. |
503 rate_limited | The ingest queue was briefly full. | Nothing to do; the next batch goes through. |
If nothing is sent at all
The script skips sending in these cases, by design:
- The page runs on
localhost. Adddata-dev="true"to the tag to test locally, and remove it before deploying. navigator.webdriveris true: Playwright, Puppeteer, Selenium and similar automation.window.__pl_disableis set before the script runs. Some teams set it for their own staff.- The path matches a
data-excludeglob. See exclude pages. - The tag has
type="module", or a bundler or optimisation plugin rewrote it. The script reads its settings from its own classic<script>tag, so it must stay exactly as given. - An ad or privacy blocker in your own browser blocks
api.productlense.app. Try another browser profile. To reduce blocking for visitors, see proxy the script through your own domain.
If the request succeeds but the visit is missing
- Bots are dropped silently with a
204, so that they never cost you quota. A user agent containingbot,crawl,spider,preview,headless,lighthouse,curl/and similar markers is treated as a bot, and so is an empty user agent. Test from a normal browser. See bots and filtering. - The Live page shows the last 30 minutes. For an older visit, open the Overview with Today selected and no filters.
Content Security Policy
If your site sends a Content-Security-Policy header, the browser console shows a violation when ProductLense is blocked. Allow both the script and the endpoint it posts to:
script-src 'self' https://api.productlense.app;
connect-src 'self' https://api.productlense.app;If you use the inline queue stub, allow it too, for example by adding its sha256- hash to script-src.
Caching and optimisation plugins
Page caches can serve an old version of your pages without the script; purge the cache after installing. Plugins that combine, minify, defer or delay JavaScript (common on WordPress) can rewrite the tag. Exclude api.productlense.app/a.js from them.
Still stuck
Send the page URL and the status and message from the POST /api/e response through the contact page.