The Matomo Tracking API Answers 200 to Everything
Contents
Matomo can be fed without a line of JavaScript: a single HTTP request to matomo.php carries a page view, an event or a goal. That makes it the obvious way to record what a browser never sees – an order confirmed in the back office, a subscription cancelled by phone, a batch job that finished.
The property that makes it pleasant to use is also the one that makes it hard to debug. The endpoint answers every request the same way: HTTP 200 and an empty image, whether the hit was recorded, silently discarded or aimed at a site that does not exist. Everything below is about telling those three apart without the response saying anything.

What a Hit Needs at a Minimum
Three parameters decide whether anything is recorded at all: idsite, the site the hit belongs to, rec=1, which is the switch that turns a request into a recording, and something to name the hit – a url, an action_name or both. Without rec=1 the request is answered exactly as before and nothing is stored.
Everything else is optional in the sense that the hit survives without it, and rarely optional in the sense that the report stays useful. A hit without a URL appears in the reports without a name, which is the kind of entry that is discovered months later and can no longer be assigned to anything.
The Visitor Id Decides Everything Else
The parameter _id is a visitor identifier of exactly sixteen hexadecimal characters. Not fifteen, not a UUID with its dashes, not the thirty-two characters of an MD5 hash – Matomo silently ignores anything that does not match the format, and silently is the operative word.
What happens then is worse than an error. Without a usable _id, Matomo derives the visitor from IP address and user agent, which works acceptably in a browser and collapses on a server: every hit arrives from the same address with the same client string, so the whole day’s traffic merges into a single visitor with a remarkable number of actions.
A recognisable case of this is a report in which one visitor accounts for most of the events while the visit count stays flat. It is not a bot; it is a batch job without an identifier.
Custom Dimensions Are Only Numbers
Dimensions are passed as dimension1, dimension2 and so on, and the number is not a label – it refers to a slot that has to exist in the Matomo interface before anything can be written into it. A value sent to an unconfigured index is accepted and discarded.
The second thing to get right is the scope. A dimension of visit scope holds one value per visit and is overwritten by every later hit that carries it; a dimension of action scope holds one value per action. A tariff name in the wrong scope produces either a value that changes with the last page view or one that cannot be used as a segment across the visit.
What Only a Server May Send
A handful of parameters change data that the request is not normally allowed to touch: cip for the visitor’s real IP address, cdt for a timestamp other than now, and the geographic fields. All of them require token_auth, and Matomo ignores them without it rather than refusing the request.
The token belongs on a server and nowhere else. In a browser it is readable by every visitor, and it does not grant reading rights but writing rights: anyone holding it can write arbitrary hits into the statistics, and there is no way to tell those apart afterwards. Where a page needs to send hits itself, it sends them without those parameters.
One limitation applies even with a token: a custom timestamp more than about a day in the past is rejected. Backfilling a week of history is therefore not a matter of setting cdt; it needs the log importer or a direct import.
Why a Hit Disappears Without a Word
| Cause | What is seen |
|---|---|
Missing rec=1 |
Nothing at all, and the request looks completely normal. |
Wrong idsite |
The hits land in another site, usually the one with id 1. |
_id in the wrong format |
Every hit merges into one visitor, and the visit count stops rising. |
cdt without a token |
The hit is recorded, with the arrival time rather than the intended one. |
| Unconfigured dimension | The hit is recorded, the dimension is not. |
The check that ends most of these in a minute is the visits log with the real-time view open while the request is sent. A hit that appears there is recorded; one that does not was discarded, and the list above says where to look. The response never will.