Skip to content
Analytik
Esc
↑↓navigate↵open⌘Jpreview
On this page

Funnels and goals

Record goals from the page, declaratively or from your server, then define funnels over pages and goals and read the drop-off.

A goal is any named event you care about: a sign-up, a click on a pricing button, a finished onboarding. Anything the tracker records with a name can be used in a funnel.

Record goals

From the page:

analytik.track('signup', { plan: 'pro' })

Or declaratively, with no JavaScript:

<button data-analytik-event="signup" data-analytik-plan="pro">Start free</button>

From your server, when the action happens there (a webhook, a background job), with the site’s API key:

curl -X POST https://analytics.example.com/api/v1/goals \
  -H "Authorization: Bearer $ANALYTIK_SITE_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"name":"trial_started","props":{"plan":"pro"},"visitorId":"VISITOR_ID"}'

visitorId or email ties the goal to a visitor; email matching follows the rules of the site’s privacy mode. Property values are strings, numbers, booleans or null.

The Events view lists goals with counts, and GET /api/sites/SITE_ID/breakdown?dimension=goal gives the same as data. Break down a goal by source or channel to see which source converts.

Break down a goal by its properties

Click a goal in the Events view, and a Properties card appears for it. It lists the goal’s property keys (up to 20); pick a key and it shows the values with their visitors, the 50 most frequent and the rest as one line. A goal signup sent with { plan: "pro" } or { plan: "free" } therefore shows how many signups chose each plan. Clicking a value filters the whole dashboard to the visits where this goal had it, so you can see where the Pro signups came from.

The filter is prop:<key>: ?f.prop%3Aplan=pro in the dashboard address, prop:plan=pro in the HTTP API, and { 'prop:plan': 'pro' } in stats filters. It works with every view, saved segments and shared dashboards. When a goal filter is set as well, the same goal event has to carry the value. Only goal events are read, never sign-ins, and keys may use 1 to 64 letters, digits, _, . and -; other keys are left out.

Send values you would group by: a plan, a button, a currency. A value that is different every time, such as an order id, fills the list with single rows. Never send personal data in properties: they are stored as sent and shown to everyone who can read the dashboard or a share link.

Count missing pages

To see which pages visitors fail to find, send a goal from your 404 page with the path as a property:

<script>
  analytik('track', 'not_found', { path: location.pathname })
</script>

Click not_found in the Events view and pick the key path.

Define a funnel

A funnel is an ordered list of steps. Each step is a page path or a goal name. A page path may end in * to match everything below it.

curl -b cookies.txt -X POST https://analytics.example.com/api/sites/SITE_ID/funnels \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Pricing to signup",
    "steps": [
      { "type": "page", "match": "/pricing" },
      { "type": "page", "match": "/signup" },
      { "type": "goal", "match": "signup" }
    ]
  }'

You can also build funnels in the dashboard’s Funnels view.

Read the result

GET /api/sites/SITE_ID/funnels/FUNNEL_ID returns a FunnelResult: the funnel and, for each step, the number of visitors who reached it and the drop-off from the previous step (null for the first).

import type { FunnelResult } from 'analytik'

const res = await fetch('/api/sites/SITE_ID/funnels/FUNNEL_ID?from=2026-10-01&to=2026-10-31', {
  credentials: 'include',
})
const result: FunnelResult = await res.json()

for (const step of result.steps) {
  console.log(step.match, step.visitors, step.dropOff)
}

Set a target for a goal

A goal can have a target: a number to reach within a week, a month or a quarter. Add targets in Settings → Goals, or send targets when you update the site:

curl -X PATCH /api/sites/SITE_ID -H 'Content-Type: application/json' \
  -d '{ "targets": [{ "goal": "signup", "target": 1000, "period": "month" }] }'

The Goals block on the overview then shows the count so far, the share of the target and, once a tenth of the period has passed, where the period is heading at the current pace (a straight-line estimate). A site can have up to 20 targets.

Progress always covers the target’s own period, in the site’s time zone with weeks starting on Monday, whatever date range the dashboard shows. GET /api/sites/SITE_ID/targets returns it as TargetsResult, and analytik.stats.targets(siteId) does the same in code. All targets with the same period are counted with one query. Targets are admin only: shared dashboards do not show them.

Core Web Vitals

The Pages view has a Core Web Vitals card: the 75th percentile of LCP, INP and CLS for the whole site and for the fifty pages with the most reports, each rated good, needs work or poor with Google’s thresholds (LCP up to 2.5 s and over 4 s, INP up to 200 ms and over 500 ms, CLS up to 0.1 and over 0.25). Three in four page loads are at least as good as the number shown. It is also a block you can add to the overview.

Measuring is opt-in per site and sampled; see the tracker guide for how to turn it on. GET /api/sites/SITE_ID/vitals returns the same numbers as VitalsResult for the dashboard’s range, and analytik.stats.vitals(siteId, query) does it in code. The server reads the newest 20,000 reports of the range and ranks them in the database. Shared dashboards do not show the card, and the route is admin only.

Reports follow the site’s retention setting and are deleted with the site.

Journeys

The Journeys view shows where sessions go after they land, as a flow of up to four steps: the most common next pages at each step, a bar for everything else, and where sessions end. Click a page, or focus it and press Enter, to filter the dashboard by it (step one filters by entry page, later steps by page viewed). A table with the same links is there for screen readers.

Only sessions that viewed more than one page make a path. Reloading a page does not count as a step. Each step lists its eight biggest pages and groups the rest as “Other pages”. The server reads the newest 5,000 sessions of the range, and the range is cut to its last 30 days; the view says so when either applies. The unit is sessions, so journeys work in every privacy mode.

GET /api/sites/SITE_ID/journeys returns JourneysResult for the dashboard’s range and filters, and analytik.stats.journeys(siteId, query) does it in code. The response may be cached for five minutes. Shared dashboards do not show the view, and the route is admin only.

Retention

The Retention view shows weekly cohorts of new visitors: each row is the visitors first seen in one week, and each cell is the share of them who came back that many weeks later (week 0 is their own week, so it reads 100%). The values are written in the cells, and the colour only adds to them. At most the latest 12 cohorts of the range are shown, each followed for up to 12 weeks, and weeks run from Monday in UTC. Filters apply to each visitor’s first session.

It needs visitors to be recognised across days, so only sites in the persistent privacy mode have it. In strict and enriched mode the visitor id changes at least monthly, so the view explains how to turn it on instead of drawing a heatmap.

GET /api/sites/SITE_ID/retention returns RetentionResult, and analytik.stats.retention(siteId, query) does the same in code. It is one aggregate query, kept by the browser for five minutes. Shared dashboards do not show the view, and the route is admin only.

Notes on accuracy

In strict mode a visitor who changes network mid-funnel is counted as two visitors, and one of them drops out. Expect funnel completion to read a little lower than in persistent mode.

Was this page helpful?