Every platform uses the same tag. Setup → Install on your dashboard shows it with your real values filled in.
<script defer data-site="site_yourapp" src="https://YOUR-DATAPENNY-URL/js/script.js"></script>
Platform guides
Plain HTML
Put the tag in the <head> of each page.
Next.js
With the App Router it goes in app/layout.tsx, and with the Pages Router in pages/_document.tsx:
import Script from 'next/script';
<Script
src="https://YOUR-DATAPENNY-URL/js/script.js"
data-site="site_yourapp"
strategy="afterInteractive"
/>
Vite, SvelteKit, Astro, Remix, Nuxt
It belongs in whichever file renders your <head>: index.html for Vite, src/app.html for SvelteKit, the base layout for Astro, app/root.tsx for Remix, and app.head in nuxt.config for Nuxt.
WordPress
The free WPCode plugin does the job. Open Code Snippets → Header & Footer, paste the tag into the Header box, then clear your page cache.
Shopify
Go to Online Store → Themes → Edit code, open layout/theme.liquid, and paste the tag just before the closing </head>.
Webflow
Add it under Site settings → Custom code → Head code. Webflow runs it on the live site only, so save and then publish.
Framer
Under Site Settings → General → Custom Code, paste it into End of <head> tag, then publish.
Wix, Squarespace, Ghost, Carrd, Bubble
Each of these has one place for code that runs across the whole site:
- Ghost: Code injection → Site header
- Squarespace: Developer tools → Code Injection → Header
- Wix: Settings → Custom code, set to Head on all pages
- Bubble: SEO / metatags → header scripts
- Carrd: an Embed element set to Head
Attributes
| Attribute | What it does |
|---|---|
data-site |
Required. Your site id. data-website-id works too. |
data-domain |
Cookie root domain, so subdomains share one visitor. Worked out for you if left out. |
data-api |
Sends events to another address, for a first-party proxy. |
data-privacy |
cookie, cookieless or consent. Leave it out and the site’s setting decides, with no code change: the script learns it from the answer to its first event, and stores nothing until then. |
data-dev |
Tracks on localhost, which is ignored by default. data-allow-localhost does the same. |
JavaScript API
Need to call it before the script has finished loading? Put this small queue above the tag:
<script>window.datapenny = window.datapenny || function () { (window.datapenny.q = window.datapenny.q || []).push(arguments); };</script>
datapenny('goal', 'signup', { plan: 'pro' });
datapenny('signup', { plan: 'pro' }); // the same, shorter
datapenny('identify', { user_id: 'user_123', email: 'mira@example.com', name: 'Mira' });
datapenny('consent', true); // on cookieless-until-consent sites
datapenny.visitorId(); // pass this to your checkout
datapenny.sessionId();
What it tracks without being asked
- Pageviews, including
pushState,replaceStateand back-button navigation in single-page apps. - Reading time and scroll depth, sent when a visitor leaves the page.
- Clicks on links to other sites.
- Clicks on checkout links for Dodo Payments, Stripe, Lemon Squeezy, Polar and Creem, which also get tagged with the visitor id. Links opened with
window.opencount too. - The return from a checkout, from the id your provider adds to the thank-you page URL.
Leaving yourself out of the numbers
Under Setup → Your own visits:
- Stop counting this browser opens
yoursite.com/?dp_ignore=1once and remembers it there.?dp_ignore=0counts it again. - List IP addresses or ranges, such as
10.0.0.0/8, for home, the office or the team. Add my current IP saves you looking yours up. - Don’t count browsers that use this dashboard leaves out anyone signed in here, on that browser and connection. It’s off by default, so the test visit you make after installing shows up.
Moving from DataFast
window.datafast() calls, data-fast-goal, data-fast-goal-* and data-fast-scroll attributes, the datafast_visitor_id cookie and checkout metadata all keep working, and so does data-website-id on the tag. On most sites you only swap the script’s src.