Walker Formulation Academy for developers API reference Error catalog OpenAPI
Sections

#JS and PHP Tracker by Token

Status: Available.

Purpose: the minimal way to count visits on any site without a module: one <script> tag, or a server-side send from PHP. The path, source, and UTM are sent; only the server variant sees bots.

#Contents

  1. When to use it
  2. Access
  3. JS tag
  4. Server variant (PHP)
  5. The POST /api/v1/track/visit request
  6. Responses
  7. Limits
  8. What is recorded
  9. What the tracker does not do
  10. Verification
  11. Open questions

#When to use it

You needPath
A visit counter on an arbitrary siteJS tag
To see visits by bots and AI crawlers on a PHP sitePHP variant
Chat, behavior tracking, consentLoader or a module
Events, forms, an SPA, a mobile appEvents API, Planned

#Access

Token32 hex characters; in the token table there are two kinds: tracker (regular) and connector (created by a module binding)
Issuancethe dashboard, seo.manage permission
Paidno
Personal dataIP and User-Agent hashes; the values are not stored (see Overview)
Secrecythe token is public: it sits in the tag on the page. Limits and a daily quota protect against abuse

#JS tag

<script async src="https://formula-cream.pro/api/v1/track/js?t=0123456789abcdef0123456789abcdef"></script>

Replace the domain with your cell's address: RU https://formula-cream.pro, EN https://wfacademy.org, ID https://wfacademy.id. The token is issued by the dashboard.

#Parameters of GET /api/v1/track/js

ParameterTypeRequiredConstraintsExample
tstringyes32 hex characters0123456789abcdef0123456789abcdef

#Response

ConditionCodeBody
The token has an invalid form400/* bad token */
Otherwise200the script, Cache-Control: public, max-age=3600, Access-Control-Allow-Origin: *

Whether the token exists in the database is not checked when the script is served: the hit receiver checks it.

#What the script sends

One request per page load through navigator.sendBeacon (a text/plain Blob), with a fallback of fetch in no-cors mode with keepalive.

{
  "t": "0123456789abcdef0123456789abcdef",
  "kind": "js",
  "path": "/blog/emulsions",
  "host": "example.ru",
  "ref": "https://www.google.com/",
  "utm": { "source": "newsletter", "medium": "email", "campaign": "october" }
}

#Server variant (PHP)

It is inserted into Bitrix init.php (or the start of any PHP application). The source is served by GET /api/v1/track/php?t=<token>.

What the snippet does: after the request is processed, it sends a hit with the parameters without delaying the page, using a non-blocking curl_multi with a 150 ms connect timeout and a 200 ms total timeout.

The hit is not sent if:

To disable it temporarily, add define('SAPPORT_TRACKER_DISABLED', true); above the inserted block.

The visitor's IP is taken from the first element of X-Forwarded-For (if the header is present), otherwise from REMOTE_ADDR. If the site is reachable directly, a visitor can forge the header: put the server behind a trusted proxy.

#The POST /api/v1/track/visit request

The receiver is shared by the JS tag, the PHP code, and the modules' signed beacon. The signature for modules is described in the connector protocol; no signature is needed for a token.

Headers: Content-Type: application/json (text/plain is also accepted, as sent by sendBeacon). CORS: *.

#Request body

FieldTypeRequiredConstraintsExample
tstringyestracker token, 32 hex0123456789abcdef0123456789abcdef
kindstringnojs (default) or server. server is trusted only if host is among the tenant's domains; otherwise the hit is treated as a JS hitserver
pathstringyespage path; the query is dropped, the result is ≤ 512 characters (PHP sends up to 600, with the query: UTM is taken from it)/blog/emulsions
hoststringyespage hostexample.ru
refstringnoreferrer, ≤ 600https://www.google.com/
utmobjectnosource, medium, campaign, each ≤ 100 characters{"source":"newsletter"}
uastringserver onlyUser-Agent, ≤ 1000Mozilla/5.0 …
ipstringserver onlyvisitor IP203.0.113.7
countrystringnotwo-letter code; for JS the country is taken from the cf-ipcountry headerRU
statusintegerno100–599, the HTTP code of the page response200

The body is at most 8192 bytes; anything larger gets a 204 response without recording.

curl -i -X POST 'https://formula-cream.pro/api/v1/track/visit' \
  -H 'Content-Type: application/json' \
  -d '{"t":"0123456789abcdef0123456789abcdef","kind":"js","path":"/blog/emulsions","host":"example.ru","ref":"","utm":{"source":"newsletter"}}'

#Responses

Successful receipt and most rejections look the same, 204 with no body, so that tokens cannot be guessed.

CodeWhen
204accepted; or the body is not JSON; or the token is invalid, unknown, or revoked; or the daily quota is exceeded; or the body is larger than 8192 bytes
403the loader key (connector) was used for kind: server; or the site is not bound, the counter is off, or the tenant has no access
429the rate limit per address or per token was exceeded
500database error (nothing was recorded)

Errors do not return application/problem+json: this is a separate counter receiver, not part of the public API (see API conventions for the server API).

#Limits

LimitValue
Per sender address (unsigned)1 request per second, burst up to 60
Per token10 requests per second, burst up to 100
Daily token quota200,000 hits per day (UTC); beyond the quota, 204 without recording
Body size8192 bytes
Module's signed beacon1,200 requests per minute per address

The quota and limits are counted in process memory: with several processes, the effective limit is softer than the nominal one.

Hits are retained for 180 days.

#What is recorded

Hit fieldSource
kindbot or human, by User-Agent
bot_familythe bot family from a closed list
path, hostfrom the request
referrer_hostthe referrer's host
ai_referrerthe AI assistant label by referrer host
utm_source, utm_medium, utm_campaignfrom utm or from the path's query
ua_hash, ip_hashsha256(salt | value) truncated to 32 hex
countryfrom cf-ipcountry or from the body of a trusted server hit
statusHTTP code
sample_uaa User-Agent sample, bots only, up to 300 characters
beaconjs or server

#Classification

LabelReferrer hosts
chatgptchatgpt.com, chat.openai.com
perplexityperplexity.ai
geminigemini.google.com, bard.google.com
copilotcopilot.microsoft.com, bing.com/chat
youyou.com
claudeclaude.ai
aliceya.ru/alice, alice.yandex.ru, yandex.ru/alice
duckassistduckduckgo.com/aichat
mistralmistral.ai
deepseekchat.deepseek.com

Plain ya.ru is not Alice: a visit from it is not labeled as AI.

#What the tracker does not do

#Verification

  1. Open a page; in the "Network" tab find the track/js?t=… request (200) and then track/visit.
  2. A 204 response does not mean the hit was recorded (see the response table); check the counter in the dashboard under "Promotion → Overview".
  3. For PHP: curl -A "Googlebot" https://your-site/ makes a bot visit appear under "AI assistants and bots".

#Open questions

  1. Technical. The receiver's code comments say "the response is always 204" and give limits of "~60/min per address and ~600/min per token"; the actual values differ (token buckets, 429/403/500 codes). This page describes the actual behavior; the code comments should be corrected.
  2. Technical. The quota and limits live in process memory: is a shared (Redis) quota needed for public guarantees?
  3. Technical. The "visitor" unit is impossible in the server beacon's anonymous mode: in the public API, visitors is null in this mode (Reading analytics).