Skip to content
Start here

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.

  1. Copy the tag

    Paste this into your site's HTML, with data-site set to your domain.

    <script defer src="https://kymo.in/beat.js" data-site="yoursite.com"></script>

    data-site is the value Kymo files events under. It must match the domain registered in your dashboard exactly — yoursite.com, not https://yoursite.com and not www.yoursite.com.

  2. Put it in the <head>

    Place the tag inside <head>, or immediately before the closing </body>. Either works. defer means 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.

  3. 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.

  4. Optionally switch first-touch attribution off

    First-touch attribution is on by default. On the first visit, beat.js writes one localStorage key, 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.js reads no storage and writes none. Any other value is on. See First-touch attribution.

  5. 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:

[object Object]

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.