Skip to main content
Browse documentation

API & automation

Events & ingest reference

The exact contracts for every public door traffic data comes in through — field by field, with the responses you actually get back. This is the reference; the walkthrough version lives at Visitor tag and crawler tracking.

There is deliberately no generic named-event endpoint yet — events we cannot store we do not accept. Ranksify records exactly two kinds of traffic event, AI crawler hits and AI-referred human visits, and an endpoint that took arbitrary named events would return 200 while dropping them. When there is somewhere to put them, there will be an endpoint for them.

GET /api/beacon.js

Serves the embeddable snippet. Public, cacheable for an hour, CORS-open. Drop the tag into your page and it posts one pageview per load to /api/beacon, deriving that endpoint from its own src — so the snippet works unmodified on whatever host serves it.

<script src="https://app.ranksify.ai/api/beacon.js"
        data-project="YOUR_PROJECT_ID"
        data-plugins="+debug"
        defer></script>
AttributeRequiredMeaning
data-projectYesYour brand UUID. Without it the snippet does nothing at all — it does not guess, and it does not fall back to a default brand.
data-pluginsNoComma-separated plugin list. +name or a bare name adds a plugin, -name removes one, and unknown names are ignored so a future plugin name in your tag never breaks the current snippet.

Shipped plugins

  • privacyon by default. Visitors sending Global Privacy Control or DNT: 1 are not counted: the snippet sends nothing for them at all. Opt out with data-plugins="-privacy" if you need every visitor counted.
  • debug — opt-in. Logs the payload and the server’s verdict to the console, so “why is my dashboard empty” is answerable from the browser you are already in.

POST /api/beacon

The endpoint the snippet posts to. JSON body, no authentication — a brand UUID is not a secret, which is why the origin check below exists instead.

FieldTypeNotes
projectIduuidRequired.
urlstringRequired. 1–2000 characters.
referrerstring | nullOptional. Up to 2000 characters. This is what identifies an AI referral.
sessionIdstring | nullOptional. Up to 64 characters. Your own opaque id; we never set a cookie.
{"data":{"tracked":true}}
  • Only AI-referred hits are stored. A visit with no AI referrer is a successful request that stores nothing — this is LLM analytics, not general analytics.
  • The request Origin must match the brand’s domain. A mismatch drops browser cross-origin posts and catches a tag installed on the wrong site. A non-browser client can set Origin itself, so this is defence in depth, not authentication.
  • A dropped hit answers {"data":{"tracked":false}} — a truthful 200, not an error. It reports the same negative whether the brand exists or not, so the endpoint cannot be used to enumerate brand ids.
  • Over the rate limit you get 429 with a retry-after header.
tracked:false is the response you should expect while testing from localhost: your browser’s origin is not your brand’s domain. It means the gate works, not that the beacon is broken.

POST /api/ingest/{projectId}

The machine-to-machine door for server logs. Crawlers do not run JavaScript, so the beacon cannot see them — this is how AI crawler hits get in. Authenticate with an API key carrying the ingest scope.

curl -X POST "https://app.ranksify.ai/api/ingest/PROJECT_ID?source=rest" \
  -H "Authorization: Bearer rk_…" \
  -H "content-type: text/plain" \
  --data-binary @access.log
  • ?source= accepts rest or cf-worker. Anything else is a 400, not a silent fallback — ingest provenance has to be honest rather than guessable by whoever holds the token.
  • The body is raw log text, one entry per line: Caddy JSON lines, or the Apache/Nginx combined format. Both grammars are accepted in the same upload.
  • The request body is limited to 2 MiB and each log line to 16,384 characters. The projectId in the endpoint must match the brand the ingest key is bound to.
  • Re-uploading identical lines is idempotent. Each raw line is deduped server-side on a hash of itself, so overlapping windows and retried uploads do not double-count.
{"data":{"linesRead":1204,"crawlerHits":37,"aiVisits":4,"dropped":1163}}

A large dropped count is normal and not a failure: every line that is neither an AI crawler nor an AI-referred visit is discarded at the door.

GET /api/v1/bots.json

The public, versioned list of AI crawlers we classify against — the same list this endpoint’s ingest path matches your logs with. Public, unauthenticated, cached for an hour. Full field reference, versioning policy and a live copy of the table are on the Bot registry page.