Installation
Add your site in the dashboard and copy the snippet with your ID. Here are the steps for each framework.
HTML
Paste the snippet inside the <head> of every page.
<script
defer
data-site="st_YOUR_ID"
src="https://in.datatrack.dev/js/script.js">
</script>Next.js
File: app/layout.tsx. In the App Router root layout, using next/script. It loads once and detects client-side navigations.
import Script from "next/script";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<Script
data-site="st_YOUR_ID"
src="https://in.datatrack.dev/js/script.js"
strategy="afterInteractive"
/>
</body>
</html>
);
}Astro
File: src/layouts/Layout.astro. In your base layout, inside the <head>. is:inline stops Astro from processing and bundling it. If you use View Transitions, the script detects page changes.
<head>
<script
is:inline
defer
data-site="st_YOUR_ID"
src="https://in.datatrack.dev/js/script.js">
</script>
</head>WordPress
File: functions.php (child theme). Add it to your child theme's functions.php, or paste the HTML snippet into the header with a plugin like WPCode.
add_action('wp_head', function () {
echo '<script defer data-site="st_YOUR_ID" src="https://in.datatrack.dev/js/script.js"></script>';
});Shopify
File: layout/theme.liquid. In Online Store → Themes → Edit code, inside the <head> of theme.liquid.
<script
defer
data-site="st_YOUR_ID"
src="https://in.datatrack.dev/js/script.js">
</script>Nuxt
File: nuxt.config.ts. Add it as a global app script.
export default defineNuxtConfig({
app: {
head: {
script: [{ src: "https://in.datatrack.dev/js/script.js", defer: true, "data-site": "st_YOUR_ID" }],
},
},
});Your own subdomain (recommended)
Ad blockers stop requests to analytics domains, so 10–30% of visits never arrive. In your website's settings, add a subdomain such as m.yoursite.com and create the CNAME record it shows you. Once it's active, load the script from it:
<script defer data-site="st_YOUR_ID" src="https://m.yoursite.com/js/script.js"></script>It works with any DNS provider. Only the address changes: the data collected and the cookieless mode stay the same. Pick a neutral name (m., e., data.); names like analytics. or stats. are on blocklists.
AI and search bots
ChatGPT, Claude, Perplexity or Google read your pages without running JavaScript, so the script never sees them. Your server can report them in the background. Only known bots are recorded, and visitors aren't affected.
Cloudflare Pages: functions/_middleware.ts
const BOTS = /bot|crawl|spider|chatgpt|claude|perplexity|anthropic|bytespider|meta-external/i;
export const onRequest: PagesFunction = async ({ request, next, waitUntil }) => {
const ua = request.headers.get("user-agent") ?? "";
const page = !/\.[a-z0-9]{2,5}$/i.test(new URL(request.url).pathname); // pages, not files
if (request.method === "GET" && page && BOTS.test(ua)) {
waitUntil(fetch("https://in.datatrack.dev/crawler", {
method: "POST",
body: JSON.stringify({ s: "st_YOUR_ID", u: request.url, ua }),
}));
}
return next();
};Next.js: middleware.ts
import { NextResponse, type NextFetchEvent, type NextRequest } from "next/server";
const BOTS = /bot|crawl|spider|chatgpt|claude|perplexity|anthropic|bytespider|meta-external/i;
export function middleware(req: NextRequest, event: NextFetchEvent) {
const ua = req.headers.get("user-agent") ?? "";
if (BOTS.test(ua)) {
event.waitUntil(fetch("https://in.datatrack.dev/crawler", {
method: "POST",
body: JSON.stringify({ s: "st_YOUR_ID", u: req.url, ua }),
}));
}
return NextResponse.next();
}
export const config = { matcher: ["/((?!_next|.*\\..*).*)"] };Custom events
window.datatrack?.track("signup", { plan: "pro" });Or mark any element and the click is sent as that event, no code needed:
<a href="/pricing" data-dt-event="pricing_cta" data-dt-value="29">See plans</a>Server events
What happens in your backend (a payment, a confirmed sign-up, a qualified lead) can be sent with an API key from Settings → API. It shows under Events and counts as a goal of type "Server event", like anything the script sends.
curl -X POST https://in.datatrack.dev/v1/events \
-H "Authorization: Bearer dt_live_…" \
-H "Content-Type: application/json" \
-d '{"site":"st_…","name":"payment_completed","value":49,"visitor":"<dt_vid or your user id>","props":{"plan":"pro"}}'One event or an array of up to 100 per request; the whole request is accepted (202) or rejected with the reason (400). site andname are required. visitor ties the event to a person: the id the script keeps in dt_vid (full mode) or your own user id; without it each event counts as one visitor. url must be on the website's domain. Limits: 600 events a minute per key, 64 KB per request.
Scroll, downloads, forms
Always on: how far each visit scrolls (sent with the engagement time) and clicks on downloads (download, with the file name),mailto: (email_click) and tel: (phone_click) links. Opt in with attributes on the script:
data-forms·form_startwhen a form gets its first focus,form_submit,form_abandonwhen the page is left with a started form unsent (with the last field touched) andform_erroron browser validation errors. Only the form's id, name or action and the field's name: never what was typed.data-rage·rage_clickwhen the same element is clicked three times in a second, with a short selector.data-clicks· every click on a link or button asclick:button#cta, up to 30 per page. No text is sent.
<script defer data-site="st_…" data-forms data-rage data-clicks src="https://in.datatrack.dev/js/script.js"></script>Also always on, from your markup:
data-dt-view="pricing"on a section ·view:pricingwith the seconds it was on screen, sent when the visit leaves the page. Which parts of a landing get seen, and for how long.data-dt-404anywhere in your not-found template ·not_foundwith the page the broken link came from.- Failed requests to your own site (
HTTP 500 /api/book) and Content-Security-Policy blocks show up under Errors, next to JavaScript errors. datatrack.consent(true|false)also records aconsentevent, so you can see how many accept the banner.
All of them are events: add a goal of type "Event" with the name to count them, or use them in funnels.
Email campaigns
Measure the emails you send yourself (newsletters, transactional mail) next to the visits they bring. Pick a campaign name (letters, digits, dots, dashes) and add two things to the email. Nothing identifies the recipient.
An open pixel, at the end of the HTML:
<img src="https://in.datatrack.dev/px/st_…/newsletter-nov.gif" width="1" height="1" alt="">And each link through the redirect, with the destination URL-encoded in u. Only links to your own domain are allowed; the visit arrives with utm_campaign set, so it shows under the Email channel:
https://in.datatrack.dev/r/st_…/newsletter-nov?u=https%3A%2F%2Fyoursite.com%2FoffersIf the website has its own subdomain set up, use it instead of in.datatrack.dev. Apple Mail and Gmail preload images, so opens run high: clicks and visitors are the reliable numbers.