Send cookieless analytics events from your servers and read your sites' statistics as JSON.
Pointing an AI agent at this API?
Hand it the LLM-ready Markdown version — self-contained instructions an agent can follow with just a URL and an API key.
All endpoints live under `https://naxyyp4k.vibecode.cloud/api/v1`. `https://naxyyp4k.vibecode.cloud` is the origin you were given (scheme + host, e.g. `https://example.com`). Do not add a trailing slash.
Endpoints marked "Bearer token" require a personal API key sent as a Bearer token: `Authorization: Bearer sk_your_key_here`. Keys always start with `sk_`. A missing or invalid key returns `401 { "error": "Invalid or missing API key" }`. Create keys in the app under Settings → API Keys. `POST /api/v1/collect` takes a site secret (`ssk_…`) instead, and the browser ingest `POST /api/event` is open.
Reading statistics (`/api/v1/sites…`) uses your personal API key (`sk_…`, Settings → API Keys). Sending events from a server (`POST /api/v1/collect`) uses the SITE secret (`ssk_…`) shown when the site is created (Site → Settings → Install → Server; it can be rotated there). A site secret can only write events for its one site — it can read nothing — so it is safe to deploy on a web server.
No cookies, no client storage. A visitor is identified by a hash of (daily-rotating salt, site, IP, User-Agent); the salt is deleted after its UTC day and the IP is never stored. Visits (sessions) end after 30 minutes of inactivity. Send the end-user's real IP and User-Agent with server events so visitors and sessions are counted correctly.
Stats endpoints take `period` = `today` | `24h` | `7d` | `30d` (default) | `90d` | `12mo`, evaluated in the site's time zone, and optional filters `page`, `source`, `country` (ISO-3166 alpha-2), `browser`, `os`, `device` (`desktop`|`mobile`|`tablet`), `campaign` (utm_campaign) and `event` (keeps whole visits that fired that event — a goal filter). Use the value `(none)` to select rows where a dimension is unknown.
Responses are JSON unless noted (file download returns raw bytes). Request bodies are JSON (`Content-Type: application/json`) except file upload, which is `multipart/form-data`.
Requests are rate limited per API key. When you exceed a limit you get `429` (or `403` if the limit is configured to block) with an `error` message and, when applicable, a `Retry-After` header (seconds). Back off and retry.
Errors are JSON with an `error` string and a matching HTTP status (`400` bad input, `401` unauthenticated, `403` forbidden, `404` not found, `413` payload too large, `429` rate limited, `500` server error).
Your base URL is https://naxyyp4k.vibecode.cloud. Create API keys under Profile → API Keys.
/api/v1/healthConfirms the API is up and your key is valid. Handy as a first call to verify credentials and connectivity.
Request
curl https://naxyyp4k.vibecode.cloud/api/v1/health \
-H "Authorization: Bearer sk_your_key_here"Response
{
"status": "healthy",
"timestamp": "2026-07-19T12:00:00.000Z",
"uptime": 1234.56,
"version": "1.0.0",
"apiKey": "My key",
"userId": "usr_...",
"message": "API is running successfully"
}/api/v1/statsReturns the calling user together with API-usage counters (requests today / this week / this month, error rate, API-key count).
Request
curl https://naxyyp4k.vibecode.cloud/api/v1/stats \
-H "Authorization: Bearer sk_your_key_here"Response
{
"user": { "id": "usr_...", "email": "you@example.com", "name": "You", "role": "user", "createdAt": "..." },
"apiStats": {
"totalApiKeys": 2,
"requestsToday": 14,
"requestsThisWeek": 98,
"requestsThisMonth": 412,
"errorRate": "1.20%",
"errorCount": 5
},
"meta": { "timestamp": "...", "apiKey": "My key" }
}/api/v1/usersLists users. A regular key returns only its own user record; an admin key returns all users with pagination.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| limit | query | integer | no | Page size, 1–100 (default 10). Admin only; ignored for non-admins. |
| offset | query | integer | no | Rows to skip (default 0). Admin only. |
Request
curl "https://naxyyp4k.vibecode.cloud/api/v1/users?limit=20&offset=0" \
-H "Authorization: Bearer sk_your_key_here"Response
{
"users": [
{ "id": "usr_...", "email": "you@example.com", "name": "You", "role": "user", "emailVerified": null, "createdAt": "..." }
],
"meta": { "limit": 20, "offset": 0, "total": 1, "apiKey": "My key" }
}/api/v1/usersAdmin-only endpoint scaffold for creating a user. Ships as a stub in this starter — it validates input and echoes it back rather than persisting. Fill in real creation logic before relying on it.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| body | string | yes | New user email. | |
| name | body | string | yes | New user display name. |
| role | body | string | no | 'user' (default) or 'admin'. |
Request
curl -X POST https://naxyyp4k.vibecode.cloud/api/v1/users \
-H "Authorization: Bearer sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{"email":"new@example.com","name":"New User","role":"user"}'Response
{
"message": "User creation endpoint - implementation needed",
"requestedData": { "email": "new@example.com", "name": "New User", "role": "user" },
"apiKey": "My key"
}/api/v1/filesUploads a file and stores its raw bytes. Use this instead of a form/Server Action for any real upload (Server Actions cap the body at ~1MB; this endpoint does not). Send `multipart/form-data` with a single `file` field.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| file | form | file | yes | The file to upload (multipart field name must be "file"). |
Request
curl -X POST https://naxyyp4k.vibecode.cloud/api/v1/files \
-H "Authorization: Bearer sk_your_key_here" \
-F "file=@./photo.png"Response
{
"id": "fil_...",
"filename": "photo.png",
"url": "/api/v1/files/fil_..."
}/api/v1/files/:idStreams the raw file bytes with the stored Content-Type. Because it is Bearer-gated you cannot put it directly in an `<img src>`; fetch it with the token and build an object URL client-side.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| id | path | string | yes | File id returned by the upload endpoint. |
Request
curl https://naxyyp4k.vibecode.cloud/api/v1/files/fil_your_file_id \
-H "Authorization: Bearer sk_your_key_here" \
--output downloaded-fileResponse
Raw binary body with the stored `Content-Type` and `Content-Disposition: inline; filename="..."`. Returns `404 { "error": "File not found" }` if unknown./api/v1/files/:idDeletes a file owned by the calling key.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| id | path | string | yes | File id to delete. |
Request
curl -X DELETE https://naxyyp4k.vibecode.cloud/api/v1/files/fil_your_file_id \
-H "Authorization: Bearer sk_your_key_here"Response
{ "deleted": true } // { "deleted": false } with status 404 if not found / not owned/api/v1/collectRecords pageviews and custom events server-side — for server-rendered sites, backends, apps or anywhere JavaScript does not run. Send ONE event object or an ARRAY of up to 100. An event named `pageview` (the default when `name` is omitted) counts as a pageview; any other name is a custom event/goal. Pass the end-user's IP and User-Agent so visitors and visits are counted (they are hashed with a daily salt, never stored).
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| name | body | string | no | 'pageview' (default) or a custom event name, ≤120 chars. |
| url | body | string | yes | Absolute http(s) URL of the page, or a path starting with `/` (the site domain is assumed). UTM params in it are recorded. |
| ip | body | string | no | The visitor's IP (hashed, never stored). Strongly recommended. |
| userAgent | body | string | no | The visitor's User-Agent (browser/OS/device + visitor hash). |
| referrer | body | string | no | Referer header of the original request (query string is dropped). |
| country | body | string | no | ISO-3166 alpha-2 country code if you know it (e.g. from CF-IPCountry). |
| visitor | body | string | no | Your own stable, already-anonymised visitor key — used INSTEAD of ip+userAgent for identity. |
| props | body | object | no | Flat map of custom properties (string/number/boolean, ≤30 keys, values ≤256 chars). |
| timestamp | body | string|number | no | ISO-8601 or epoch-ms; defaults to now. May be up to 7 days in the past, never in the future. |
Request
curl -X POST https://naxyyp4k.vibecode.cloud/api/v1/collect \
-H "Authorization: Bearer ssk_your_site_secret" \
-H "Content-Type: application/json" \
-d '[{"name":"pageview","url":"https://example.com/pricing","ip":"203.0.113.7","userAgent":"Mozilla/5.0 ...","referrer":"https://www.google.com/"},
{"name":"Purchase","url":"/checkout","ip":"203.0.113.7","userAgent":"Mozilla/5.0 ...","props":{"plan":"pro"}}]'Response
{
"accepted": 2,
"results": [
{ "index": 0, "stored": true },
{ "index": 1, "stored": true }
]
}/api/eventThe endpoint the `/a.js` snippet beacons to. Public and CORS-open; bodies are `text/plain` JSON so no preflight is needed. You normally never call it directly — embed `<script defer data-site="st_…" src="{BASE_URL}/a.js"></script>` and call `sitebeacon('EventName', { props: {…} })` for custom events. Hits whose page hostname is not the site's domain (or a subdomain) are ignored.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| s | body | string | yes | Public site id (`st_…`). |
| k | body | string | yes | 'pageview' | 'event' | 'engage'. |
| u | body | string | yes | Page URL (pageview/event). |
| n | body | string | no | Event name (k=event). |
| r | body | string | no | document.referrer. |
| tz | body | string | no | IANA time zone — used to derive the country when no CDN country header exists. |
| pv | body | string | no | Random per-pageview id; lets later `engage` pings add visible time to it. |
| e | body | integer | no | Engaged (visible) milliseconds on the page (k=engage). |
| p | body | object | no | Custom event properties (k=event). |
Request
curl -X POST https://naxyyp4k.vibecode.cloud/api/event \
-H "Content-Type: text/plain" \
-H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/128 Safari/537.36" \
-d '{"s":"st_your_site_id","k":"pageview","u":"https://example.com/","r":"https://duckduckgo.com/","tz":"Europe/Amsterdam"}'Response
`202 Accepted` with an empty body (also for ignored hits). `400`/`404`/`413`/`429` carry `{ "error": "..." }`./api/v1/sitesEvery site owned by the key owner (in the key's tenant when multi-tenant mode is on).
Request
curl https://naxyyp4k.vibecode.cloud/api/v1/sites \
-H "Authorization: Bearer sk_your_key_here"Response
{
"sites": [
{
"id": "st_2O8iQhfAz3yg",
"name": "Demo store",
"domain": "example.com",
"timezone": "Europe/Amsterdam",
"public": false,
"firstEventAt": "2026-07-02T22:07:22.000Z",
"createdAt": "2026-07-02T09:39:44.000Z"
}
]
}/api/v1/sites/:id/statsHeadline metrics for the period (and the previous period of equal length, for comparison) plus a visitors/visits/pageviews series bucketed by hour (today, 24h), day (7d–90d) or month (12mo) in the site's time zone.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| id | path | string | yes | Site id (`st_…`). |
| period | query | string | no | today | 24h | 7d | 30d (default) | 90d | 12mo. |
| page, source, country, browser, os, device, campaign, event | query | string | no | Optional filters (see Essentials → Periods and filters). |
Request
curl "https://naxyyp4k.vibecode.cloud/api/v1/sites/st_your_site_id/stats?period=7d&country=NL" \
-H "Authorization: Bearer sk_your_key_here"Response
{
"site": { "id": "st_…", "name": "Demo store", "domain": "example.com", "timezone": "Europe/Amsterdam" },
"period": "7d",
"from": "2026-09-23T22:00:00.000Z",
"to": "2026-09-30T10:00:00.000Z",
"granularity": "day",
"filters": { "country": "NL" },
"summary": { "visitors": 1301, "visits": 1452, "pageviews": 3081, "events": 320, "viewsPerVisit": 2.12, "bounceRate": 40.4, "avgDurationSec": 141 },
"previous": { "visitors": 1176, "visits": 1290, "pageviews": 2732, "events": 299, "viewsPerVisit": 2.12, "bounceRate": 38.1, "avgDurationSec": 138 },
"timeseries": [ { "bucket": "2026-09-24", "visitors": 218, "visits": 240, "pageviews": 515 } ]
}/api/v1/sites/:id/breakdownRanks the values of one dimension by unique visitors for the period, honouring the same filters as /stats.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| id | path | string | yes | Site id (`st_…`). |
| dimension | query | string | yes | page | entry | exit | source | referrer | campaign | medium | country | browser | os | device | event. |
| period | query | string | no | As for /stats (default 30d). |
| limit | query | integer | no | Rows to return, 1–500 (default 10). |
| page, source, country, browser, os, device, campaign, event | query | string | no | Optional filters. |
Request
curl "https://naxyyp4k.vibecode.cloud/api/v1/sites/st_your_site_id/breakdown?dimension=source&period=30d&limit=5" \
-H "Authorization: Bearer sk_your_key_here"Response
{
"dimension": "source",
"period": "30d",
"filters": {},
"rows": [
{ "value": "Google", "visitors": 1972, "pageviews": 4679, "count": 5161 },
{ "value": "Direct / None", "visitors": 1715, "pageviews": 4135, "count": 4556 }
]
}/api/v1/sites/:id/realtimeUnique visitors and pageviews in the last 5 minutes, the top 5 active pages, and pageviews per minute for the last 30 minutes (index 0 = 29 minutes ago).
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| id | path | string | yes | Site id (`st_…`). |
Request
curl https://naxyyp4k.vibecode.cloud/api/v1/sites/st_your_site_id/realtime \
-H "Authorization: Bearer sk_your_key_here"Response
{
"visitors": 3,
"pageviews": 4,
"pages": [ { "path": "/pricing", "visitors": 2 } ],
"perMinute": [ { "minute": 29, "pageviews": 0 }, "…", { "minute": 0, "pageviews": 2 } ]
}