Pular para o conteúdo principal
13 min de leitura

External API

The External API allows you to embed Zenovay analytics data on your customer-facing websites. Use your API key to fetch live visitor counts, analytics summaries, page statistics, and more.

Base URL

All External API requests should be made to:

https://api.zenovay.com/api/external/v1

Authentication

External API endpoints require an API key passed in the request header:

API Key HeaderBash
curl -X GET 'https://api.zenovay.com/api/external/v1/websites' \
-H 'X-API-Key: YOUR_API_KEY' \
-H 'Content-Type: application/json'

Generate your API key from the Zenovay Dashboard under Settings > API Keys. Each key can have specific permissions (read, write, admin).

Account Endpoints

Get Account Usage

Retrieve your account usage statistics and limits:

GET/api/external/v1/usage

Get account usage and limits

RequestBash
curl -X GET 'https://api.zenovay.com/api/external/v1/usage' \
-H 'X-API-Key: YOUR_API_KEY'
Response (200 OK)JSON
{
"usage": {
  "websites": 5,
  "pageviews_this_month": 125000,
  "events_this_month": 45000
},
"limits": {
  "websites": 10,
  "pageviews_per_month": 500000,
  "events_per_month": 100000
},
"plan": "pro",
"period": {
  "start": "2025-01-01T00:00:00Z",
  "end": "2025-01-31T23:59:59Z"
}
}

Website Endpoints

List Websites

Get all websites linked to your account:

GET/api/external/v1/websites

List all tracked websites

RequestBash
curl -X GET 'https://api.zenovay.com/api/external/v1/websites' \
-H 'X-API-Key: YOUR_API_KEY'
Response (200 OK)JSON
{
"websites": [
  {
    "id": "ws_abc123",
    "domain": "example.com",
    "name": "Example Website",
    "tracking_code": "ZV_XXXXXXXXXXX",
    "created_at": "2025-01-01T00:00:00Z",
    "live_visitors": 42
  },
  {
    "id": "ws_def456",
    "domain": "shop.example.com",
    "name": "Example Shop",
    "tracking_code": "ZV_YYYYYYYYYYY",
    "created_at": "2025-01-10T00:00:00Z",
    "live_visitors": 18
  }
],
"total": 2
}

Get Website Details

Retrieve details for a specific website:

GET/api/external/v1/websites/:websiteId

Get website details

RequestBash
curl -X GET 'https://api.zenovay.com/api/external/v1/websites/ws_abc123' \
-H 'X-API-Key: YOUR_API_KEY'
Response (200 OK)JSON
{
"website": {
  "id": "ws_abc123",
  "domain": "example.com",
  "name": "Example Website",
  "tracking_code": "ZV_XXXXXXXXXXX",
  "created_at": "2025-01-01T00:00:00Z",
  "settings": {
    "track_outbound_links": true,
    "track_downloads": true,
    "session_replay": true
  }
},
"stats": {
  "live_visitors": 42,
  "today_pageviews": 1234,
  "today_visitors": 567
}
}

Analytics Endpoints

Get Analytics Summary

Retrieve comprehensive analytics data for a website:

GET/api/external/v1/analytics/:websiteId

Get analytics overview

Query Parameters:

ParameterTypeRequiredDescription
timeRangestringNoTime range: today, 7d, 30d, 90d (default: 7d)
RequestBash
curl -X GET 'https://api.zenovay.com/api/external/v1/analytics/ws_abc123?timeRange=30d' \
-H 'X-API-Key: YOUR_API_KEY'
Response (200 OK)JSON
{
"summary": {
  "visitors": 12543,
  "pageviews": 45231,
  "sessions": 15420,
  "bounce_rate": 0.42,
  "avg_session_duration": 185,
  "pages_per_session": 2.9
},
"period": {
  "start": "2024-12-21T00:00:00Z",
  "end": "2025-01-20T23:59:59Z"
},
"live_visitors": 42
}

Get Visitors Data

Retrieve visitor information with optional filtering:

GET/api/external/v1/analytics/:websiteId/visitors

Get visitor data

Query Parameters:

ParameterTypeRequiredDescription
timeRangestringNoTime range: today, 7d, 30d, 90d
limitintegerNoMax results (default: 50, max: 100)
offsetintegerNoPagination offset
RequestBash
curl -X GET 'https://api.zenovay.com/api/external/v1/analytics/ws_abc123/visitors?timeRange=7d&limit=10' \
-H 'X-API-Key: YOUR_API_KEY'
Response (200 OK)JSON
{
"visitors": [
  {
    "visitor_id": "vis_abc123",
    "first_seen": "2025-01-15T10:00:00Z",
    "last_seen": "2025-01-20T14:30:00Z",
    "total_sessions": 8,
    "total_pageviews": 45,
    "value_score": 87,
    "country": "US",
    "device": "desktop"
  }
],
"total": 1234,
"limit": 10,
"offset": 0
}

Get Page Analytics

Retrieve page-level statistics:

GET/api/external/v1/analytics/:websiteId/pages

Get top pages

Query Parameters:

ParameterTypeRequiredDescription
timeRangestringNoTime range: today, 7d, 30d, 90d
limitintegerNoMax results (default: 20)
RequestBash
curl -X GET 'https://api.zenovay.com/api/external/v1/analytics/ws_abc123/pages?timeRange=7d&limit=10' \
-H 'X-API-Key: YOUR_API_KEY'
Response (200 OK)JSON
{
"pages": [
  {
    "path": "/",
    "title": "Home",
    "views": 5423,
    "unique_visitors": 3241,
    "avg_time_on_page": 125,
    "bounce_rate": 0.35
  },
  {
    "path": "/pricing",
    "title": "Pricing",
    "views": 2134,
    "unique_visitors": 1876,
    "avg_time_on_page": 210,
    "bounce_rate": 0.28
  }
]
}

Get Geographic Data

Retrieve visitor data by country:

GET/api/external/v1/analytics/:websiteId/countries

Get geographic breakdown

Query Parameters:

ParameterTypeRequiredDescription
timeRangestringNoTime range: today, 7d, 30d, 90d
RequestBash
curl -X GET 'https://api.zenovay.com/api/external/v1/analytics/ws_abc123/countries?timeRange=30d' \
-H 'X-API-Key: YOUR_API_KEY'
Response (200 OK)JSON
{
"countries": [
  {
    "code": "US",
    "name": "United States",
    "visitors": 4521,
    "percentage": 36.1
  },
  {
    "code": "GB",
    "name": "United Kingdom",
    "visitors": 1823,
    "percentage": 14.5
  },
  {
    "code": "DE",
    "name": "Germany",
    "visitors": 1456,
    "percentage": 11.6
  }
]
}

Get Technology Breakdown

Retrieve device, browser, and OS statistics:

GET/api/external/v1/analytics/:websiteId/technology

Get technology breakdown

Query Parameters:

ParameterTypeRequiredDescription
timeRangestringNoTime range: today, 7d, 30d, 90d
RequestBash
curl -X GET 'https://api.zenovay.com/api/external/v1/analytics/ws_abc123/technology?timeRange=7d' \
-H 'X-API-Key: YOUR_API_KEY'
Response (200 OK)JSON
{
"devices": [
  { "type": "desktop", "visitors": 7234, "percentage": 57.7 },
  { "type": "mobile", "visitors": 4521, "percentage": 36.1 },
  { "type": "tablet", "visitors": 788, "percentage": 6.3 }
],
"browsers": [
  { "name": "Chrome", "visitors": 6543, "percentage": 52.2 },
  { "name": "Safari", "visitors": 3421, "percentage": 27.3 },
  { "name": "Firefox", "visitors": 1234, "percentage": 9.8 }
],
"operating_systems": [
  { "name": "Windows", "visitors": 4521, "percentage": 36.1 },
  { "name": "macOS", "visitors": 3234, "percentage": 25.8 },
  { "name": "iOS", "visitors": 2876, "percentage": 22.9 }
]
}

Advanced Endpoints

Get Heatmap Pages

List pages with heatmap data:

GET/api/external/v1/heatmaps/:websiteId/pages

List heatmap pages

RequestBash
curl -X GET 'https://api.zenovay.com/api/external/v1/heatmaps/ws_abc123/pages' \
-H 'X-API-Key: YOUR_API_KEY'
Response (200 OK)JSON
{
"pages": [
  {
    "url": "/",
    "title": "Home",
    "total_clicks": 12543,
    "total_sessions": 4521,
    "last_updated": "2025-01-20T14:30:00Z"
  },
  {
    "url": "/pricing",
    "title": "Pricing",
    "total_clicks": 8765,
    "total_sessions": 2134,
    "last_updated": "2025-01-20T14:25:00Z"
  }
]
}

Get Session Replays

List recorded sessions:

GET/api/external/v1/replays/:websiteId/sessions

List session replays

RequestBash
curl -X GET 'https://api.zenovay.com/api/external/v1/replays/ws_abc123/sessions' \
-H 'X-API-Key: YOUR_API_KEY'
Response (200 OK)JSON
{
"sessions": [
  {
    "session_id": "sess_abc123",
    "visitor_id": "vis_xyz789",
    "started_at": "2025-01-20T14:00:00Z",
    "duration": 245,
    "pages_visited": 5,
    "country": "US",
    "device": "desktop"
  }
],
"total": 156
}

Get Error Groups

List JavaScript error groups:

GET/api/external/v1/errors/:websiteId/groups

List error groups

RequestBash
curl -X GET 'https://api.zenovay.com/api/external/v1/errors/ws_abc123/groups' \
-H 'X-API-Key: YOUR_API_KEY'
Response (200 OK)JSON
{
"groups": [
  {
    "fingerprint": "err_abc123",
    "message": "Cannot read property 'length' of undefined",
    "type": "TypeError",
    "occurrences": 45,
    "affected_users": 23,
    "first_seen": "2025-01-15T10:00:00Z",
    "last_seen": "2025-01-20T14:30:00Z"
  }
],
"total": 12
}

Stats API (Plausible parity, Pro+)

The Stats API exposes three Plausible-compatible endpoints for programmatic analytics queries. Use them to build custom dashboards, embed live metrics in BI tools, or migrate from Plausible with minimal code changes.

Plan required: Pro, Scale, or Enterprise. Free-tier API keys can read these docs but receive a 403 PLAN_REQUIRED error when calling the endpoints.

Endpoints

EndpointPurpose
GET /stats/aggregateSingle-number metrics over a period (totals, rates, durations).
GET /stats/timeseriesTime-bucketed series (one row per day, hour, or month).
GET /stats/breakdownGroup-by metrics (top pages, top countries, top browsers, …).

OpenAPI spec: /api/external/v1/openapi.json (live, public, no auth required for the spec itself).

Common parameters

ParamRequiredDescription
site_idyesWebsite UUID. Find it in your dashboard URL or via GET /websites.
periodyesday, 7d, 30d, month, 6mo, 12mo, or custom:YYYY-MM-DD,YYYY-MM-DD (max 366 days).
datenoISO 8601 anchor for the period (default = today).
metricsyesComma-separated. Allowed: visitors, pageviews, visit_duration, bounce_rate, events.
filtersnoPlausible-style: country==US;browser==Chrome;page!=/admin. See Filters below.

GET /stats/aggregate

Returns single-number values for each requested metric over a period.

GET/api/external/v1/stats/aggregate

Aggregate metrics over a period

Request — visitors and pageviews over last 7 daysBash
curl -G 'https://api.zenovay.com/api/external/v1/stats/aggregate' \
-H 'X-API-Key: YOUR_API_KEY' \
--data-urlencode 'site_id=00000000-0000-0000-0000-000000000001' \
--data-urlencode 'period=7d' \
--data-urlencode 'metrics=visitors,pageviews,bounce_rate'
Response (200 OK)JSON
{
"success": true,
"data": {
  "results": {
    "visitors":    { "value": 12453 },
    "pageviews":   { "value": 38219 },
    "bounce_rate": { "value": 42.1 }
  },
  "meta": {
    "period": "7d",
    "period_start": "2026-05-07T00:00:00.000Z",
    "period_end":   "2026-05-13T23:59:59.999Z",
    "filters_applied": []
  }
},
"timestamp": "2026-05-14T08:30:00.000Z"
}

GET /stats/timeseries

Returns one row per bucket over the period. Default interval=day; interval=hour requires period=day; interval=month requires period of 6mo, 12mo, month, or custom.

GET/api/external/v1/stats/timeseries

Time-bucketed metric series

Request — daily visitors over last 30 daysBash
curl -G 'https://api.zenovay.com/api/external/v1/stats/timeseries' \
-H 'X-API-Key: YOUR_API_KEY' \
--data-urlencode 'site_id=00000000-0000-0000-0000-000000000001' \
--data-urlencode 'period=30d' \
--data-urlencode 'metrics=visitors,pageviews' \
--data-urlencode 'interval=day'
Response (200 OK)JSON
{
"success": true,
"data": {
  "results": [
    { "date": "2026-04-14", "visitors": 1820, "pageviews": 5640 },
    { "date": "2026-04-15", "visitors": 1933, "pageviews": 5921 }
  ],
  "meta": {
    "period": "30d",
    "interval": "day",
    "period_start": "2026-04-14T00:00:00.000Z",
    "period_end":   "2026-05-13T23:59:59.999Z",
    "filters_applied": []
  }
},
"timestamp": "2026-05-14T08:30:00.000Z"
}

The response is dense — days with zero traffic still appear with visitors: 0. This keeps chart libraries happy without client-side gap-filling.

GET /stats/breakdown

Returns metrics grouped by a dimension, sorted by visitors descending.

GET/api/external/v1/stats/breakdown

Group-by metrics (top pages, top countries, …)

Request — top 10 pages by visitors over last 7 daysBash
curl -G 'https://api.zenovay.com/api/external/v1/stats/breakdown' \
-H 'X-API-Key: YOUR_API_KEY' \
--data-urlencode 'site_id=00000000-0000-0000-0000-000000000001' \
--data-urlencode 'period=7d' \
--data-urlencode 'property=event:page' \
--data-urlencode 'metrics=visitors,pageviews' \
--data-urlencode 'limit=10'
Response (200 OK)JSON
{
"success": true,
"data": {
  "results": [
    { "page": "/pricing",      "visitors": 4421, "pageviews": 4421 },
    { "page": "/blog/launch",  "visitors": 2018, "pageviews": 2018 }
  ],
  "meta": {
    "period": "7d",
    "property": "event:page",
    "pagination": { "page": 1, "limit": 10, "total": 47, "has_more": true }
  }
},
"timestamp": "2026-05-14T08:30:00.000Z"
}

Allowed property values: event:page, visit:country, visit:browser, visit:device, visit:os, visit:source.

Pagination: limit is 1–1000 (default 100); page is 1-indexed (default 1).

Filters

Plausible-style filter clauses, joined with ;:

  • country==US — equals
  • country==US,CA,GB — equals one of
  • browser!=Safari — not equals

Allowed keys: country, browser, device, os, source, utm_source, utm_medium, utm_campaign, page.

V1 limitation: When filters is provided, only the visitors metric is computed; other metrics return null with a meta.note flag. Full filter support across all metrics ships in V2.

JavaScript example

Fetch aggregate metrics with fetch()JavaScript
async function getAggregate(siteId, period = '7d', metrics = ['visitors', 'pageviews']) {
const url = new URL('https://api.zenovay.com/api/external/v1/stats/aggregate');
url.searchParams.set('site_id', siteId);
url.searchParams.set('period', period);
url.searchParams.set('metrics', metrics.join(','));

const res = await fetch(url, {
  headers: { 'X-API-Key': process.env.ZENOVAY_API_KEY }
});
if (!res.ok) {
  const err = await res.json();
  throw new Error(`${err.error.code}: ${err.error.message}`);
}
return res.json();
}

const data = await getAggregate('00000000-0000-0000-0000-000000000001', '7d');
console.log(`Visitors: ${data.data.results.visitors.value}`);

Error codes

The Stats API returns codes you can switch on for graceful UX:

CodeHTTPWhen
MISSING_SITE_ID400site_id query param is required.
MISSING_METRICS400metrics query param is required.
MISSING_PERIOD400period query param is required.
MISSING_PROPERTY400/stats/breakdown requires property.
INVALID_METRIC400One of the comma-separated metrics is not in the allowlist.
INVALID_PROPERTY400The breakdown property isn't allowed.
INVALID_PERIOD400The period isn't day/7d/30d/month/6mo/12mo/custom:....
INVALID_CUSTOM_PERIOD400Malformed custom: syntax or invalid date(s).
PERIOD_TOO_LONG400Custom period > 366 days.
INTERVAL_PERIOD_MISMATCH400e.g. interval=hour with period=7d.
UNKNOWN_FILTER_KEY400Filter key isn't in the allowlist.
EMPTY_FILTER_VALUE400Filter clause has no value.
FORBIDDEN403Free-tier key calling a Pro+ endpoint, or site_id out of API-key scope.
NOT_FOUND404Site doesn't exist or is hidden from this key.
(rate limit)429Per-tier rate limit exceeded. Retry-After header indicates wait time.

Migrating from Plausible

The parameter shape is intentionally Plausible-compatible:

PlausibleZenovayNotes
site_idsite_idPlausible uses a domain string; Zenovay uses a UUID. Map domain → site_id once via GET /websites.
period, dateperiod, dateSame allowlist + custom:YYYY-MM-DD,YYYY-MM-DD.
metricsmetricsPlausible's visitors, pageviews, bounce_rate, visit_duration all map 1:1. events is V1-approximated as pageviews.
propertypropertySame shape: event:page, visit:country, etc.
filters (v1 string)filtersSame key==value;key!=value shape. JSON v2 array filters land in Zenovay V2.

JavaScript Example

Embed analytics data on your website using JavaScript:

Fetch and Display AnalyticsJavaScript
// Fetch analytics data from your backend
async function fetchAnalytics() {
const response = await fetch('/api/analytics', {
  headers: {
    'X-API-Key': 'YOUR_API_KEY' // Use server-side proxy to protect your key
  }
});

const data = await response.json();

// Update your UI
document.getElementById('live-visitors').textContent = data.live_visitors;
document.getElementById('total-pageviews').textContent = data.summary.pageviews.toLocaleString();
document.getElementById('bounce-rate').textContent = (data.summary.bounce_rate * 100).toFixed(1) + '%';
}

// Refresh every 30 seconds
fetchAnalytics();
setInterval(fetchAnalytics, 30000);

Security Note: Never expose your API key in client-side code. Create a server-side proxy endpoint that calls the Zenovay API with your key, then have your frontend call your proxy.

Rate Limits

External API rate limits by plan:

PlanRequests/Minute (target)Monthly Limit (hard cap)
Free101,000
Pro3010,000
Scale60100,000
Enterprise1201,000,000

How rate-limiting is enforced. The per-minute number is a target, not a hard ceiling. We use Cloudflare's edge rate-limit, which is per data center with a small burst allowance — a sustained client may transiently see 1.5–3× the headline number before being throttled, especially when requests fan out across regions. The hard cap is the monthly quota, enforced atomically against your account regardless of which edge POP serves the request. Plan capacity-sensitive workloads against the monthly quota; treat the per-minute target as a smoothing signal.

Error Responses

Status CodeDescription
200Success
400Bad Request - Invalid parameters
401Unauthorized - Invalid or missing API key
403Forbidden - Insufficient permissions
404Not Found - Website not found
429Too Many Requests - Rate limit exceeded
500Internal Server Error
Error Response ExampleJSON
{
"error": {
  "code": "unauthorized",
  "message": "Invalid API key provided"
}
}

Next Steps

Esta página foi útil?