Integration Guide
Detailed setup instructions for different frameworks and platforms.
First-site tutorial
Steps
- Open your site in the dashboard, then choose Setup & tracking code from the site menu.
- Press Copy to take the tracking code. Copying it does not install anything, and Klar does not count it as done.
- Paste the code into the head of every page of your site, then publish the change.
- Open your own site in another tab so that it sends a visit to Klar.
- Back in the setup sheet, press Check for a first pageview. The check only reads, and it runs only when you press the button.
- Observed means Klar stored a visit from your site. Not observed means nothing has arrived yet: imported history, older visits and visits to other sites do not count, so open your site and check again. Unavailable means the check could not run, which does not mean your installation is wrong.
Static HTML
The simplest setup — just add the script tag before </body>:
<!DOCTYPE html>
<html>
<head>
<title>My Site</title>
</head>
<body>
<h1>Hello World</h1>
<script
src="https://klar.alphabros.eu/klar.v2.js"
data-site-id="your-site-id"
data-key="your-api-key"
defer
></script>
</body>
</html>React / Next.js
Next.js (App Router)
Add to your root layout.tsx:
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
{children}
<script
src="https://klar.alphabros.eu/klar.v2.js"
data-site-id="your-site-id"
data-key="your-api-key"
defer
/>
</body>
</html>
)
} React (Vite / CRA)
Add to index.html or use a component:
import { useEffect } from 'react';
function Analytics() {
useEffect(() => {
const script = document.createElement('script');
script.src = 'https://klar.alphabros.eu/klar.v2.js';
script.setAttribute('data-site-id', 'your-site-id');
script.setAttribute('data-key', 'your-api-key');
script.defer = true;
document.body.appendChild(script);
}, []);
return null;
}
export default Analytics;Vue / Nuxt
Nuxt 3
Use the useHead composable:
// app.vue or layouts/default.vue
<script setup>
useHead({
script: [
{
src: 'https://klar.alphabros.eu/klar.v2.js',
'data-site-id': 'your-site-id',
'data-key': 'your-api-key',
defer: true
}
]
})
</script> Vue 3
Add to index.html or create a plugin:
// plugins/klar.ts
export default {
install: (app: any) => {
const script = document.createElement('script');
script.src = 'https://klar.alphabros.eu/klar.v2.js';
script.setAttribute('data-site-id', 'your-site-id');
script.setAttribute('data-key', 'your-api-key');
script.defer = true;
document.body.appendChild(script);
}
};Svelte / SvelteKit
SvelteKit
Add to src/app.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
<script
src="https://klar.alphabros.eu/klar.v2.js"
data-site-id="your-site-id"
data-key="your-api-key"
defer
></script>
</body>
</html>Angular
Add the script tag to src/index.html, before </body>. The
Angular router uses the History API, so route changes are tracked automatically — no
service or router subscription needed:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<base href="/" />
</head>
<body>
<app-root></app-root>
<script
src="https://klar.alphabros.eu/klar.v2.js"
data-site-id="your-site-id"
data-key="your-api-key"
defer
></script>
</body>
</html> Custom events work from any component or service: window.klar?.track('signup'). If you use hash-based routing (withHashLocation()), call window.klar?.page(path) yourself on NavigationEnd: Klar detects navigations by pathname, which hash routes never
change.
WordPress
Add to your theme's footer.php before <?php wp_footer(); ?>:
<script
src="https://klar.alphabros.eu/klar.v2.js"
data-site-id="your-site-id"
data-key="your-api-key"
defer
></script>
<?php wp_footer(); ?> Or use a plugin like "Insert Headers and Footers" to add the script.
Ghost
Go to Settings → Code injection and paste in the Site Footer field:
<script
src="https://klar.alphabros.eu/klar.v2.js"
data-site-id="your-site-id"
data-key="your-api-key"
defer
></script>Webflow
In your project settings, go to Custom Code → Footer Code and add:
<script
src="https://klar.alphabros.eu/klar.v2.js"
data-site-id="your-site-id"
data-key="your-api-key"
defer
></script>Verifying Installation
After installing, verify Klar is working:
- Visit your website
- Open browser DevTools (F12)
- Check the Network tab for a request to
klar.v2.js - Look for a POST request to
/api/collect - Check your Klar dashboard for the pageview
Excluding your own visits and test traffic
Klar stores nothing on a visitor's device, so there is no cookie to opt out with. The exclusion is a per-site setting on the server: a beacon that matches it is not recorded and does not count toward your monthly pageviews. Visitors who do not match are counted as usual.
- IP addresses (
excludeIps): an IPv4 or IPv6 address, or an IPv4 range from/16to/32. Up to 20 entries. - Test and staging hosts (
excludeHosts): a hostname such asstaging.example.comorlocalhost. A beacon sent from that host or any of its subdomains is dropped. The site's own domain is refused. Up to 20 entries.
Site owners and admins set both with PUT /api/sites/<site id>/exclusions while signed in:
{ "excludeIps": ["203.0.113.7", "198.51.100.0/24"], "excludeHosts": ["staging.example.com"] } Send { "addMyIp": true } to add the address you are connecting from, and { "excludeIps": [] } to clear a list. A field you leave out is unchanged. GET on the same path returns the current rules and yourIp. An
excluded beacon still gets a 202 answer, so a page cannot tell it was excluded.
Note that a beacon is matched on the address it arrives from: someone on the same office network as an excluded address is excluded too, and your own visits from another network are counted until you add that address.
Native and Offline Apps
An iOS, Android, desktop or offline-first app can send activity to the same site and read
it in the same dashboard. The @klar/collect SDK (the sdk/js folder of the repository) wraps the collection API: it queues events while the device is offline
and sends them when the connection returns, with the time they were recorded.
import { createKlar } from '@klar/collect';
const klar = createKlar({
endpoint: 'https://klar.alphabros.eu',
siteId: 'YOUR-SITE-ID',
storage: AsyncStorage, // optional: keeps the queue across restarts
});
await klar.pageview('/settings');
await klar.track('purchase', { plan: 'pro' }, '/checkout'); Events recorded offline
Each event is stamped with the time it happened (t, epoch milliseconds) and
delivered after reconnect in the order it was recorded. Klar stores that time, so a
delayed event lands on the day it happened. Klar accepts a timestamp up to 7 days old; the
SDK drops an older event. A timestamp ahead of Klar's clock by a few minutes is treated as
clock skew and set to the moment of receipt.
No persistent device identifier
The SDK stores no persistent device identifier. It does not create, read or send a device id, advertising id, install id or session id, and it sets no cookie. The only thing it can keep on the device is the queue of events not yet delivered, and that queue is emptied as they are delivered. Visitors are counted on the server from a hash that rotates every UTC day, so a person cannot be followed from one day to the next.
Collection API contract
Without the SDK, send POST https://klar.alphabros.eu/api/collect with a JSON body: s (site id), type (pageview or event), p (screen path), e and d (event name and data), t (when it happened) and data-key (the site's API key, if it has
one). A 201 means recorded; 400 an unknown site or an invalid or too-old
timestamp; 402 the plan limit; 403 a wrong key. Do not send a User-Agent
that names a bot or a scripting client: such a request is acknowledged and not counted.
AI Crawlers and Agents
A visit whose User-Agent names a known AI crawler or AI agent is reported apart from your human visitors, in the AI agents view of the Sources card: each agent, its purpose, its visits and the pages AI agents fetch most. These visits never count as visitors, pageviews, bounces or goal conversions, and they do not use your quota.
The match reads the User-Agent only: Klar stores the agent name, its purpose, the page and
the time of the visit, and nothing else. No cookie, no visitor or session identifier, no
IP address and not even the User-Agent string is kept for an AI agent visit. The list
below is versioned with the code (version 2026-10-27); each
agent is matched when its token appears, case-insensitively, anywhere in the User-Agent.
| Agent | Operator | Purpose | Matched on |
|---|---|---|---|
| ChatGPT-User | OpenAI | On-demand agent | chatgpt-user |
| OAI-SearchBot | OpenAI | Search index | oai-searchbot |
| GPTBot | OpenAI | Training | gptbot |
| Claude-User | Anthropic | On-demand agent | claude-user |
| Claude-SearchBot | Anthropic | Search index | claude-searchbot |
| ClaudeBot | Anthropic | Training | claudebot, claude-web, anthropic-ai |
| Perplexity-User | Perplexity | On-demand agent | perplexity-user |
| PerplexityBot | Perplexity | Search index | perplexitybot |
| Google-Extended | Training | google-extended | |
| Meta-ExternalAgent | Meta | Training | meta-externalagent |
| Meta-ExternalFetcher | Meta | On-demand agent | meta-externalfetcher |
| Amazonbot | Amazon | Search index | amazonbot |
| Applebot-Extended | Apple | Training | applebot-extended |
| Bytespider | ByteDance | Training | bytespider |
| CCBot | Common Crawl | Training | ccbot |
| cohere-ai | Cohere | Training | cohere-ai |
| Diffbot | Diffbot | Training | diffbot |
| DuckAssistBot | DuckDuckGo | Search index | duckassistbot |
| ImagesiftBot | Hive | Training | imagesiftbot |
| MistralAI-User | Mistral AI | On-demand agent | mistralai-user |
| Omgili | Webz.io | Training | omgilibot, omgili |
| Timpibot | Timpi | Search index | timpibot |
| YouBot | You.com | Search index | youbot |
Training crawlers fetch pages to build a model's training data, search index crawlers index pages for an AI search product, and an on-demand agent fetches a page because a person asked an assistant about it. An AI product that sends an ordinary browser User-Agent is not recognised and is counted as a visit.
Automated Traffic and Bots
Klar drops a visit before it is counted when the browser announces itself as a known automated client, or when the visit's referrer is a known referral-spam domain. Dropped visits never count as visitors, pageviews, bounces or goal conversions, do not appear in any report and do not count toward your plan's pageview quota; the tracker still gets a normal success answer. Both lists are versioned with the code and the filter is always on.
What is filtered
- Search engine and SEO crawlers, for example Googlebot, Bingbot, DuckDuckBot, Baiduspider, YandexBot, AhrefsBot and SemrushBot
- AI crawlers and agents are not dropped: they are stored as AI agent visits and reported apart, see above
- Link-preview fetchers, for example facebookexternalhit, Twitterbot, LinkedInBot, Slackbot, Discordbot and the WhatsApp preview
- Uptime and speed monitors, for example UptimeRobot, Pingdom, StatusCake and Lighthouse
- Scripting HTTP clients, for example curl, wget, python-requests,
Go-http-client, Java and axios. Not filtered when the request is server-side collection:
it carries the site's data key and no
RefererorOriginheader, see the REST API - Any User-Agent that contains the whole word
bot,crawler,spiderorscraper
What is not filtered
- A bot that sends an ordinary browser User-Agent. The filter reads what the client announces; it does not fingerprint behavior, IP ranges or JavaScript execution, so deliberately disguised traffic is counted.
- Headless browsers such as headless Chrome, Playwright and Puppeteer. They run the tracker like a person would, and automated tests use them, so they are counted.
- Visits with an empty User-Agent. Some privacy tools remove it, so these are counted rather than risk dropping real people.
- Most crawlers never run JavaScript at all, so they never load the tracker; the filter is a second line of defense for the ones that do.
Real browsers, including mobile browsers and in-app browsers, are never filtered. The list is matched by name only, so it can lag behind brand-new bots; a bot missing from it is counted until the list is updated.
Referral spam
Referral spam is traffic sent to your site only so that its domain shows up in your
reports: "free SEO" tools, fake share buttons and traffic resellers. It comes with an
ordinary browser User-Agent, so the bot list above cannot see it. Klar drops a visit whose
referrer host is one of the domains below, or any subdomain of one (version 2026-10-28). The match reads the referrer host only: not
the path, the query string, the IP address or any behavior.
semalt.com, buttons-for-website.com, buttons-for-your-website.com, best-seo-offer.com, best-seo-solution.com, seo-platform.com, free-share-buttons.com, simple-share-buttons.com, get-free-social-traffic.com, free-social-buttons.com, free-floating-buttons.com, share-buttons.xyz, 100dollars-seo.com, o-o-6-o-o.com, site-auditor.online, seo-2-0.com, top1-seo-service.com, darodar.com, ilovevitaly.com, ilovevitaly.ru, blackhatworth.com, hulfingtonpost.com, priceg.com, econom.co, cenoval.ru, savetubevideo.com, kambasoft.com, bestwebsitesawards.com, theguardlan.com, 7makemoneyonline.com, floating-share-buttons.com, social-buttons.com, event-tracking.com, traffic2money.com, trafficmonetizer.org, rankings-analytics.com, success-seo.com, law-enforcement-bot-ff.com, law-enforcement-ee.com, bot-traffic.icu, bot-traffic.xyz, keywords-monitoring-your-success.com, boom.ltd, trafficbot.life, ranksonic.org, ranksonic.info, traffic-cash.xyz, news.grets.store, grets.store
What is not filtered: a visit with no referrer (a direct visit is always counted), a referrer whose domain is not on the list, and a spam domain that merely contains a listed name. A new spam domain is counted until the list is updated; the list is maintained in the Klar repository, so every change to it is versioned.
Troubleshooting
Script not loading
- Check the script URL is correct
- Check that your Klar instance is reachable:
https://klar.alphabros.eu/klar.v2.jsshould open in your browser. On the hosted service there is nothing to run on your side; if you run your own instance, make sure it is up and that the script URL points to it - Check for Content Security Policy (CSP) blocking the script
Pageviews not appearing
- Verify
data-site-idmatches your site ID in Klar - Verify
data-keyis your site's API key — copy the full snippet from your site's dashboard; a missing or wrong key gets a403from/api/collect - In DevTools, the
POST /api/collectrequest must go tohttps://klar.alphabros.eu, not to your own site - Testing from
localhost? Klar only accepts events from the domain (or aliases) registered for the site, so local pages get403 Invalid referer— test on the real domain - Check browser console for errors
- Ensure ad-blockers aren't blocking requests
- Check if DNT/GPC honoring is enabled (
data-honor-dnt="true") and the visitor's browser sends a signal — honoring is opt-in, off by default
SPA navigation not tracked
- Klar automatically tracks
pushStateand back/forward (popstate) when the pathname changes;replaceStateis deliberately not tracked - Hash-based routes (
#/page) are not auto-tracked: callklar.page(path)on each route change (SPA guide) - If using a custom router, manually call
klar.trackPageview()
Need Help?
Check out these resources: