A platform, such as a link-in-bio site or a store builder, has many users, and each user has their own page. DataDad counts each page as a tenant. One script tag on all pages collects the data. Your server reads each tenant's numbers through the stats API and shows them to its owner. The numbers of two tenants never mix.
Analytics for Platforms is turned on for each account. To turn it on, send a message from the Help page.
Add your platform
- In DataDad, select Add, then Platform.
- Enter your platform's domain and choose what a user's page is called, such as Profile or Store.
- Add the script tag to your pages (see below), then select I've installed it.
- Open one user's page on your live site. The setup shows the first visit with its tenant.
- Create an API key. The setup shows a sample response for the tenant it saw.
You need no API call to create a tenant. A tenant exists from its first page view.
Name the tenant on each page
Add the script to each user's page and set data-tenant to the page's ID:
<script defer src="https://c.data.dad/s.js"
data-site="site_…"
data-tenant="p_123"
data-outbound></script>
- Use an ID that does not change, such as the database ID of the profile. Do not use the handle in the address: when a user changes it, the history would split.
- Use 1 to 64 characters: letters, digits, and
_.:-. Do not use e-mail addresses or names. - Pages that do not belong to one user, such as your home page or pricing page, use the same tag without
data-tenant. They count only in your platform's totals. - In a single-page app, set
data-tenanton the script tag when the new page renders. The script counts a route change one task after the router callspushState, and it readsdata-tenantat that moment. When a page can render later than that, for example behind a loading screen, adddata-manualand callwindow.datadad.page()after you setdata-tenant. The Next.js tabs below do this.
<script defer src="https://c.data.dad/s.js" data-site="site_…"
data-tenant="{{ $profile->id }}" data-outbound></script><script defer src="https://c.data.dad/s.js" data-site="site_…"
data-tenant="<%= @profile.id %>" data-outbound></script>"use client";
import Script from "next/script";
import { usePathname } from "next/navigation";
import { useLayoutEffect } from "react";
// data-manual: the script does not count route changes itself.
// Render once on each page: tenant={profile.id}, or no tenant.
export function DataDad({ tenant }) {
const pathname = usePathname();
useLayoutEffect(() => {
const tag = document.querySelector("script[data-site]");
if (tenant) tag?.setAttribute("data-tenant", tenant);
else tag?.removeAttribute("data-tenant");
window.datadad?.page();
}, [tenant, pathname]);
return <Script src="https://c.data.dad/s.js" data-site="site_…"
data-tenant={tenant} data-outbound data-manual />;
}import Script from "next/script";
import { useRouter } from "next/router";
import { useLayoutEffect } from "react";
// data-manual: the script does not count route changes itself.
// Render once on each page: tenant={profile.id}, or no tenant.
export function DataDad({ tenant }) {
const { asPath } = useRouter();
useLayoutEffect(() => {
const tag = document.querySelector("script[data-site]");
if (tenant) tag?.setAttribute("data-tenant", tenant);
else tag?.removeAttribute("data-tenant");
window.datadad?.page();
}, [tenant, asPath]);
return <Script src="https://c.data.dad/s.js" data-site="site_…"
data-tenant={tenant} data-outbound data-manual />;
}<script defer src="https://c.data.dad/s.js" data-site="site_…"
data-tenant="{{ profile.id }}" data-outbound></script>Count link clicks
With data-outbound on the script tag, a click on a link to another site counts as a link click. The API shows clicks per link with by=link.
To keep a link's count when its address changes, give the link a name with data-dd-link:
<a href="https://open.spotify.com/artist/1" data-dd-link="lnk_42">My music</a>
A link with data-dd-link counts even when it points to your own domain, for example to a redirect such as /go/42. The address is stored without its query string.
Show the numbers to your users
Your server calls the API with the tenant, and your page shows the result. For example, a stats page for a profile owner:
// Server route for the signed-in owner's stats page.
const params = new URLSearchParams({ tenant: profile.id, range: '30d' });
const headers = { Authorization: `Bearer ${process.env.DATADAD_API_KEY}` };
const [summary, links] = await Promise.all([
fetch(`https://data.dad/api/v1/stats/summary?${params}`, { headers }).then(r => r.json()),
fetch(`https://data.dad/api/v1/stats/breakdown?${params}&by=link&limit=20`, { headers }).then(r => r.json()),
]);
Check on your server that the signed-in user owns the profile before you send its ID to the API. The key can read every tenant of the app.
How tenants are counted
- Visitors: a person who opens two profiles on the same day counts as one visitor of each profile, and as one visitor of the platform.
- Visits: a visit ends where a tenant's pages end. A visitor who goes from Alice's profile to Bob's profile makes one visit on each. On Alice's profile, that visit is a bounce if it had one page view.
- Visit duration: visible time counts for the tenant of the page where it was spent.
See your tenants in DataDad
On the app's dashboard, select Profiles (or the name you chose). The list shows your tenants ranked by visitors. Select a tenant to see its report: the same numbers the API returns.