Skip to main content

Multi-Site Aggregate Stats

Consolidated analytics across multiple sites at once. Instead of querying /stats/* once per site and merging the results yourself, the /stats/aggregate/* endpoints return portfolio-level totals — total entrances, revenue, top pages, top sources — summed across every site you pass in.

Typical use case: an internal or agency dashboard that shows "all my properties" in one view.

Base path: /stats/aggregate


Access Requirements

These endpoints are more restricted than the regular stats endpoints:

  • Superadmin role required. Only users with the global superadmin role can call multi-site aggregation. Any other role receives 403.
  • read scope required on the token (same as other stats endpoints).
  • The caller must have access to all requested account_ids — if any is denied, the whole request fails with 403 listing the denied accounts.
curl "https://my.sealmetrics.com/api/v1/stats/aggregate/overview?account_ids=site-a&account_ids=site-b&period=30d" \
-H "Authorization: Bearer YOUR_JWT"

Common Parameters

All aggregate endpoints accept these parameters:

ParameterTypeRequiredDescription
account_idsstring (repeated)YesSite IDs to aggregate. Repeat the parameter for each site (?account_ids=a&account_ids=b). Minimum 2 distinct IDs; duplicates are removed
start_datedateNoStart date (YYYY-MM-DD)
end_datedateNoEnd date (YYYY-MM-DD)
periodstringNoPeriod shortcut (e.g. 30d, this_month) — overrides explicit dates

Passing fewer than 2 account_ids returns 400 ("Multi-site aggregation requires at least 2 account_ids").

Timezone note: period shortcuts (30d, this_month, ...) are resolved using the timezone of the first account_id in the list. Explicit start_date/end_date are passed through unchanged.

The paginated endpoints (pages, mediums, sources, geo/countries, landing-pages, conversions) additionally accept:

ParameterTypeDefaultDescription
pageint1Page number (min 1)
page_sizeint50Items per page (1-100)
sort_bystringentrancesSort field (count for /conversions)
sort_orderstringdescasc or desc

Aggregated Overview

GET /stats/aggregate/overview

Consolidated dashboard overview: traffic totals, conversion totals, previous-period comparison, and time series — all summed across the requested sites.

Extra parameters:

ParameterTypeDefaultDescription
comparestringpreviousComparison mode: previous, yoy, or none
include_seriesbooltrueInclude time series data for charts
include_per_accountboolfalseInclude per-site breakdown inside traffic.by_account

Response:

{
"success": true,
"data": {
"account_ids": ["site-a", "site-b"],
"account_count": 2,
"date_range": {
"start_date": "2025-01-01",
"end_date": "2025-01-30"
},
"traffic": {
"account_count": 2,
"entrances": 45230,
"engaged_entrances": 26890,
"page_views": 112450,
"microconversions": 3120,
"conversions": 587,
"revenue": 48120.50,
"by_account": [
{
"account_id": "site-a",
"entrances": 30150,
"engaged_entrances": 18200,
"page_views": 76300,
"microconversions": 2100,
"conversions": 402,
"revenue": 33400.00,
"bounce_rate": 39.64,
"conversion_rate": 1.33
}
],
"bounce_rate": 40.55,
"pages_per_session": 2.49,
"conversion_rate": 1.3
},
"conversions": {
"conversions": 587,
"revenue": 48120.50,
"microconversions": 3120,
"conversion_rate": 1.3,
"average_order_value": 81.98
},
"comparison": {
"date_range": {
"start_date": "2024-12-02",
"end_date": "2024-12-31"
},
"entrances": 41800,
"engaged_entrances": 24500,
"page_views": 103900,
"microconversions": 2890,
"conversions": 531,
"revenue": 43050.00,
"bounce_rate": 41.39,
"conversion_rate": 1.27
},
"entrances_series": {
"metric": "entrances",
"points": [
{ "date": "2025-01-01", "value": 1420 },
{ "date": "2025-01-02", "value": 1585 }
],
"total": 45230
},
"page_views_series": { "metric": "page_views", "points": [], "total": 0 },
"conversions_series": { "metric": "conversions", "points": [], "total": 0 }
},
"meta": {},
"timestamp": "2025-01-31T00:00:00Z"
}

by_account is null unless include_per_account=true; the three *_series fields are null when include_series=false; comparison is null when compare=none.


Aggregated Pages

GET /stats/aggregate/pages

Page metrics summed across sites. Pages with the same path on different sites merge into a single row; account_count says how many sites contributed to it.

Response (paginated):

{
"success": true,
"data": [
{
"path": "/pricing",
"content_grouping": "marketing",
"account_count": 2,
"entrances": 8420,
"engaged_entrances": 5210,
"page_views": 15300,
"conversions": 214,
"bounce_rate": 38.12
}
],
"total": 342,
"page": 1,
"page_size": 50,
"has_next": true,
"has_prev": false,
"comparison": null,
"totals": null,
"timestamp": "2025-01-31T00:00:00Z"
}

Aggregated Mediums

GET /stats/aggregate/mediums

Traffic grouped by UTM medium across all requested sites.

Response (paginated, items share the AggregatedSourceMetrics shape):

{
"success": true,
"data": [
{
"name": "organic",
"utm_source": "",
"utm_medium": "organic",
"utm_campaign": "",
"account_count": 2,
"entrances": 19800,
"engaged_entrances": 12400,
"page_views": 52100,
"microconversions": 1340,
"conversions": 265,
"revenue": 21500.00,
"bounce_rate": 37.37,
"conversion_rate": 1.34
}
],
"total": 8,
"page": 1,
"page_size": 50,
"has_next": false,
"has_prev": false,
"comparison": null,
"totals": null,
"timestamp": "2025-01-31T00:00:00Z"
}

Aggregated Sources

GET /stats/aggregate/sources

Traffic grouped by UTM source across all requested sites.

Extra parameter:

ParameterTypeRequiredDescription
utm_mediumstringNoFilter sources to a single UTM medium

Response items use the same AggregatedSourceMetrics shape as /mediums, with name/utm_source set to the source value (e.g. "google").


Aggregated Countries

GET /stats/aggregate/geo/countries

Geographic breakdown across all requested sites.

Response (paginated):

{
"success": true,
"data": [
{
"country": "ES",
"country_name": "Spain",
"account_count": 2,
"entrances": 15230,
"engaged_entrances": 9400,
"page_views": 38900,
"microconversions": 1020,
"conversions": 198,
"revenue": 16800.00,
"bounce_rate": 38.28
}
],
"total": 64,
"page": 1,
"page_size": 50,
"has_next": true,
"has_prev": false,
"comparison": null,
"totals": null,
"timestamp": "2025-01-31T00:00:00Z"
}

Aggregated Devices

GET /stats/aggregate/devices

Device-type breakdown across all requested sites. Not paginated — returns the full list (there are only a handful of device types).

Response:

{
"success": true,
"data": [
{
"name": "desktop",
"entrances": 24900,
"engaged_entrances": 15800,
"page_views": 65400,
"microconversions": 1900,
"conversions": 362,
"revenue": 30200.00,
"percentage": 55.05,
"bounce_rate": 36.55,
"conversion_rate": 1.45
},
{
"name": "mobile",
"entrances": 18100,
"engaged_entrances": 9800,
"page_views": 41200,
"microconversions": 1100,
"conversions": 201,
"revenue": 15900.00,
"percentage": 40.02,
"bounce_rate": 45.86,
"conversion_rate": 1.11
}
],
"meta": {},
"timestamp": "2025-01-31T00:00:00Z"
}

percentage is each device's share of total entrances (0-100).


Aggregated Landing Pages

GET /stats/aggregate/landing-pages

Landing page metrics summed across sites.

Response (paginated):

{
"success": true,
"data": [
{
"landing_page": "/lp/spring-sale",
"content_grouping": "campaigns",
"account_count": 2,
"entrances": 6200,
"engaged_entrances": 4100,
"microconversions": 480,
"conversions": 121,
"revenue": 9800.00,
"bounce_rate": 33.87,
"conversion_rate": 1.95
}
],
"total": 87,
"page": 1,
"page_size": 50,
"has_next": true,
"has_prev": false,
"comparison": null,
"totals": null,
"timestamp": "2025-01-31T00:00:00Z"
}

Aggregated Conversions

GET /stats/aggregate/conversions

Conversions grouped by conversion type across all requested sites. Default sort field is count (not entrances).

Response (paginated):

{
"success": true,
"data": [
{
"conversion_type": "purchase",
"account_count": 2,
"count": 587,
"revenue": 48120.50,
"avg_value": 81.98
},
{
"conversion_type": "signup",
"account_count": 2,
"count": 1240,
"revenue": 0,
"avg_value": 0
}
],
"total": 4,
"page": 1,
"page_size": 50,
"has_next": false,
"has_prev": false,
"comparison": null,
"totals": null,
"timestamp": "2025-01-31T00:00:00Z"
}

Errors

HTTP CodeCause
400Fewer than 2 account_ids, invalid compare mode, or invalid date range
403Caller is not superadmin, or lacks access to one or more of the requested accounts

See Also