API Reference
The public collection endpoints the tracker uses. You can call them directly from any language.
Base URL & CORS
The production API lives at https://app.produl.tech. The current tracker posts to /r, /b, and /f. The older /api/collect, /api/ping, and /api/vitals paths remain available for compatibility. All three endpoints accept cross-origin requests — Access-Control-Allow-Origin: * is returned on every response.
All request bodies are application/json. All responses are 204 No Content on success. No response body is returned, even for successful writes.
Authentication
The public collection endpoints are unauthenticated and identified by public site API key — the tracker POSTs from the browser without any secret. The site API key (field k) is what attributes the event to your site.
For server-side tracking, include an X-Produl-Server-Key header with a secret key generated from your dashboard. Server-authenticated requests get a higher rate limit. See Server-Side Tracking.
Rate limits
| Request type | Limit |
|---|---|
| Client (browser) requests | 200 / minute / IP |
| Server-authenticated requests | 2,000 / minute / IP |
Requests over the limit return 429 Too Many Requests.
POST /r
/rSend a pageview or custom event. Alias of /api/collect.
Body
| Field | Type | Required | Description |
|---|---|---|---|
k | string | yes | Site ID |
t | "pv" | "ev" | yes | Type: pageview or event |
u | string | yes | Full URL of the page |
p | string | yes | Path, including query string when present |
vi | string | yes | Visitor ID |
sk | string | yes | Session key |
ti | string | no | Page title |
r | string | no | Referrer URL |
d | number | no | Duration on previous page (ms) |
sd | number | no | Scroll depth (0–100) |
sw | number | no | Screen width (px) |
sh | number | no | Screen height (px) |
l | string | no | Browser language |
n | string | if t=ev | Event name |
pr | object | no | Event properties (JSON) |
Pageview example
{
"k": "site_abc123",
"t": "pv",
"u": "https://example.com/pricing?utm_source=newsletter",
"p": "/pricing?utm_source=newsletter",
"vi": "v8abcd1lmno2",
"sk": "sess_xyz789",
"ti": "Pricing — Example",
"r": "https://google.com/",
"sw": 1920,
"sh": 1080,
"l": "en-US"
}Event example
{
"k": "site_abc123",
"t": "ev",
"u": "https://example.com/pricing?utm_source=newsletter",
"p": "/pricing?utm_source=newsletter",
"vi": "v8abcd1lmno2",
"sk": "sess_xyz789",
"n": "cta_click",
"pr": { "location": "hero", "variant": "blue" }
}Response codes
| Status | Meaning |
|---|---|
204 No Content | Accepted. (Also returned silently for invalid payloads, bot UAs, and over-limit sites.) |
429 Too Many Requests | Rate limit exceeded. |
Why 204 on invalid payloads?
POST /b
/bKeep the active-visitor counter fresh and backfill time-on-page + scroll depth. Alias of /api/ping.
Body
| Field | Type | Required | Description |
|---|---|---|---|
k | string | yes | Site ID |
sk | string | yes | Session key |
vi | string | yes | Visitor ID |
p | string | yes | Current path |
d | number | no | Page duration (ms), sent on visibility change |
sd | number | no | Scroll depth (0–100) |
Behavior
The tracker sends /b on a 30-second heartbeat and again on visibilitychange. This keeps the live-visitor counter fresh — any visitor whose last ping is older than 3 minutes is treated as inactive.
POST /f
/fSubmit Core Web Vitals measurements captured in the browser. Alias of /api/vitals.
Body
| Field | Type | Required | Description |
|---|---|---|---|
k | string | yes | Site ID |
sk | string | yes | Session key |
vi | string | yes | Visitor ID |
p | string | yes | Path |
lcp | number | no | Largest Contentful Paint (ms) |
fcp | number | no | First Contentful Paint (ms) |
cls | number | no | Cumulative Layout Shift |
inp | number | no | Interaction to Next Paint (ms) |
ttfb | number | no | Time to First Byte (ms) |
fid | number | no | First Input Delay (ms) — deprecated in favor of INP |
Example
{
"k": "site_abc123",
"sk": "sess_xyz789",
"vi": "v8abcd1lmno2",
"p": "/pricing",
"lcp": 2350,
"fcp": 1200,
"cls": 0.07,
"inp": 180,
"ttfb": 420
}Errors
With the exception of rate-limit violations, these endpoints return 204regardless of the outcome — this keeps third-party analytics from interfering with your site's normal operation. To debug an event that doesn't arrive:
- Check the browser network tab — the request should show as a
204 - Verify your site API key matches a site in the dashboard
- For script installs, confirm
window.ma.track('debug_event')creates a successful/rrequest - Check FAQ & Troubleshooting for common issues (ad blockers, CSP)