Skip to content

Fix tracking that is not working

Symptom by symptom: no events, a domain that never turns green, a rejected request, missing revenue, an anonymous person, far more events than people, absent conversions.

Find your symptom, then follow the checks in order.

SymptomGo to
Nothing arrives at allNo events at all
A hostname you did not expectEvents from an unexpected hostname
A domain stuck on Waiting for eventsThe install checklist never turns green
Tracking domain will not verifyThe tracking host stays unverified
Your backend gets an errorThe events route rejects the request
Money accepted but not visibleRevenue is not showing
Someone stays AnonymousA person is not identified
Far more events than peopleFar more events than people, or a funnel that empties out
Google shows no conversionsGoogle Ads conversions are not arriving

No events at all

Live still says it is waiting for your first event.

  1. Is the snippet on the page you loaded? Open the page in a browser, view its source and look for the script tag. It has to be in the <head> of the page you actually loaded, not only on the home page.
  2. Did you deploy? A snippet pasted into a builder or a template does nothing until the site is published. This is the single most common cause.
  3. Is the snippet gated behind your own consent check? If your code only renders the tag after someone accepts, everybody who declines or never answers sends nothing. Load the tag unconditionally — the script handles consent itself.
  4. Is a content blocker stopping it? Try another browser, or one with no extensions. Blocked events never leave the page.
  5. Are you using the minimal variant with lt(…) calls of your own? A call made before the script has loaded throws in your page and the event is lost. Use the loader snippet — see Install the tracking script.

Events from an unexpected hostname

Or: Also sending events shows a hostname you never registered.

  1. Compare the two spellings. acme.com and www.acme.com are two different hostnames, and www is never stripped.
  2. Add the one visitors actually use with Add to checklist, and remove the one that never sends events.

Registering a hostname changes no collection: events are stored from whatever host runs your script, listed or not. The checklist is a report, not a filter — see Register the domains you track.

The install checklist never turns green

A domain sits on Waiting for events.

  1. Load a page on that domain yourself. Verification is driven by real traffic; a site nobody visits never turns green.
  2. Check the spelling against Also sending events, as above.
  3. Check the snippet is on that domain’s pages, and that the site is deployed.
  4. Wait for the page to recheck. It refreshes about every ten seconds.
  5. Check how old the traffic is. A domain counts as seen if events arrived in the last 30 days. A domain that goes quiet for longer falls back to Waiting for events — which is a true statement about your install.

One misconception worth clearing up. A visitor who declines consent does not stop a domain verifying. Their events still arrive — without cookies and without identifiers, but carrying the page address — so the hostname is seen. The only thing that sends nothing at all is a snippet your own code hid behind a consent check.

The tracking host stays unverified

The binding on Tracking domain reads Waiting for DNS to publish, or Verification failed.

  1. Publish the records exactly as shown, at your DNS provider, against the host you claimed.
  2. Press Check now as soon as your records are out, instead of waiting for the next automatic check.
  3. Check what the host answers. The script is fetched over your host and JavaScript is required back. A redirect, a parked page, an error status or a holding page fails the check — a host that merely resolves is not enough.
  4. Read the reason on Verification failed. It names what the last check got: a redirect, an HTTP status, a content type that was not JavaScript, or no answer.
  5. Press Retry. Automatic checks give up after 72 hours, and DNS changes can take that long by themselves, so failure often just means the two clocks did not line up. Retry as often as you like; the first check that passes flips the binding to Active.

After a binding goes active, copy the snippet again from Install. The script URL is inside the HTML you pasted.

The events route rejects the request

You call the Events API and get an error instead of an accounting.

The first three are decided at the edge, before the request reaches Tracking, and come back as { "error": { "code": …, "step": "public_key_check" } }. A key you have only just created can produce one of them for about 30 seconds, until the edge picks the key up — see Send events from your backend.

What you getWhat it meansFix
401 public_key_requiredNo key reached the edgeSend the key as Authorization: Bearer <your key>. X-Public-Key and the legacy X-Api-Key carry it too, but a request with none of them is refused.
401 public_key_invalidThe key is not one of this product’s keysCheck you are not pointing a staging key at production. A revoked key reads the same way, as does a key revoked less than about 30 seconds ago being retried too soon.
403 public_key_scopeThe key is real, but its scope does not cover the Events APIAsk a product Owner or Admin to widen the key’s scope in ProductSettingsPublic Access, or use a key that covers the tracking toolkit.
503 public_config_unavailableThe edge has not loaded its key snapshot yet and fails closedRetry. Nothing about your key or your request is wrong.
400 request body must be { "events": [ … ] }The envelope is wrongWrap your events in an events array.
400 events batch exceeds the 500-event limitToo many eventsSplit the batch.
413The body is over 64 KiBSend fewer events per request.

A success carrying entries in rejected is not a failure of the request — the other events were stored. Read the reason on each entry: the usual ones are a payment or refund without amount_minor, an amount_minor without a currency, and a ts outside the writable window (more than 24 hours ahead or about 90 days back). See Send revenue from your backend and the Tracking API reference.

Revenue is not showing

The money is accepted but does not appear where you expect it.

  1. Check where you are looking. A payment shows in the person’s journey at once, but the Lifetime value tile is recomputed on a schedule and can take up to an hour.
  2. Check the person is identified. Money reaches a person through the email — or an anonymous_id you captured in the browser — on the revenue event. With neither, the payment is stored but belongs to nobody: no Customer, no lifetime value, and it lands in Unattributed in the reports.
  3. Check the amount unit. amount_minor is a whole number of minor units. A payment sent as 49 when you meant 49.00 EUR is stored as 0.49 EUR and reads as a plausible small number everywhere.
  4. Check the currency. Nothing is ever summed across currencies, so revenue sent in a second currency appears as its own line rather than being added to the first.
  5. For the reports specifically, check the conversion. Revenue by source counts people whose first paid conversion falls in the date range, and a subscription only exists when you send subscription_started with a subscription_ref in props. Payments alone give a person money and Customer status but no conversion for the cohort to select.
  6. Check the date range. The range picks people by their first conversion, not payments by their date. A renewal from an older customer never appears in a recent range.

A person is not identified

They stay Anonymous on People when you expected an email address.

  1. Check that something told Tracking who they are. Identity comes from your own lt('identify', …) call or from the email on a backend event. It is never taken from the properties of a track call, and it is never taken from a form the visitor submitted — the script does not read forms.
  2. Check that identify is actually called. Add the call where your code learns the visitor’s own email address: the submit handler of your sign-up or sign-in form, the callback of a social login, or every page load of a signed-in visitor. Nothing identifies a visitor for you.
  3. Check that the call is not on the wrong form. Never call identify from a form that carries someone else’s address — inviting a colleague, sending a gift, referring a friend. That records the visitor as the other person, in silence: nothing errors, the People page is simply wrong, and removing the call later does not give back the visits already attributed to them.
  4. Check the consent state. In cookieless mode an identify call is dropped whole. A declining visitor cannot become a person, by design.
  5. Check the phone format. International form — a leading + and 8 to 15 digits — is always accepted; other shapes may be ignored rather than risk merging two people.
  6. Call identify again on every page load where the visitor is signed in. Repeating a known identity costs nothing and repairs a lost cookie.

See Identify a visitor and What tracking does with a consent answer.

Far more events than people, or a funnel that empties out

The event totals look right, but People, the report rows and the funnel steps built on them are all smaller than you expect.

  1. Check whether your site asks for consent. If it does, this is the usual answer, and it is not a fault. A visitor who declines statistics still sends events — they are in the totals, in Events and in Live — but those events carry no identifiers and are never resolved into a person.
  2. Read the two layers apart. Event counts cover everyone. Person counts, revenue, attribution and funnels cover the visitors who consented. A funnel follows one person from step to step, so declining visitors are missing from every step of it, not only the later ones.
  3. Put a number on the gap. Paid tracking health on the Google Ads page says how many of the paid clicks in its window were cookieless. That is the only place the share is measured, and it covers paid traffic.
  4. Check the banner is not hiding the snippet. If the event totals are short as well, the declining visitors are missing from both layers and the gap is an install problem rather than consented coverage — see No events at all, step 3.
  5. Check identification for the rest. Consented visitors only become known people through your own lt('identify', …) call or the email on an event sent from your backend.

See What tracking does with a consent answer and Read the attribution reports.

You turned on conversion feedback but Google shows nothing.

  1. Check the connection. Conversions shows a connect screen instead of its settings whenever the account is not connected.
  2. Check the master switch and the rules. Feedback is off by default, and a rule has to be enabled as well.
  3. Run one pass manually with Run now instead of waiting for the daily run.
  4. Read the funnel on Upload status. It says how many conversions were considered, how many were tied to a click, how many were uploaded, and where the rest went. No click id found and no value yet are the two usual answers, and neither is silent.
  5. If it is no click id: check paid tracking health and auto-tagging. Without Google’s auto-tagging there is no gclid, and without a gclid a conversion cannot be uploaded. Visitors who declined consent carry no click id either.
  6. If it is no value yet: the money has not landed. A revenue-valued rule waits for the revenue; those rows are picked up by a later run.
  7. Give Google time. Offline conversions can take hours to appear on their side.

See Close the loop on Google Ads.

Nothing here matches

Two places show you the raw truth before any report does:

  • Live — the events as they arrive, with the page, the source and the visitor behind each one.
  • Events — every event name your sites have ever sent, with counts. A typo shows up here as a near-duplicate row with a small count.

If Live shows what you expect and a report does not, the question is about attribution, not delivery.

Next steps

Was this page helpful?
Esc

Start typing to search the docs.

navigateselect