Install
Kymo has two install steps, and they capture different audiences. The script tag is cookieless and needs no consent banner.
Step 1: the script tag, for human visitors
This is the browser beacon. It records pageviews, referrers, outbound clicks and goals from real people. It does not record crawlers.
- Copy the tag
Paste this into your site's HTML, with
data-siteset to your domain.<script defer src="https://kymo.in/beat.js" data-site="yoursite.com"></script>data-siteis the value Kymo files events under. It must match the domain registered in your dashboard exactly —yoursite.com, nothttps://yoursite.comand notwww.yoursite.com. - Put it in the <head>
Place the tag inside
<head>, or immediately before the closing</body>. Either works.defermeans it never blocks rendering, so the<head>is fine.Add it to every page you want tracked. If your site is a template or single layout file, one edit covers the whole site.
- Optionally declare key-page goals
To count views of specific paths as conversions, list them in
data-goals, comma-separated:<script defer src="https://kymo.in/beat.js" data-site="yoursite.com" data-goals="/signup,/onboarding/complete"></script>WhatsApp,
tel:,mailto:, and form submits are tracked as goals automatically — you do not need to list those. - Optionally switch first-touch attribution off
First-touch attribution is on by default. On the first visit,
beat.jswrites onelocalStoragekey,kymo_ft, on your own domain. It holds the UTM parameters, the referrer and the landing path of that first visit, plus a timestamp — no cookie, no identifier, nothing that identifies a person. It expires after 13 months. Every later beacon carries it, so a purchase months later can be credited to the campaign that first brought the visitor in.To switch it off:
<script defer src="https://kymo.in/beat.js" data-site="yoursite.com" data-attribution="off"></script>Off means
beat.jsreads no storage and writes none. Any other value is on. See First-touch attribution. - Deploy
Ship the change. The next real pageview sends a beacon. There is nothing to build and no package to install.
Step 2: the server-side snippet, for AI crawlers
Kymo detects AI crawlers server-side, from the HTTP request itself. Your server sends the request's user agent to Kymo's /api/crawl endpoint, which matches it against the crawler registry and records the visit. The crawler never runs a script, so JavaScript execution is not required. GPTBot, ClaudeBot, PerplexityBot, OAI-SearchBot, Applebot and the rest are all detected this way.
This requires the server-side snippet, not the JavaScript tag. The beat.js script tag runs in a browser and measures human visitors. A crawler requests your page, reads the HTML and leaves without executing anything, so a JavaScript tag cannot see it. Install the server-side snippet for Next.js, Express, Astro, SvelteKit, Cloudflare Workers or plain fetch to record crawlers. Most sites install both.
The shape is the same on every backend. Match the request's user agent against a cheap regex, and forward the ones that look like a bot:
// Swap in your framework's real request/response objects for the four below.
const BOT = /bot|crawl|spider|slurp|GPTBot|ChatGPT|Claude|Perplexity|Bytespider/i;
if (BOT.test(userAgent)) {
fetch('https://kymo.in/api/crawl', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
site: 'yoursite.com', path: requestPath, ua: userAgent, status: responseStatus
})
}).catch(() => {});
}The call is fire-and-forget. It never blocks your response, and /api/crawl never returns an error to you: a user agent it does not recognise is discarded silently. Classification happens on Kymo's side, so a new crawler is recognised without any change to this snippet.
See The bot taxonomy for which crawlers are detected and how each is classified.
What happens on first load
When the page loads, beat.js runs once and sends a single pageview beacon to https://kymo.in/api/beat. The request is a text/plain POST via navigator.sendBeacon, so it never blocks the page and never triggers a CORS preflight.
The beacon carries the path, the referrer hostname, any UTM parameters, and the viewport width. It carries no cookie and no identifier you set. Kymo derives a visitor hash server-side from data it already has — see Cookieless identity.
After the first pageview, the script also listens for outbound-link clicks, history.pushState navigations (for single-page apps), and form submits.
Excluding your own visits
To keep your own browsing out of your stats, visit your site once with ?kymo=ignore appended to any URL:
This sets a kymo_ignore flag in your browser's localStorage and stops that browser from sending events. Undo it with ?kymo=track. This is per-browser and affects only you. To exclude an office IP for everyone, use the per-site IP exclusion in Settings instead.
Next
Confirm it works: Verify events are arriving.