Start here
Install the script
One tag, about 4KB, loaded with defer so it never blocks rendering. It sets a visitor ID, sends a pageview on load, and exposes a small API on window.numberhill.
The script tag
Paste this into your <head>, replacing YOUR_SITE_ID with the ID from your dashboard settings.
html
<script defer data-website-id="YOUR_SITE_ID"
src="https://numberhill.com/js/numberhill.js"></script>Script attributes
| Attribute | Required | Description |
|---|---|---|
| data-website-id | required | Your site ID. Every event is stored against it. |
| data-api | optional | Override the ingest endpoint. Defaults to the origin the script was served from plus /api/collect — useful when proxying through your own domain. |
| defer | recommended | Keeps the script off the critical rendering path. |
Framework snippets
Next.js (App Router)
app/layout.tsx
export default function RootLayout({ children }) {
return (
<html lang="en">
<head>
<script defer data-website-id="YOUR_SITE_ID" src="https://numberhill.com/js/numberhill.js" />
</head>
<body>{children}</body>
</html>
);
}Google Tag Manager
Create a new tag → Custom HTML, paste the script tag, and set the trigger to All Pages. Leave “Support document.write” unchecked.
WordPress
Appearance → Theme File Editor → header.php, and paste before </head>. With a child theme or a headers plugin, use that instead so updates don't overwrite it.
Single-page apps
The script patches history.pushState and listens for popstate, so client-side route changes are counted as pageviews automatically. No router integration needed.
Verify it works
- Load your site, open DevTools → Network, and look for a
POSTto/api/collectreturning202. - In the console,
window.numberhill.visitorId()should return a 32-character hex string. - Your dashboard's Realtime view should show the visit within a few seconds.
Ad blockers
Some blockers drop third-party analytics requests. Serving the script and/api/collect from your own domain via a proxy — then pointing data-api at it — avoids most of that.