Complete reference for all Zenovay REST API endpoints. For getting started, see API Overview.
A machine-readable OpenAPI spec is published at https://api.zenovay.com/api/external/v1/openapi.json if you'd rather generate a client.
External API
The External API uses API key authentication and is a paid feature — it's available to Pro, Scale, and Enterprise plans. Free-tier keys are rejected with a 403 API_PAID_PLAN_REQUIRED.
Base URL
https://api.zenovay.com/api/external/v1
Authentication
Generate a key under Account → Security & access (or open app.zenovay.com/api-keys directly). Keys start with zv_. Include your key in every request using either header:
X-API-Key: zv_YOUR_API_KEY
# or
Authorization: Bearer zv_YOUR_API_KEY
Response envelope
Every External API response is wrapped in a standard envelope. Successful responses look like this:
{
"success": true,
"data": { },
"timestamp": "2026-06-13T00:00:00.000Z"
}
The examples below show the contents of the data object. Errors use a separate shape — see Error Responses.
Analytics Endpoints
Get Analytics Overview
GET /analytics/{websiteId}
Returns an aggregated analytics summary for a website.
Query parameters:
| Parameter | Type | Description |
|---|---|---|
| range | string | Time range: 24h, 7d, 30d, 90d, 1y (default: 7d) |
data:
{
"website": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"domain": "example.com",
"name": "My Website"
},
"time_range": "7d",
"date_range": {
"start": "2026-06-06",
"end": "2026-06-13"
},
"summary": {
"total_visitors": 1247,
"total_page_views": 3891,
"unique_visitors": 982
},
"daily_stats": []
}
daily_stats contains the raw analytics_daily rows for the range. For a Plausible-style query API (visitors, pageviews, bounce_rate, visit_duration with filters and breakdowns) see the Stats API.
Get Visitors
GET /analytics/{websiteId}/visitors
Returns individual visitor records for a website.
Query parameters:
| Parameter | Type | Description |
|---|---|---|
| range | string | Time range (default: 7d) |
| limit | integer | Results per page (default: 100, max: 1000) |
| offset | integer | Number of records to skip (default: 0) |
Get Pages
GET /analytics/{websiteId}/pages
Returns top pages for a website. Accepts range and limit (default 20, max 100).
Get Countries
GET /analytics/{websiteId}/countries
Returns a geographic breakdown of visitors by country. Accepts range and limit (default 20, max 100).
Get Technology
GET /analytics/{websiteId}/technology
Returns the technology breakdown — devices, browsers, and operating_systems, each with counts and percentages. Accepts range.
Websites Endpoints
List Websites
GET /websites
Returns all websites accessible to your API key.
data:
{
"websites": [
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"domain": "example.com",
"name": "My Website",
"tracking_code": "abc123def456",
"is_active": true,
"created_at": "2026-01-01T00:00:00Z"
}
],
"total": 1
}
Get Website Details
GET /websites/{websiteId}
Returns details for a specific website under data.website.
Heatmap Endpoints
Get Heatmap Pages
GET /heatmaps/{websiteId}/pages
Returns pages that have heatmap data collected. Accepts limit (default 20, max 100).
Session Replay Endpoints
Get Replay Sessions
GET /replays/{websiteId}/sessions
Returns session replay recordings for a website. Accepts limit (default 50, max 200) and offset.
Error Tracking Endpoints
Get Error Groups
GET /errors/{websiteId}/groups
Returns grouped error data for a website. Accepts limit (default 50, max 200) and an optional status filter (open, resolved, ignored).
AI & Advanced Endpoints (Pro+)
These endpoints require a Pro plan or higher and gate on the owning website's team plan.
| Endpoint | Description |
|---|---|
GET /insights/{websiteId} | AI-generated insights for the site |
GET /anomalies/{websiteId} | Detected traffic/behaviour anomalies |
GET /retention/{websiteId} | Retention cohort analysis (granularity, periods) |
GET /revenue/{websiteId}/ltv | Lifetime-value cohort analysis |
Natural Language Query (Scale+)
POST /query/{websiteId}
Runs a natural-language analytics query. Requires a Scale plan or higher. Send a JSON body with a query string (max 500 characters); each call consumes 3 query credits.
Stats API
Plausible-compatible read-only query endpoints (Pro+). These let you query metrics, time series, and breakdowns with filters. See the dedicated Stats API guide for the full parameter reference.
| Endpoint | Description |
|---|---|
GET /stats/aggregate | Single-number metrics over a period |
GET /stats/timeseries | Time-bucketed series (interval=day/hour/month) |
GET /stats/breakdown | Group-by metrics (top pages, top countries, etc.) |
All three take site_id plus period, metrics, and optional filters. Supported metrics are visitors, pageviews, bounce_rate, visit_duration, and events.
Usage Endpoint
Check API Usage
GET /usage
Returns your current API usage and limits.
data:
{
"api_key": {
"id": "key_123",
"name": "Personal API key",
"permission": "full_access",
"website_id": null,
"auth_kind": "user"
},
"usage": {
"monthly_requests": 4521,
"monthly_limit": 10000,
"monthly_remaining": 5479,
"monthly_reset_at": "2026-07-01T00:00:00.000Z",
"total_requests": 18204,
"scope": "user"
},
"rate_limits": {
"requests_per_minute": 30
},
"subscription": {
"tier": "Pro"
}
}
Tracking Endpoints (Public)
These endpoints do not require API key authentication. They use the website tracking code and are open on every plan (rate-limited only).
Track Pageview or Event
POST /e/{trackingCode}
Sends a pageview or custom event. This is what the tracking script calls automatically. For server-side tracking, send the same payload format with appropriate headers. Heartbeat events can also be sent as the heartbeat event type to this endpoint.
Live Visitor Count
GET /e/live/{trackingCode}
Returns the current number of live visitors on the site, with a short list of active sessions.
Heartbeat
POST /e/heartbeat/{trackingCode}
Sends a heartbeat to keep a visitor session alive. Requires a session_id in the JSON body.
Error Responses
Error Format
Errors are returned with success: false and an error object:
{
"success": false,
"error": {
"message": "site_id parameter is required",
"code": "MISSING_SITE_ID",
"timestamp": "2026-06-13T00:00:00.000Z"
}
}
Common Error Codes
| Code | HTTP Status | Description |
|---|---|---|
| UNAUTHORIZED | 401 | API key invalid or missing |
| API_PAID_PLAN_REQUIRED | 403 | API access requires a Pro plan or higher |
| FORBIDDEN | 403 | Key lacks access to the requested site, or plan tier too low |
| NOT_FOUND | 404 | Resource not found |
| VALIDATION_ERROR | 400 | Bad or missing parameter |
| MISSING_SITE_ID | 400 | site_id query parameter is required (Stats API) |
| RATE_LIMIT_EXCEEDED | 429 | Per-minute rate limit exceeded |
| MONTHLY_LIMIT_EXCEEDED | 429 | Monthly API request limit reached |
| INTERNAL_ERROR | 500 | Internal error |
Rate Limits
External API rate limits are per API key. (The Free row is shown for completeness, but Free-tier keys cannot call the External API — it's a paid feature.)
| Plan | Requests/Minute | Monthly Limit |
|---|---|---|
| Pro | 30 | 10,000 |
| Scale | 60 | 100,000 |
| Enterprise | 120 | 1,000,000 |
Responses include usage and rate-limit headers:
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 27
X-Usage-Monthly: 4521
X-Usage-Limit: 10000
X-Usage-Reset: 2026-07-01T00:00:00.000Z
When a per-minute limit is hit, the response also includes a Retry-After header (seconds to wait).