Skip to content

Clicktag event tracking

Free event tracking for websites with one function call

Clicktag tracks custom events for free with no credit card and no cookies. Load one 4 KB script and call window.tt_event in the browser, or send a JSON POST from your server, then use your events across up to 50 goals and funnels.

Pageviews tell you which URLs people visit, not what they do once they are there. To measure button clicks, form submissions, pricing toggles or backend signups, you send a custom event. In Clicktag, you record custom events by calling window.tt_event(name, metadata) in JavaScript or sending a JSON POST request to /events from your server. Both methods are included for free on up to 50 websites per account, without cookies or an API key.

When you need custom events

Clicktag's latest.js script tracks pageviews and single-page app route changes through pushState and popstate automatically. It does not record link clicks, outbound links, file downloads or form submissions on its own.

When an action does not change the URL, or when you want to verify that a step actually succeeded rather than just loading a page, you fire a custom event. Common cases include:

  • Clicking a call-to-action button, a pricing tier toggle, or an external link (see click tracking).
  • Downloading a PDF, ZIP or installer file that does not run JavaScript (see download tracking).
  • Submitting a newsletter, contact or lead form where the visitor stays on the same URL.
  • Completing a registration or checkout step on the server after validation.

Track events in JavaScript with tt_event

Once latest.js is on your site, you can call window.tt_event(name, metadata, callback) inside any DOM event listener or application handler.

<button id="upgrade-btn">Upgrade Plan</button>

<script>
  document.getElementById("upgrade-btn").addEventListener("click", function () {
    if (typeof window.tt_event === "function") {
      window.tt_event("upgrade_clicked", { tier: "pro", period: "annual" });
    }
  });
</script>

If your code might trigger an event before the async latest.js script finishes loading, place the queue snippet in your document head before the script tag.

<script>
  window.tt_event =
    window.tt_event ||
    function () {
      window.tt_event.q.push(Array.from(arguments));
    };
  window.tt_event.q = window.tt_event.q || [];
</script>
<script
  async
  src="https://clicktag.io/latest.js"
  data-hostname="example.com"
></script>

Keep standard function () syntax instead of an arrow function so arguments is captured properly, and keep the window.tt_event.q array. When latest.js loads, it checks for that q array, takes over tt_event, and replays your queued calls in order.

An optional third argument (or second argument if you skip metadata) is a callback function. It runs when the /simple.gif network request finishes or fails, so do not gate critical application logic on it. Full browser details and TypeScript definitions are in the custom events documentation.

Send events from your backend

Ad blockers, closed tabs and network drops can stop browser events from firing. If you want to record completed signups or server-side actions without loading client scripts, send a single JSON POST request to https://clicktag.io/events. No API key is needed.

async function recordSignup(hostname, visitorUserAgent) {
  if (!hostname || !visitorUserAgent) {
    throw new Error(
      "A registered hostname and the real visitor user-agent are required",
    );
  }

  const response = await fetch("https://clicktag.io/events", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    signal: AbortSignal.timeout(10_000),
    body: JSON.stringify({
      hostname,
      type: "event",
      event: "signup_completed",
      path: "/signup",
      metadata: { plan: "starter" },
      ua: visitorUserAgent,
    }),
  });

  const result = await response.text();
  if (!response.ok) {
    throw new Error(`Event collector ${response.status}: ${result}`);
  }

  return result;
}

When sending server-side events, keep these rules in mind:

  1. Always include "type": "event" in the JSON body. If you leave it out, the collector records the hit as a pageview.
  2. Pass the real incoming visitor user-agent string in the ua field, not a made-up string.
  3. Pass the visitor's browser IANA timezone if you have it, because Clicktag gets the country from the time zone rather than the IP address.
  4. Send one JSON object per request. Batch requests and form-encoded bodies are not accepted, and timestamps are assigned when the request reaches the server.

How to name events so you can compare them

Clicktag accepts a plain JavaScript object as metadata, up to 4,096 characters of serialized JSON. However, metadata is stored as raw context and cannot be grouped or filtered in dashboard reports or the Stats API.

Put any detail you want to compare or filter in reports directly into the event name.

The collector normalizes every incoming event name automatically: it cuts names at 256 characters, turns sequences of non-alphanumeric characters into underscores, and strips leading or trailing underscores. Using lowercase snake_case keeps your code aligned with what appears in your dashboard.

Action you want to measure Hard to compare (relies on metadata) Easy to compare (detail in the name) Max length
Pricing plan click plan_clicked with { tier: "pro" } plan_clicked_pro_annual 256 characters
File download file_downloaded with { file: "guide.pdf" } download_guide_pdf 256 characters
Lead form submission form_submitted with { location: "footer" } lead_form_submitted_footer 256 characters
Outbound link click outbound_click with { dest: "github" } outbound_click_github 256 characters
Server-side signup signup with { method: "oauth" } signup_completed_oauth 256 characters

Use custom events in goals and funnels

Recording events is step one; connecting them to traffic sources and multi-step flows is where they become useful. Each site in Clicktag supports up to 50 goals, with funnels included in that count.

In goal tracking, you pick either a page path or a custom event name. Clicktag immediately computes conversions, converted visitors and conversion rate (converted visitors divided by visitors) across the data you have already collected, so creating a new goal includes your historical events.

You can then break that goal down by referrer, entry page or country to see whether visitors from google.com or chatgpt.com convert better on lead_form_submitted_footer. Use browser events for this: backend events carry your server's IP address, so they do not inherit the visitor's referrer or entry page.

In funnel analytics, you combine 2 to 6 steps in order - mixing pageviews and custom events - completed on the same UTC day. The report shows step-by-step drop-off along with the median and p90 time to convert.

You can also query event counts programmatically with the Stats API:

curl --fail-with-body -sS --max-time 30 \
  'https://clicktag.io/api/sites/demo.bitgate.dev/breakdown?dim=events&limit=10'

FAQ

Does Clicktag track link clicks, downloads or forms automatically?

No. The latest.js script records pageviews on its own, but not link clicks, downloads or form submissions, and Clicktag does not serve /auto.js or /auto-events.js helper scripts. You attach window.tt_event calls to the specific buttons, download links or forms you want to measure.

Can I filter or group reports by event metadata?

No. Metadata (up to 4,096 characters of JSON) is stored with the event, but custom metadata filtering and grouping are not supported in reports or the API. Put any value you need to compare into the 256-character event name itself, such as upgrade_clicked_pro.

How do custom events work with Simple Analytics queues?

Clicktag only reads its own window.tt_event.q array and leaves any existing sa_event queue untouched. If you are running both tools or migrating, call each provider's function separately rather than pointing Clicktag at sa_event.

Why don't my backend events match real visitors?

Visitor hashes are built from the UTC day, site, connection IP, user-agent and a daily random salt, and Clicktag does not accept client IP or user-ID overrides on /events. When your backend sends events from a single server IP, it does not recreate individual end-user visitor identities.

What is not included in Clicktag event tracking?

Events carry no revenue values, and Clicktag has no individual visitor profiles, so you cannot follow one person's events over time. Reports aggregate events by name, with a visitor count for each event.

Updated October 30, 2026