API Reference
Analytics read API endpoints are available under /v1/analytics/ for organization-scoped queries
and /v1/analytics/{projectId}/ for service-scoped queries, where {projectId} is the Moneat
service identifier. They require an authenticated dashboard session JWT via the
Authorization: Bearer {token} header.
Authentication
Include the dashboard session JWT in every request:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...Analytics read endpoints do not currently use the scoped API tokens documented on the API Tokens page.
Common parameters
These query parameters are accepted by all endpoints (unless noted otherwise):
| Parameter | Type | Default | Description |
|---|---|---|---|
period | string | 30d | Date range preset: today, 7d, 30d, month, 6mo, 12mo, custom |
date_from | string | - | Start date in YYYY-MM-DD format. Required when period=custom. |
date_to | string | - | End date in YYYY-MM-DD format. Required when period=custom. |
comparison | string | previous_period | Comparison mode: previous_period, year_over_year, none |
filters[] | string[] | - | Filter expressions in property:operator:value format. See Filtering. |
limit | integer | 100 | Maximum number of results for breakdown endpoints. |
Endpoints
GET /overview
Returns top-level KPI metrics for the selected period.
Response:
{
"uniqueVisitors": 12450,
"totalPageviews": 34200,
"bounceRate": 42.5,
"avgVisitDuration": 185,
"viewsPerVisit": 2.75,
"comparison": {
"uniqueVisitors": 11200,
"totalPageviews": 30100,
"bounceRate": 45.1,
"avgVisitDuration": 170,
"viewsPerVisit": 2.69
}
}| Field | Type | Description |
|---|---|---|
uniqueVisitors | integer | Distinct visitors in the period |
totalPageviews | integer | Total page loads |
bounceRate | float | Percentage of single-pageview sessions (0–100) |
avgVisitDuration | integer | Average session duration in seconds |
viewsPerVisit | float | Average pageviews per session |
comparison | object | Same fields for the comparison period (absent if comparison=none) |
GET /timeseries
Returns time-bucketed data for charting visitors and pageviews over time.
Response:
[
{ "timestamp": "2025-01-01T00:00:00Z", "visitors": 142, "pageviews": 380 },
{ "timestamp": "2025-01-02T00:00:00Z", "visitors": 156, "pageviews": 425 },
...
]| Field | Type | Description |
|---|---|---|
timestamp | string | ISO 8601 timestamp for the start of the bucket |
visitors | integer | Unique visitors in this bucket |
pageviews | integer | Total pageviews in this bucket |
Bucket size is determined automatically by the period (hourly for today, daily for 7d/30d, weekly for 6mo, monthly for 12mo).
GET /sites
Tracked sites (hostnames) ranked by visitor count. Use it to tell one site's traffic from another when several report into the same project.
Response:
{
"results": [
{ "name": "moneat.io", "visitors": 5400, "pageviews": 14200 },
{ "name": "app.moneat.io", "visitors": 210, "pageviews": 9800 }
]
}Pass filters[]=site:is:<hostname> to any endpoint that accepts filters to narrow it to one of
them. /realtime and /retention take no filters.
GET /pages
Top pages ranked by visitor count.
Response:
[
{
"name": "/pricing",
"visitors": 3200,
"pageviews": 4100,
"bounceRate": 35.2,
"avgDuration": 210,
"percentage": 25.7
},
...
]| Field | Type | Description |
|---|---|---|
name | string | Page URL path |
visitors | integer | Unique visitors |
pageviews | integer | Total views |
bounceRate | float | Bounce rate for sessions entering on this page |
avgDuration | integer | Average session duration in seconds |
percentage | float | Share of total visitors (0–100) |
GET /entry-pages
Top entry (landing) pages - the first page visitors see when they arrive.
Response: Same structure as /pages.
GET /exit-pages
Top exit pages - the last page visitors see before leaving.
Response:
[
{
"name": "/checkout",
"visitors": 1800,
"pageviews": 2400,
"percentage": 14.5
},
...
]GET /sources
Traffic sources (referrers) ranked by visitor count.
Response:
[
{ "name": "Google", "visitors": 5400, "pageviews": 14200, "percentage": 43.4 },
{ "name": "Direct", "visitors": 3100, "pageviews": 7800, "percentage": 24.9 },
{ "name": "twitter.com", "visitors": 1200, "pageviews": 2800, "percentage": 9.6 },
...
]GET /utm/{param}
Breakdown by a specific UTM parameter.
Path Parameters:
| Parameter | Values |
|---|---|
param | source, medium, campaign, term, content |
Example:
GET /v1/analytics/1/utm/campaign?period=30dResponse:
[
{ "name": "spring-sale", "visitors": 2400, "pageviews": 5600, "percentage": 38.0 },
{ "name": "newsletter-jan", "visitors": 1100, "pageviews": 2300, "percentage": 17.5 },
...
]GET /locations
Geographic breakdown of visitors by country.
Response:
[
{ "name": "US", "visitors": 5200, "pageviews": 14000, "percentage": 41.8 },
{ "name": "GB", "visitors": 1800, "pageviews": 4200, "percentage": 14.5 },
{ "name": "DE", "visitors": 1400, "pageviews": 3600, "percentage": 11.2 },
...
]The name field contains the ISO 3166-1 alpha-2 country code. Requires GeoIP database to be configured.
GET /devices
Device, browser, or OS breakdown.
Query Parameters:
| Parameter | Required | Values |
|---|---|---|
type | Yes | browser, os, device |
Example:
GET /v1/analytics/1/devices?type=browser&period=30dResponse:
[
{ "name": "Chrome", "visitors": 6800, "pageviews": 18400, "percentage": 54.6 },
{ "name": "Safari", "visitors": 2400, "pageviews": 5800, "percentage": 19.3 },
{ "name": "Firefox", "visitors": 1600, "pageviews": 4200, "percentage": 12.9 },
...
]GET /events
Custom event names and occurrence counts.
Response:
[
{ "name": "Signup", "visitors": 840, "pageviews": 920, "percentage": 6.7 },
{ "name": "Download", "visitors": 420, "pageviews": 580, "percentage": 3.4 },
...
]GET /realtime
Current number of active visitors (last 5 minutes).
No query parameters - this endpoint does not accept period or filter parameters.
Response:
{
"currentVisitors": 24
}GET /funnel
Funnel analysis for ordered event sequences.
Query Parameters:
| Parameter | Required | Description |
|---|---|---|
steps[] | Yes | Ordered funnel steps. Use pageview:/path for pageviews or EventName for custom events. |
Example:
GET /v1/analytics/1/funnel?steps[]=pageview:/pricing&steps[]=Start Trial&steps[]=Complete Signup&period=30dResponse:
{
"steps": [
{ "name": "pageview:/pricing", "visitors": 1200, "conversionRate": 100.0, "dropoffRate": 0.0 },
{ "name": "Start Trial", "visitors": 840, "conversionRate": 70.0, "dropoffRate": 30.0 },
{ "name": "Complete Signup", "visitors": 504, "conversionRate": 42.0, "dropoffRate": 40.0 }
],
"overallConversionRate": 42.0
}See Funnel Analysis for detailed usage.
Excluded traffic
These endpoints are organization-scoped: they take no {projectId} and address
/v1/analytics/exclusions directly. Reading is open to any member; creating and deleting require
an organization owner or admin.
GET /exclusions
The client addresses and ranges whose events are discarded at ingest.
{
"exclusions": [
{
"id": "0f5f2a3e-6b4d-4a5f-9a70-6f4e2d1c8b90",
"ipRange": "203.0.113.0/24",
"label": "Office",
"createdAt": "2026-09-21T09:14:02Z"
}
]
}GET /exclusions/client-address
The address this request arrived from, read exactly as ingest reads it, and whether an exclusion already covers it.
{ "address": "203.0.113.4", "excluded": false }POST /exclusions
Body:
{ "ipRange": "203.0.113.0/24", "label": "Office" }ipRange is an IP address or a CIDR range; a range is stored as its network address. Returns
201 with the created exclusion.
| Status | Meaning |
|---|---|
400 | ipRange is not an address or CIDR range |
403 | The caller is not an organization owner or admin |
409 | The organization already excludes that range |
422 | The organization holds the maximum of 200 exclusions |
DELETE /exclusions/{id}
Stops excluding one address or range, addressed by the id the list returns. Returns 204, 400 when the
id is not one Moneat issued, or 404 when the organization holds no such exclusion.
Error responses
| Status Code | Description |
|---|---|
401 Unauthorized | Missing or invalid JWT token |
403 Forbidden | User does not have access to this service or organization |
404 Not Found | Service or organization not found |
422 Unprocessable Entity | Invalid parameters (e.g., bad date format, unknown filter property) |
429 Too Many Requests | Rate limit exceeded |
Error responses include a JSON body:
{
"error": "Invalid filter property: unknown_prop"
}Rate limits
Analytics API endpoints are rate-limited per organization. Current limits depend on your plan and deployment configuration; see Billing & Plans for the current source of truth.
Rate limit headers are included in every response:
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 298
X-RateLimit-Reset: 1706745600