Clicktag docs

Goals and funnels

Clicktag goals count the visitors who view a page or fire a custom event, and funnels chain 2 to 6 steps completed in order on the same UTC day, with conversion by referrer, entry page or country. A site can have up to 20 goals and funnels combined.

Base URL: https://clicktag.io. Read this page as Markdown.

Create a goal

Open a site, choose the Goals tab and click New goal. A site without goals suggests its top events and pages; pick one to start from it.

Every step matches either:

  • Page: pageviews by path. is matches the path with or without a trailing slash, starts with matches the path and every path below it (/blog matches /blog/post but not /blogging, and / matches every page), contains matches anywhere in the path.
  • Event: a custom event by its exact name.

Add filters to narrow a step by page, source, country, device, browser, OS or UTM source, medium and campaign. Filters on different fields must all match. Two filters on the same field match either value. A step takes up to 6 filters, and a site up to 20 goals and funnels combined.

Page values start with /, contain no ? or #, and drop trailing slashes, so /pricing/ is saved as /pricing. Event names use letters, digits and _. In a funnel, a page step with no filters matches any pageview.

What the numbers mean

Number Meaning
Visitors Unique visitors per day: someone active on 3 days counts 3 times
Converted visitors Visitors with at least one matching pageview or event that day
Conversions Every matching pageview or event
Conversion rate Converted visitors divided by visitors, never above 100%
Funnel step Visitors who completed this step after all earlier steps, in order, on the same UTC day
Time to convert Median and 90th percentile time between two consecutive funnel steps

Dashboard filters apply to goals too: they narrow the pageviews and events that goals are counted over. Funnel bars show each step as a share of step 1, with the drop-off from the step before.

Click a goal to break it down by referrer, entry page, country, device, browser, OS or UTM parameter. Each visitor is counted under the value of their first pageview that day.

Coverage

Goals need a visitor identity on every pageview. The collector derives one from each request; imported data has none. When less than 99% of the pageviews in a range carry an identity, the Goals tab says so:

Goals cover 72% of pageviews in this range (older imported data has no visitor identity).

Changes against the previous period only show when at least 99% of that period's pageviews carry an identity too. A range without any identified pageviews shows No visitor data in this range.

Events sent from your server with POST /events get an identity built from your server's IP address, not the visitor's, so they can't follow the visitor's pageviews in a funnel or inherit their referrer and entry page.

Access

Goal routes need the site owner's Firebase ID token or a management key, with read for GET and manage for changes, even for public sites. Every route, reads included, needs a verified site. Sites you don't own return 404 unknown site. See Authentication.

Method Route Result
GET /api/sites/{hostname}/goals List goals, oldest first
POST /api/sites/{hostname}/goals Create a goal (201)
GET /api/sites/{hostname}/goals/stats Numbers for every goal in a date range
GET /api/sites/{hostname}/goals/{id} Read one goal
PUT /api/sites/{hostname}/goals/{id} Replace a goal
DELETE /api/sites/{hostname}/goals/{id} Delete a goal
GET /api/sites/{hostname}/goals/{id}/stats Breakdown and funnel timing for one goal

Goal object

json
{
  "id": "0b7c3e52-6a0e-4f0d-9d51-3f1f5c2a9e10",
  "name": "Pricing to signup",
  "kind": "funnel",
  "steps": [
    {
      "name": "Pricing",
      "match": "pageview",
      "filters": [{ "key": "page", "op": "eq", "value": "/pricing" }]
    },
    {
      "name": "Signup completed",
      "match": "event",
      "filters": [{ "key": "event", "op": "eq", "value": "signup_completed" }]
    }
  ],
  "created_at": "2026-09-30T09:12:44.000Z",
  "updated_at": "2026-09-30T09:12:44.000Z"
}
Field Rules
name 1 to 80 characters
kind goal (exactly 1 step) or funnel (2 to 6 steps)
steps[].name Optional, up to 40 characters; stats call an unnamed step Step n
steps[].match pageview or event
filters[].key page, referrer, country, device, browser, os, utm_source, utm_medium, utm_campaign, event
filters[].op eq; prefix and contains only with page
filters[].value Page paths start with /; event names match ^[A-Za-z0-9_]{1,256}$

An event step has exactly one event filter with op: "eq"; a pageview step cannot use the event key. A goal step needs at least one filter. Two identical steps in a row, or the same filter twice in a step, are rejected.

A goal that no longer passes validation is listed with "invalid": true and left out of stats until it is saved again.

Create and change goals

bash
curl -X POST https://clicktag.io/api/sites/example.com/goals \
  -H "Authorization: Bearer $TT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"Signup completed","kind":"goal","steps":[{"match":"event","filters":[{"key":"event","op":"eq","value":"signup_completed"}]}]}'

POST returns the new goal with 201. PUT takes the same body, replaces the whole goal and returns it. DELETE returns {"deleted": "<id>"}. Goals are computed at query time, so a new or changed goal applies to past data too.

Goal stats

GET /api/sites/{hostname}/goals/stats?from=1790208000&to=1790812800&tz=Europe/Amsterdam

from and to are unix seconds with the same defaults and limits as the statistics API, tz sets the chart buckets and the previous period, and f takes the same filters.

json
{
  "granularity": "day",
  "visitors": 2016,
  "previous_visitors": 1874,
  "coverage": {
    "pageviews": 5230,
    "identified": 5230,
    "previous": { "pageviews": 4870, "identified": 4870 }
  },
  "goals": [
    {
      "id": "7d2a8c1e-3b4f-4e6a-8c9d-0e1f2a3b4c5d",
      "name": "Signup completed",
      "kind": "goal",
      "conversions": 143,
      "converting_visitors": 118,
      "rate": 0.0585,
      "previous": { "conversions": 126, "converting_visitors": 104, "rate": 0.0555 },
      "series": [
        { "t": 1790208000, "conversions": 19 },
        { "t": 1790294400, "conversions": 23 }
      ]
    }
  ],
  "funnels": [
    {
      "id": "0b7c3e52-6a0e-4f0d-9d51-3f1f5c2a9e10",
      "name": "Pricing to signup",
      "kind": "funnel",
      "steps": [
        { "name": "Pricing", "visitors": 412 },
        { "name": "Signup completed", "visitors": 61 }
      ],
      "previous": [388, 52]
    }
  ],
  "invalid": []
}
  • rate is a fraction from 0 to 1, or null when the range has no visitors.
  • series has one entry per hour for ranges up to 4 days, otherwise one per day, zero-filled. Funnels have no series.
  • The previous period is the range moved back by as many calendar days as it covers in tz, like previous_range in the overview. previous_visitors and every previous are null when less than 99% of its pageviews carry an identity, or none do.

Goal breakdown

GET /api/sites/{hostname}/goals/{id}/stats?from=1790208000&to=1790812800&dim=referrers&limit=10

dim is one of referrers, pages (entry page), countries, devices, browsers, os, utm_sources, utm_mediums or utm_campaigns. limit is 1 to 100, default 10. from, to and f work as in goal stats. Rows are sorted by visitors, most first.

json
{
  "dim": "referrers",
  "breakdown": [
    { "name": "google.com", "visitors": 880, "steps": [170, 46] },
    { "name": "Direct / none", "visitors": 640, "steps": [131, 22] }
  ],
  "timing": [{ "p50_s": 204, "p90_s": 3310, "n": 61 }]
}

visitors counts the visitors whose first pageview that day had this value, and steps how many of them reached each step. timing has one entry per step transition, in seconds, and is null for goals; p50_s and p90_s are null when n is 0.

Errors

Status Error
400 invalid json, invalid range, invalid filter, unknown dimension, this goal has too many filters to measure, remove some or a validation message such as step 2 filter 1: unknown key
401 sign in required, or invalid or revoked management key
403 Install your tracking script to verify this site first; a management key without the needed scope gets scope read required or scope manage required
404 unknown site, unknown goal
429 too many goals for this site; a management key sending changes too fast gets rate limited, retry in <n>s with Retry-After
504 date range too large for goals, narrow it