Quick start
What Meterio is, how it works, and what it takes to get your product sending events. It is a one-time setup, and this page walks you through all of it.
Start setting up →What Meterio answers
Three questions:
- Are the people who signed up still using the product?
- Are they reaching the moment the product is built for?
- Is that number going up week over week?
It is opinionated on purpose. The metrics are pre-computed and come with coaching for the stage you are at, which means there is no SQL to write, no funnel to build, and no dashboard to assemble. If you want to slice arbitrary data, this is the wrong tool.
The model is Lean Analytics: one metric that matters at a time, and which one depends on your stage. Background is in the guides and in Lean Analytics.
How data gets in
One snippet, then autocapture does the rest. You do not hand-instrument events:
| Event | When it fires |
|---|---|
| $session_started | A new session begins |
| $pageview | Page load, and every SPA route change |
| $click | Click on a button, link or submit |
| $form_submit | Form submission |
| $input_change | Field value changes (debounced) |
| $scroll_depth | 25 / 50 / 75 / 100% milestones |
| $time_on_page | Page unload or tab hidden |
| $user_identified | A user's identity is resolved |
- Sessions restart on a first visit, at a day boundary, or when a tab comes back after 30 minutes idle.
- Single-page apps are handled. History navigation is patched, so route changes count as page views.
- Identity is the one call you make yourself: pass your stable user id to
identify()when someone signs in, or the metrics that depend on knowing who a user is cannot be calculated correctly. Detection for Supabase, Firebase, Clerk and Auth0 exists behind opt-in flags, off by default. - Privacy: input values are never captured, only their length. Sensitive fields are skipped, URLs and text are scrubbed, and Global Privacy Control and Do Not Track are respected. Capture is on by default;
requireConsentmakes it dormant until someone opts in, andblacklistSelectorsopts individual elements out.
Setting it up: four stages
Setup is resumable, and progress is derived live rather than stored, so revoking a key or clearing your key action reopens that stage.
1. Create a key
Creating an application no longer mints a key for you; you create one deliberately, with a label. Five active keys per application. Keys are not environment-scoped. The isolation boundary is the application itself. An ingest key is write-only and ships in your client bundle by design, so it is safe to commit.
2. Install
The integration is one script tag, which makes it a task you can delegate. Paste this into Claude Code, Cursor, or whichever agent you work with, and it will place the snippet and commit the change.
Add Meterio product analytics to this app.
1. Add this snippet to the <head> of the root HTML document. If the project has
no index.html, add it to the framework's own document or root layout head —
app/layout.tsx in Next.js, app.html in SvelteKit, index.html in a Vite app.
<script>
!function(w,d,s){
var m=w.Meterio=w.Meterio||{};
if(m.__loaded)return;
m.__q=m.__q||[];
['init','identify','track','alias','reset','setUserProperties','optIn','optOut']
.forEach(function(f){m[f]=function(){m.__q.push([f,Array.from(arguments)]);};});
var e=d.createElement(s);e.async=1;
e.src='https://cdn.meterio.app/sdk/v1/meterio.min.js';
d.head.appendChild(e);
}(window,document,'script');
Meterio.init({ apiKey: 'mtr_YOUR_API_KEY' });
</script>
2. Replace mtr_YOUR_API_KEY with the key from the Meterio setup page.
3. Call Meterio.identify() with the app's stable user id wherever a user signs
in or a session is restored. Metrics that depend on knowing who a user is need
that id.
Apart from identify, do not add any other analytics calls — Meterio collects
page views and interactions automatically.Or do it yourself. Paste this into <head>. The stub is what makes ordering safe: calls made before the bundle arrives are queued and flushed once it loads, where a bare script tag would drop them. Then call identify() with your user id wherever someone signs in.
<script>
!function(w,d,s){
var m=w.Meterio=w.Meterio||{};
if(m.__loaded)return;
m.__q=m.__q||[];
['init','identify','track','alias','reset','setUserProperties','optIn','optOut']
.forEach(function(f){m[f]=function(){m.__q.push([f,Array.from(arguments)]);};});
var e=d.createElement(s);e.async=1;
e.src='https://cdn.meterio.app/sdk/v1/meterio.min.js';
d.head.appendChild(e);
}(window,document,'script');
Meterio.init({ apiKey: 'mtr_YOUR_API_KEY' });
</script>Or install from npm with npm i meterio-sdk@beta . The package ships under the beta tag.
npm i meterio-sdk@beta
// In your app:
import { init } from 'meterio-sdk';
init({ apiKey: 'mtr_YOUR_API_KEY' });The /sdk/v1/ path rolls forward, so it carries no integrity hash. Pinning one to a moving file would break your embed the day it changes. If you would rather pin, the SDK docs describe an immutable per-version URL you can add a hash to.
3. Verify
Deploy, then open your product and click around. That is the step, not waiting. Events flush every 5 seconds, so allow about ten before they show up.
4. Choose your key action
The one thing a user does that means they got value: sent the message, published the post, closed the ticket. Exactly one, because a metric that matters cannot be four metrics. Everything downstream is measured against it, so skipping this leaves setup unfinished. See how to find your key action.
What you get once it is running
The dashboard shows each metric with a plain-language explanation of what it is telling you, and coaching for the stage you are at.
The guides cover the thinking behind each metric when a number surprises you.
When you see no events
Almost always one of these:
- The snippet still carries the literal
mtr_YOUR_API_KEYinstead of your key. - It sits after
</body>rather than inside<head>. - You deployed, but nobody has opened the product and clicked around yet.
- You checked too soon. Events flush every 5 seconds.
- The key was revoked, or belongs to a different application.
- You set
requireConsent: trueand no one has opted in yet, so the SDK is dormant by design.
Setup is the authoritative answer on whether it is working, because it reads live state rather than a stored flag.
Ready? Create a key and paste the snippet.
Start setting up →