Introduction
Sleek Analytics is a lightweight, privacy-first analytics platform. Add one script tag and get real-time visitor insights without cookies, consent banners, or GDPR headaches.
Under 2 KB
Minified & gzipped. Won't slow down your site.
Cookie-free
No cookies, no personal data. GDPR / CCPA ready.
Real-time
See live visitor counts and active pages instantly.
Quick Start
Get up and running in under two minutes.
Sign up at getsleek.io/sign-up and add your first site. You'll get a unique site key.
Paste this snippet before the closing </body> tag (or inside <head>):
<script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>Open your dashboard at getsleek.io/dashboard. Data appears within seconds of the first pageview.
Replace YOUR_SITE_KEY with the actual key shown in your dashboard under Settings → Site Key.
CLI (Recommended)
The fastest way to add Sleek to any project. Our CLI auto-detects your framework and injects the tracking script into the right file.
Quick setup
npx sleek-analytics initThat's it. The CLI will:
- Detect your framework automatically
- Ask for your Site ID (from your Sleek dashboard)
- Inject the analytics script into the correct file
Supported frameworks
| Framework | How it injects |
|---|---|
| Next.js (App Router) | <Script> in app/layout |
| Next.js (Pages Router) | <Script> in pages/_app |
| React (Vite / CRA) | <script> in index.html |
| Vue.js | <script> in index.html |
| Nuxt.js | app.head.script in nuxt.config |
| SvelteKit | <svelte:head> in +layout.svelte |
| Remix | <script> in app/root |
| Astro | <script> in layout .astro file |
| Gatsby | setHeadComponents in gatsby-ssr.js |
| WordPress | wp_head hook in functions.php |
| HTML / Static | <script> in index.html |
Global install
If you prefer to install it globally:
npm i -g sleek-analytics
sleek-analytics initWhat gets added
A single async script tag pointing to your Sleek instance:
<script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_ID"></script>The script is ~1 KB, loads asynchronously, and doesn't slow down your site.
Requires Node.js 16 or later. The CLI only modifies a single file in your project — no config files, wrappers, or build plugins.
HTML & Vanilla JS
The simplest installation — drop the script anywhere in your HTML.
<!DOCTYPE html>
<html>
<head>
<title>My Site</title>
<!-- Sleek Analytics -->
<script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>
</head>
<body>
<!-- your content -->
</body>
</html>Using async ensures the script never blocks page rendering.
Next.js
Use the built-in Next.js Script component so the analytics script loads with the correct strategy.
App Router (Next.js 13+)
Add it to your root app/layout.tsx:
import Script from 'next/script'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<Script
src="https://getsleek.io/v1.js"
data-site="YOUR_SITE_KEY"
strategy="afterInteractive"
/>
</body>
</html>
)
}Pages Router (Next.js 12 and below)
Add it to pages/_app.tsx:
import Script from 'next/script'
import type { AppProps } from 'next/app'
export default function App({ Component, pageProps }: AppProps) {
return (
<>
<Component {...pageProps} />
<Script
src="https://getsleek.io/v1.js"
data-site="YOUR_SITE_KEY"
strategy="afterInteractive"
/>
</>
)
}The strategy="afterInteractive" loads the script after the page is interactive — ideal for analytics.
React (Vite / CRA)
Inject the script once inside your root component using a useEffect hook, or add it directly to public/index.html.
Option A — index.html (recommended)
Open public/index.html and add the script tag:
<body>
<div id="root"></div>
<script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>
</body>Option B — useEffect hook
import { useEffect } from 'react'
export default function App() {
useEffect(() => {
const s = document.createElement('script')
s.src = 'https://getsleek.io/v1.js'
s.setAttribute('data-site', 'YOUR_SITE_KEY')
s.async = true
document.head.appendChild(s)
}, [])
return <div>{/* your app */}</div>
}Vue.js
Add the script to your index.html or mount it in your root App.vue.
index.html (simplest)
<!-- public/index.html -->
<body>
<div id="app"></div>
<script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>
<script type="module" src="/src/main.ts"></script>
</body>App.vue — onMounted
<script setup>
import { onMounted } from 'vue'
onMounted(() => {
const s = document.createElement('script')
s.src = 'https://getsleek.io/v1.js'
s.setAttribute('data-site', 'YOUR_SITE_KEY')
s.async = true
document.head.appendChild(s)
})
</script>
<template>
<RouterView />
</template>Sleek automatically hooks into history.pushState so Vue Router navigations are tracked without any extra setup.
Nuxt.js
Use Nuxt's built-in useHead composable or add the script via nuxt.config.ts.
nuxt.config.ts (recommended)
// nuxt.config.ts
export default defineNuxtConfig({
app: {
head: {
script: [
{
src: 'https://getsleek.io/v1.js',
'data-site': 'YOUR_SITE_KEY',
async: true,
},
],
},
},
})app.vue — useHead composable
<script setup>
useHead({
script: [
{
src: 'https://getsleek.io/v1.js',
'data-site': 'YOUR_SITE_KEY',
async: true,
},
],
})
</script>
<template>
<NuxtPage />
</template>SvelteKit
Add the script tag inside the <svelte:head> block in your root layout.
<!-- src/routes/+layout.svelte -->
<svelte:head>
<script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>
</svelte:head>
<slot />SvelteKit's router uses the History API, which Sleek automatically intercepts — no extra configuration needed for SPA tracking.
Remix
Add the script inside the <head> in your root app/root.tsx.
// app/root.tsx
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from '@remix-run/react'
export default function App() {
return (
<html lang="en">
<head>
<Meta />
<Links />
<script
async
src="https://getsleek.io/v1.js"
data-site="YOUR_SITE_KEY"
/>
</head>
<body>
<Outlet />
<ScrollRestoration />
<Scripts />
</body>
</html>
)
}Astro
Add the script to your base layout file, typically src/layouts/Layout.astro.
---
// src/layouts/Layout.astro
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<slot name="head" />
<script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>
</head>
<body>
<slot />
</body>
</html>If you use Astro's viewTransitions, add a transition:persist attribute to the script tag to prevent it re-initialising on every page transition.
Gatsby
Use Gatsby's gatsby-browser.js or gatsby-ssr.js to inject the script globally.
gatsby-ssr.js
// gatsby-ssr.js
import React from 'react'
export const onRenderBody = ({ setHeadComponents }) => {
setHeadComponents([
<script
key="sleek"
async
src="https://getsleek.io/v1.js"
data-site="YOUR_SITE_KEY"
/>,
])
}gatsby-browser.js (route change tracking)
// gatsby-browser.js
export const onRouteUpdate = ({ location }) => {
// Sleek auto-detects pushState, but you can call the API manually too:
// window.analytics?.track('gatsby:route-change', { path: location.pathname })
}WordPress
There are two ways to add Sleek to a WordPress site.
Option A — Theme functions.php (recommended for developers)
// functions.php
function sleek_analytics_script() {
echo '<script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>';
}
add_action( 'wp_head', 'sleek_analytics_script' );Option B — WordPress Admin (no coding)
Go to Appearance → Theme Editor → header.php and paste the script tag just before </head>. Alternatively, install a plugin like "Insert Headers and Footers" and paste it in the Header section.
If you are using a page caching plugin (WP Rocket, W3 Total Cache), make sure the script tag is not excluded from the cached output.
Webflow
Webflow allows custom code in the <head> tag for all pages at once.
- Open your Webflow project and click Project Settings (gear icon).
- Navigate to the Custom Code tab.
- Paste the script tag in the Head Code field:
<script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>- Click Save Changes and republish your site.
Shopify
Add Sleek to your Shopify store by editing the theme files directly.
- From your Shopify admin, go to Online Store → Themes.
- Click Actions → Edit code next to your active theme.
- In the Layout folder, open
theme.liquid. - Paste the script tag just before
</head>:
<script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>This tracks across all storefront pages including product pages, collection pages, and the cart. It does not track inside Shopify admin itself.
Script options
The tracking script accepts a small set of data-* attributes to customise its behaviour.
| Attribute | Required | Description |
|---|---|---|
data-site | Yes | Your unique site key from the dashboard. |
data-endpoint | No | Override the default collector URL. Useful for self-hosted deployments or proxy setups. |
Example with custom endpoint
<script
async
src="https://getsleek.io/v1.js"
data-site="YOUR_SITE_KEY"
data-endpoint="https://analytics.yourdomain.com"
></script>Excluding yourself
To avoid polluting your stats with your own visits, you have several options:
1. Browser extension (easiest)
Install any ad-blocker extension on your development browser (uBlock Origin, etc.). Ad-blockers typically block analytics scripts on localhost and often on your own domain.
2. Exclude via localStorage
// Run this in your browser console once.
// Sleek will ignore all future events from this browser.
localStorage.setItem('sleek_exclude', '1')The script checks for localStorage.sleek_exclude === '1' and silently skips sending any events.
3. Exclude by URL pattern
Events sent from localhost, 127.0.0.1, and *.local are automatically filtered out in the dashboard view.
SPA / Client-side routing
Sleek automatically tracks single-page application navigations by hooking into the browser's history.pushState and the popstate event.
This means you get automatic page tracking for:
- React Router / TanStack Router
- Vue Router
- Next.js client-side navigation
- Nuxt Router
- SvelteKit router
- Any router that wraps the History API
Duration is tracked per-page: when the route changes, Sleek sends a duration event for the previous page before firing a new pageview.
Tracking custom events
Beyond automatic pageviews, you can send custom events to track any user interaction. Sleek exposes a global window.analytics object once the script loads.
Tracked events appear in the private dashboard Overview page under the Custom Events card. Public dashboards do not expose custom events.
Syntax
window.analytics.track(eventName, properties)| Parameter | Type | Description |
|---|---|---|
eventName | string | A human-readable name for the event (e.g. "Signup", "Add to Cart"). |
properties | object (optional) | Key-value pairs with additional context. Stored as JSON. |
Call window.analytics.track only after the script has loaded. If you call it immediately on page load, wrap it in a DOMContentLoaded listener or check that window.analytics exists first.
Event examples
Button click
<button onclick="window.analytics?.track('CTA Clicked', { button: 'hero' })">
Get Started
</button>Form submission
document.querySelector('#signup-form').addEventListener('submit', () => {
window.analytics?.track('Signup Form Submitted')
})React — event on button click
function UpgradeButton() {
function handleClick() {
window.analytics?.track('Upgrade Clicked', { plan: 'pro', source: 'banner' })
// ... open checkout
}
return <button onClick={handleClick}>Upgrade to Pro</button>
}E-commerce — add to cart
window.analytics?.track('Add to Cart', {
product_id: 'sku-1234',
product_name: 'Wireless Headphones',
price: 79.99,
currency: 'USD',
})Video play
document.querySelector('#hero-video').addEventListener('play', () => {
window.analytics?.track('Video Played', { video: 'hero-demo' })
})Privacy & Compliance
Sleek is designed to be compliant by default — no consent banners required.
✓ No cookies
Sleek does not set any cookies, first-party or third-party. There is nothing to consent to.
✓ No personal data collected
Visitors are identified by a short-lived, anonymous fingerprint derived from browser signals (user agent, screen size, timezone). This fingerprint cannot be reversed to identify an individual.
✓ GDPR
Because no personal data is processed and no cookies are stored, Sleek does not trigger GDPR consent requirements under most interpretations. You do not need a cookie banner for Sleek alone.
✓ CCPA
Sleek does not sell personal information, does not build user profiles, and does not share data with third parties. CCPA does not apply.
✓ Data storage
All analytics data is stored on servers within the EU (DigitalOcean, Amsterdam). No data is transferred to the US or other third parties.
Web Vitals
Sleek automatically collects Core Web Vitals from real users without any additional setup.
| Metric | Full name | Good | What it measures |
|---|---|---|---|
| LCP | Largest Contentful Paint | ≤ 2.5s | How fast the main content loads. |
| CLS | Cumulative Layout Shift | ≤ 0.1 | How much the page layout shifts unexpectedly. |
| FCP | First Contentful Paint | ≤ 1.8s | When the browser first renders any content. |
| TTFB | Time to First Byte | ≤ 800ms | How quickly the server starts responding. |
Vitals are collected via the PerformanceObserver API and sent as a separate vitals event after the page finishes loading. The dashboard shows the p75 value for each metric.
Public Dashboards
You can make any site's dashboard publicly viewable — no login required.
- Go to your site's Settings in the dashboard.
- Enable Public Dashboard and choose a slug (e.g.
mysite). - Share the URL:
getsleek.io/mysite
Public dashboards display the same analytics charts as your private dashboard but do not expose your site key, settings, or billing information.
Real-time visitors
The live visitor count shown at the top of your dashboard reflects visitors active in the last 5 minutes. It updates automatically every 3 seconds via a server-sent events stream.
For each active visitor, you can see:
- The current page they are on
- Their country (derived from IP, not stored raw)
- Whether they arrived from a referrer
The real-time feed is powered by Redis. No ClickHouse query is made for live data, so there is no latency penalty even at high traffic volumes.
Revenue Attribution
Connect your Stripe and/or Polar account to see revenue data directly inside your analytics dashboard. Understand which pages, referrers, and countries drive the most revenue.
How it works
Sleek reads your paid charges from Stripe (via a restricted API key) and your paid orders from Polar (via an Organization Access Token), then displays them alongside your traffic data. Revenue appears as an overlay on your time series chart and as a dedicated card on the overview page. If you connect both, their revenue is combined into a single total and the recent-payments list interleaves both providers newest-first.
Connecting Stripe
- Go to your Stripe Dashboard → API keys.
- Click Create restricted key.
- Give it a name (e.g. "Sleek Analytics") and enable read-only access to Charges. No other permissions are needed.
- Copy the key (starts with
rk_live_orrk_test_). - In Sleek, go to your site's Settings → Integrations and paste the key in the Stripe Revenue Tracking section.
Connecting Polar
- Open your Polar dashboard and go to Settings → Developers.
- Create a new Organization Access Token.
- Grant it the
orders:readscope. No other permissions are needed. - Copy the token (starts with
polar_oat_). - In Sleek, go to your site's Settings → Integrations and paste the token in the Polar Revenue Tracking section.
What you'll see
- Revenue card on the overview page showing total revenue (Stripe + Polar combined) for the selected time range
- Revenue overlay on the main traffic chart
- Recent payments list with amount, customer, and date — merged across providers
- Revenue data respects the same time range filters as the rest of your dashboard (today, 7d, 30d, etc.)
Both your Stripe key and Polar token are encrypted with AES-256-GCM before being stored. Sleek only has read-only access to your paid charges/orders — it cannot create payments, modify subscriptions, or access any other data in your accounts.
Disconnecting
To remove a connection, go to Settings → Integrations and click Disconnect in the relevant section. The encrypted key or token is deleted immediately. Disconnecting one provider leaves the other's revenue intact.
Stripe revenue requires a restricted key starting with rk_ — full API keys (sk_) are not accepted. Polar revenue requires an access token with the orders:read scope. Only paid USD payments are counted.
Plausible Import
Switching from Plausible? You can import your full historical data into Sleek in one click — zero data loss.
Exporting from Plausible
- Log in to your Plausible dashboard.
- Go to your site's Settings → Imports & Exports.
- Click Export. Plausible will generate a
.zipfile containing CSV files for visitors, pages, sources, and more. - Download the ZIP file to your computer.
Importing into Sleek
- In Sleek, go to your site's Settings → Import section.
- Click Upload and select the ZIP file you exported from Plausible.
- Sleek will parse the CSV files and import your data. This usually takes a few seconds.
- Once complete, you'll see the imported date range displayed in the settings.
What gets imported
- Visitors — daily visitor and pageview counts
- Pages — top pages with visit counts
- Sources — traffic sources and referrers
- Devices, browsers, OS — device breakdown data
- Countries — geographic breakdown
Imported data is merged with your live Sleek data in all dashboard views. When you select a time range that covers the import period, both datasets are combined seamlessly.
The import is a one-time operation. After importing, new data is collected by the Sleek tracking script. You can remove the imported data at any time from Settings.
Removing imported data
If you want to start fresh, go to Settings → Import and click Remove. This deletes only the imported Plausible data — your live Sleek data is not affected.
Only ZIP files exported from Plausible are supported. Other analytics platforms are not supported for import at this time.
Collect API
The tracking script sends events to POST /api/collect. You can also call this endpoint directly from your server or any HTTP client.
Endpoint
POST https://getsleek.io/api/collect
Content-Type: application/jsonRequest body
{
"site_key": "YOUR_SITE_KEY", // required — your public site key
"event_type": "pageview", // required — pageview | custom | duration | vitals
"session_id": "abc12345", // required — random per-tab ID
"visitor_id": "d4f9c2a1", // required — cookieless fingerprint
"url": "https://example.com/blog/post-1", // required
"referrer": "https://twitter.com", // optional
"duration": 42, // seconds (for duration events)
"event_name": "Signup", // custom event name
"properties": { "plan": "pro" }, // custom event properties
"lcp": 1200, // ms (vitals)
"cls": 0.05, // score (vitals)
"fcp": 800, // ms (vitals)
"ttfb": 200 // ms (vitals)
}Response
// 200 OK
{ "ok": true }
// 400 Bad Request
{ "error": "Missing required fields" }
// 401 Unauthorized
{ "error": "Invalid site key" }The endpoint has Access-Control-Allow-Origin: * — it is safe to call from any browser origin.
Public Data API
The Public Data API lets you read the same analytics aggregates used by your dashboard from your own scripts, local systems, or data pipelines.
Your tracking site key is not a read API key. Site keys are public and write-only for event collection. Create a private API key in Dashboard → Settings → API and keep it secret.
Using an AI agent?
Copy a ready-made instruction prompt for Cursor, Claude, Codex, ChatGPT, or any local agent. It tells the agent which credentials to ask for, which key not to use, and how to call the API safely from server-side code.
What you need
| Value | Where to find it | Use |
|---|---|---|
| API key | Dashboard → your site → Settings → API → Create key | Private read credential. Starts with sleek_live_. Send it as Authorization: Bearer ... |
| Site ID | Dashboard → your site → Settings → API → Site ID | The id used in Public Data API URLs, for example /api/v1/sites/SITE_ID/overview. |
| Tracking site key | Dashboard → your site → Settings → Installation | Public write-only key for the tracking script. Do not use this for Public Data API reads. |
Authentication
Authorization: Bearer sleek_live_...Keep API keys server-side only. Do not put sleek_live_... keys in frontend JavaScript, mobile app bundles, public Git repositories, or client-side environment variables.
List sites
This is the fastest way to verify your key and discover the Site ID attached to it.
curl https://getsleek.io/api/v1/sites \
-H "Authorization: Bearer YOUR_API_KEY"Analytics endpoints
Replace SITE_ID with the Site ID from Settings → API or from GET /api/v1/sites. This is not the public tracking site key from the install snippet.
GET /api/v1/sites/SITE_ID/overview
GET /api/v1/sites/SITE_ID/timeseries
GET /api/v1/sites/SITE_ID/pages
GET /api/v1/sites/SITE_ID/sources
GET /api/v1/sites/SITE_ID/geography
GET /api/v1/sites/SITE_ID/devices
GET /api/v1/sites/SITE_ID/performance
GET /api/v1/sites/SITE_ID/sparkline
GET /api/v1/sites/SITE_ID/globe
GET /api/v1/sites/SITE_ID/revenue
GET /api/v1/sites/SITE_ID/realtime
GET /api/v1/sites/SITE_ID/visitors
GET /api/v1/sites/SITE_ID/visitors/VISITOR_IDQuery parameters
| Parameter | Applies to | Description |
|---|---|---|
range | Most metrics | today, yesterday, 7d, 30d, 90d, 6mo, 1y, all. Defaults to 30d. |
tz | Date-bucketed metrics | IANA timezone such as America/New_York. Defaults to UTC. |
limit | pages, sources, geography, revenue, visitors | Maximum number of rows. Capped server-side. |
cities=1 | globe | Include city-level aggregates in globe responses. |
offset | visitors | Pagination offset for recent visitor history. |
Example
curl "https://getsleek.io/api/v1/sites/SITE_ID/overview?range=30d&tz=UTC" \
-H "Authorization: Bearer YOUR_API_KEY"Errors
// 401 Unauthorized
{ "error": "Missing or invalid API key" }
// 403 Forbidden
{ "error": "API key does not have the required scope" }
// 404 Not Found
{ "error": "Not found" }Troubleshooting
- Check that the script tag is actually present in the HTML source of your page (use View Source, not DevTools Elements — DevTools may show injected nodes).
- Confirm the data-site attribute matches the site key in your dashboard exactly.
- Open the Network tab in DevTools and look for a POST request to /api/collect. If it is blocked, an ad-blocker may be active.
- Data can take up to 10 seconds to appear. Hard-refresh your dashboard.
- Bounce rate is calculated per session. A session that has only one pageview counts as a bounce.
- If your SPA navigation is not being tracked (missing pushState hook), every "page" looks like a single pageview. Confirm the script is loaded before the router initialises.
- Make sure you call window.analytics.track only after the script has loaded. Wrap it in a load event listener or use optional chaining (window.analytics?.track).
- Custom events appear under the Events tab in your dashboard, not in the main overview.
- Run localStorage.setItem("sleek_exclude", "1") in your browser console to permanently exclude yourself from that browser.
- Alternatively, install an ad-blocker on your development machine.
- Add getsleek.io to your script-src directive:
Content-Security-Policy: script-src 'self' https://getsleek.io; connect-src 'self' https://getsleek.io;- Web Vitals rely on PerformanceObserver which is not available in all browsers (Firefox < 105 does not support LCP). Results are only included when the browser reports them.
- Vitals are sent after the page load event — if a user navigates away immediately, the vitals event may not be sent.
Still stuck?
Email us at hello@getsleek.io. We typically respond within a few hours.