Sleek Analytics
introduction
Documentation

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.

1Create your account

Sign up at getsleek.io/sign-up and add your first site. You'll get a unique site key.

2Add the script to your site

Paste this snippet before the closing </body> tag (or inside <head>):

html
<script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>
3Visit your dashboard

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

bash
npx sleek-analytics init

That's it. The CLI will:

  1. Detect your framework automatically
  2. Ask for your Site ID (from your Sleek dashboard)
  3. Inject the analytics script into the correct file

Supported frameworks

FrameworkHow 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.jsapp.head.script in nuxt.config
SvelteKit<svelte:head> in +layout.svelte
Remix<script> in app/root
Astro<script> in layout .astro file
GatsbysetHeadComponents in gatsby-ssr.js
WordPresswp_head hook in functions.php
HTML / Static<script> in index.html

Global install

If you prefer to install it globally:

bash
npm i -g sleek-analytics
sleek-analytics init

What gets added

A single async script tag pointing to your Sleek instance:

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

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:

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:

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:

html
<body>
  <div id="root"></div>
  <script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>
</body>

Option B — useEffect hook

tsx
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)

html
<!-- 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

vue
<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)

ts
// 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

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

svelte
<!-- 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.

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.

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

jsx
// 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)

js
// 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)

php
// 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.

  1. Open your Webflow project and click Project Settings (gear icon).
  2. Navigate to the Custom Code tab.
  3. Paste the script tag in the Head Code field:
html
<script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>
  1. Click Save Changes and republish your site.

Shopify

Add Sleek to your Shopify store by editing the theme files directly.

  1. From your Shopify admin, go to Online Store → Themes.
  2. Click Actions → Edit code next to your active theme.
  3. In the Layout folder, open theme.liquid.
  4. Paste the script tag just before </head>:
html
<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.

AttributeRequiredDescription
data-siteYesYour unique site key from the dashboard.
data-endpointNoOverride the default collector URL. Useful for self-hosted deployments or proxy setups.

Example with custom endpoint

html
<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

js
// 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

js
window.analytics.track(eventName, properties)
ParameterTypeDescription
eventNamestringA human-readable name for the event (e.g. "Signup", "Add to Cart").
propertiesobject (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

html
<button onclick="window.analytics?.track('CTA Clicked', { button: 'hero' })">
  Get Started
</button>

Form submission

js
document.querySelector('#signup-form').addEventListener('submit', () => {
  window.analytics?.track('Signup Form Submitted')
})

React — event on button click

tsx
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

js
window.analytics?.track('Add to Cart', {
  product_id: 'sku-1234',
  product_name: 'Wireless Headphones',
  price: 79.99,
  currency: 'USD',
})

Video play

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

MetricFull nameGoodWhat it measures
LCPLargest Contentful Paint≤ 2.5sHow fast the main content loads.
CLSCumulative Layout Shift≤ 0.1How much the page layout shifts unexpectedly.
FCPFirst Contentful Paint≤ 1.8sWhen the browser first renders any content.
TTFBTime to First Byte≤ 800msHow 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.

  1. Go to your site's Settings in the dashboard.
  2. Enable Public Dashboard and choose a slug (e.g. mysite).
  3. 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

  1. Go to your Stripe Dashboard → API keys.
  2. Click Create restricted key.
  3. Give it a name (e.g. "Sleek Analytics") and enable read-only access to Charges. No other permissions are needed.
  4. Copy the key (starts with rk_live_ or rk_test_).
  5. In Sleek, go to your site's Settings → Integrations and paste the key in the Stripe Revenue Tracking section.

Connecting Polar

  1. Open your Polar dashboard and go to Settings → Developers.
  2. Create a new Organization Access Token.
  3. Grant it the orders:read scope. No other permissions are needed.
  4. Copy the token (starts with polar_oat_).
  5. 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

  1. Log in to your Plausible dashboard.
  2. Go to your site's Settings → Imports & Exports.
  3. Click Export. Plausible will generate a .zip file containing CSV files for visitors, pages, sources, and more.
  4. Download the ZIP file to your computer.

Importing into Sleek

  1. In Sleek, go to your site's Settings → Import section.
  2. Click Upload and select the ZIP file you exported from Plausible.
  3. Sleek will parse the CSV files and import your data. This usually takes a few seconds.
  4. 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

text
POST https://getsleek.io/api/collect
Content-Type: application/json

Request body

json
{
  "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

json
// 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

ValueWhere to find itUse
API keyDashboard → your site → Settings → API → Create keyPrivate read credential. Starts with sleek_live_. Send it as Authorization: Bearer ...
Site IDDashboard → your site → Settings → API → Site IDThe id used in Public Data API URLs, for example /api/v1/sites/SITE_ID/overview.
Tracking site keyDashboard → your site → Settings → InstallationPublic write-only key for the tracking script. Do not use this for Public Data API reads.

Authentication

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

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

text
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_ID

Query parameters

ParameterApplies toDescription
rangeMost metricstoday, yesterday, 7d, 30d, 90d, 6mo, 1y, all. Defaults to 30d.
tzDate-bucketed metricsIANA timezone such as America/New_York. Defaults to UTC.
limitpages, sources, geography, revenue, visitorsMaximum number of rows. Capped server-side.
cities=1globeInclude city-level aggregates in globe responses.
offsetvisitorsPagination offset for recent visitor history.

Example

bash
curl "https://getsleek.io/api/v1/sites/SITE_ID/overview?range=30d&tz=UTC" \
  -H "Authorization: Bearer YOUR_API_KEY"

Errors

json
// 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

No data is appearing in my dashboard
  • 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.
My bounce rate looks wrong / too high
  • 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.
Custom events are not showing up
  • 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.
I see my own visits in the stats
  • 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.
The script is blocked by Content Security Policy (CSP)
  • Add getsleek.io to your script-src directive:
http
Content-Security-Policy: script-src 'self' https://getsleek.io; connect-src 'self' https://getsleek.io;
Web Vitals are missing or showing 0
  • 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.