Statistics API
Burst Statistics v3.4.0 exposes a PHP API for querying analytics data programmatically. The entry point is the Statistics class, backed by the Query_Data value object that constructs and sanitizes SQL queries.
Architecture overview
Statistics (class-statistics.php)
└── Query_Data (class-query-data.php) ← value object, builds SQL
└── Goal_Statistics (class-goal-statistics.php) ← goal-specific queries
All queries target a set of custom database tables prefixed with {$wpdb->prefix}burst_. Execution time for every query is automatically stored in burst_query_stats for slow-query analysis.
Database tables
| Table | Purpose |
|---|---|
burst_statistics | Raw page-hit records |
burst_sessions | Session records (browser, device, referrer, etc.) |
burst_browsers | Browser lookup table |
burst_browser_versions | Browser version lookup table |
burst_platforms | OS/platform lookup table |
burst_devices | Device type lookup table |
burst_referrers | Referrer lookup table |
burst_goals | Goal definitions |
burst_goal_statistics | Goal completion events |
burst_known_uids | Known visitor UIDs with first/last seen timestamps |
burst_query_stats | Query execution time statistics |
burst_statistics schema
:::caution Breaking change
In v3.3.0 the columns browser_id, browser_version_id, platform_id, device_id, first_time_visit, and bounce were moved from burst_statistics to burst_sessions. Any custom SQL or filters that reference statistics.browser_id, statistics.device_id, statistics.first_time_visit, or statistics.bounce must be updated to use the sessions.* prefix. A database migration runs automatically on upgrade via the move_columns_to_sessions upgrade routine.
:::
| Column | Type | Description |
|---|---|---|
ID | int AUTO_INCREMENT | Primary key |
page_url | varchar(191) | URL of the visited page |
page_id | int(11) | WordPress post/page ID |
page_type | varchar(191) | Post type (e.g. post, page) |
time | int | Unix timestamp of the hit |
uid | varchar(64) | Visitor unique identifier |
time_on_page | int | Time spent on page (milliseconds) |
parameters | TEXT | Serialized URL parameters |
fragment | varchar(255) | URL fragment |
session_id | int | FK → burst_sessions.ID |
burst_sessions schema (selected columns)
The following columns were added to burst_sessions in v3.3.0:
| Column | Type | Description |
|---|---|---|
browser_id | int | FK → burst_browsers.ID |
browser_version_id | int | FK → burst_browser_versions.ID |
platform_id | int | FK → burst_platforms.ID |
device_id | int | FK → burst_devices.ID |
first_time_visit | tinyint | 1 if this session is the visitor's first |
bounce | tinyint | 1 if the session is a bounce (default 1) |
burst_query_stats schema
| Column | Type | Description |
|---|---|---|
ID | int AUTO_INCREMENT | Primary key |
sql_hash | varchar(64) | Deterministic hash of the query fingerprint payload |
sql_query | TEXT | Raw SQL that was executed |
avg_execution_time | float | Rolling average execution time in seconds |
max_execution_time | float | Slowest recorded execution in seconds |
min_execution_time | float | Fastest recorded execution in seconds |
execution_count | int | Total number of executions recorded |
last_updated | int | Unix timestamp of the most recent update |
date_range_days | int | Length (in days) of the date range used by the query |
Added in v3.4.0: the date_range_days column. The burst_query_stats table is pruned to the 100 slowest queries; queries that span more than 365 days are not recorded.
The Query_Data class
Query_Data is the query builder. Pass a stable query id and an associative array of options to its constructor; it validates and sanitizes every value before use. The query id is used to generate a deterministic fingerprint hash that groups related queries together in burst_query_stats, regardless of the exact timestamps passed in.
Show code
use Burst\Admin\Statistics\Query_Data;
$qd = new Query_Data(
'top_pages_by_device',
[
'date_start' => strtotime( '2024-01-01' ),
'date_end' => strtotime( '2024-01-31' ),
'select' => [ 'pageviews', 'visitors' ],
'filters' => [ 'device' => 'mobile' ],
'group_by' => 'page_url',
'order_by' => 'pageviews DESC',
'limit' => 10,
]
);
Constructor parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
$id | string | — | Required. Stable identifier for this query family; used for deterministic fingerprinting in burst_query_stats. |
$args | array | [] | Associative array of query options (see below). |
Query options
| Option | Type | Default | Description |
|---|---|---|---|
date_start | int | 0 | Start of the date range (Unix timestamp) |
date_end | int | 0 | End of the date range (Unix timestamp) |
select | string[] | ['*'] | Metrics to retrieve (see Available Metrics) |
filters | array | [] | Key/value filters (see Available Filters) |
filter_exclusions | array | [] | Per-filter mode: 'include' or 'exclude'; automatically set when a filter value is prefixed with ! |
group_by | string|string[] | [] | Field(s) to group by |
order_by | string|string[] | [] | Field(s) to order by, e.g. 'pageviews DESC' |
limit | int | 0 | Maximum rows to return (0 = unlimited) |
joins | array | [] | Additional JOIN configurations |
date_modifiers | array | [] | Date interval settings for period grouping |
having | array | [] | HAVING clause conditions |
custom_select | string | '' | Raw SELECT fragment (admin-only; validated and prepared) |
custom_select_parameters | array | [] | Prepared-statement parameters for custom_select |
custom_where | string | '' | Raw WHERE fragment (admin-only; validated and prepared) |
custom_where_parameters | array | [] | Prepared-statement parameters for custom_where |
subquery | string | '' | Wraps the main query as a subquery with the given alias |
union | array | [] | SQL strings to UNION with the main query |
distinct | bool | false | Add DISTINCT to SELECT |
window | array | [] | Window function definitions keyed by alias |
exclude_bounces | bool | false | Automatically set to true when filters['bounces'] = 'exclude' |
Strict mode
Query_Data automatically applies strict mode for non-administrator users. In strict mode:
- Only a limited set of metrics may be selected (see
burst_allowed_metric_keysfilter). - Only a limited set of filter keys are accepted.
custom_selectandcustom_whereare silently ignored.
The default strict-mode metric allowlist is:
pageviews, visitors, sessions, bounce_rate, avg_time_on_page,
first_time_visitors, page_url, referrer, device
Filter exclusions
Prefix any filter value with ! to exclude rather than include matching rows:
Show code
$qd = new Query_Data(
'pageviews_excluding_mobile',
[
'date_start' => $start,
'date_end' => $end,
'select' => [ 'pageviews', 'page_url' ],
'filters' => [ 'device' => '!mobile' ], // exclude mobile
]
);
Changed in v3.4.0: The referrer filter no longer includes NULL referrer rows when the filter is applied in include mode. NULL referrers are still included when the filter is applied in exclude mode (!example.com). Custom code that relied on NULL rows being returned alongside matching referrers in include mode must be updated.
Query fingerprinting
Every query executed via get_var(), get_row(), or get_results() is hashed and stored in burst_query_stats for slow-query analysis. The hash is deterministic: two queries with the same id, metrics, filters, group_by, order_by, and date range length (in days) will produce the same sql_hash, regardless of the actual timestamps.
Query_Data::get_id()
Returns the sanitized query id supplied to the constructor.
$id = $qd->get_id();
// 'top_pages_by_device'
Query_Data::get_fingerprint_payload()
Returns the canonical array used to build the fingerprint hash. Absolute timestamps are intentionally excluded; date_range_days is used instead so that the same query family hashes to the same value across days.
Show code
$payload = $qd->get_fingerprint_payload();
// [
// 'id' => 'top_pages_by_device',
// 'select' => [ 'pageviews', 'visitors' ],
// 'filters' => [ 'device' => 'mobile' ],
// 'filter_exclusions' => [],
// 'group_by' => [ 'page_url' ],
// 'order_by' => [ 'pageviews DESC' ],
// 'limit' => 10,
// 'distinct' => false,
// 'exclude_bounces' => false,
// 'date_range_days' => 30,
// ]
Query_Data::get_fingerprint_hash()
Returns the 64-bit FNV-1a hash of the JSON-encoded fingerprint payload.
$hash = $qd->get_fingerprint_hash();
// e.g. 'a1b2c3d4e5f60718'
Available metrics
These are the metric keys accepted in the select parameter:
| Key | Label | Notes |
|---|---|---|
pageviews | Pageviews | |
visitors | Visitors | Distinct UIDs |
sessions | Sessions | Distinct session IDs |
first_time_visitors | New visitors | |
avg_time_on_page | Avg. time on page | Milliseconds |
avg_session_duration | Avg. session duration | |
bounce_rate | Bounce rate | Percentage |
bounces | Bounced visitors | |
conversions | Goal completions | Requires goal_id filter |
conversion_rate | Goal conv. rate | |
page_url | Page | |
referrer | Referrer | |
host | Domain | |
device | Device | |
browser | Browser | |
platform | Platform | |
device_id | Device (ID) | Resolved from sessions.device_id |
browser_id | Browser (ID) | Resolved from sessions.browser_id |
platform_id | Platform (ID) | Resolved from sessions.platform_id |
count | Count | Alias for pageviews |
period | Period | Used with group_by: 'period' |
time | Time | Raw timestamp column |
time_on_page | Time on page | |
uid | UID | |
page_id | Page ID |
Pro - CreatorAvailable in the Creator tier
The following metrics are available in Burst Pro:
| Key | Label |
|---|---|
country_code | Country |
city | City |
state | State |
continent_code | Continent |
source | Source (UTM) |
medium | Medium (UTM) |
campaign | Campaign (UTM) |
term | Term (UTM) |
content | Content (UTM) |
parameter | URL Parameter |
product | Product |
sales | Sales |
revenue | Revenue |
page_value | Page value |
sales_conversion_rate | Sales conv. rate |
avg_order_value | Avg. order value |
adds_to_cart | Added to cart |
entrances | Entrances |
exit_rate | Exit rate |
time_per_session | Time per session |
Available filters
Filters narrow query results. Pass them as an associative array to Query_Data via the filters key.
| Key | Description | Values |
|---|---|---|
page_url | Filter by page URL | String, supports * wildcard suffix |
page_id | Filter by WordPress page ID | Integer |
page_type | Filter by post type | Public post type string |
referrer | Filter by referrer URL | String |
device | Filter by device type | desktop, tablet, mobile, other |
browser | Filter by browser name | String |
platform | Filter by OS/platform | String |
device_id | Filter by device lookup ID | Integer |
browser_id | Filter by browser lookup ID | Integer |
platform_id | Filter by platform lookup ID | Integer |
goal_id | Filter by goal ID (admin only) | Integer |
bounces | Include/exclude bounced sessions (admin only) | include, exclude |
new_visitor | Filter first-time visitors (admin only) | include, exclude |
entry_exit_pages | Filter entry or exit pages (admin only) | entry, exit |
host | Filter by domain/host (admin only) | String |
parameter | Filter by URL parameter (admin only) | key, key=value |
Pro - CreatorAvailable in the Creator tier
The following filters are available in Burst Pro:
| Key | Description | Values | Notes |
|---|---|---|---|
time_per_session | Filter sessions by total time spent | "MIN-MAX" or "MIN-" in seconds | Added in v3.3.0. E.g. "30-120" for 30–120 s; "600-" for 600 s and above. |
Show code
$qd = new Query_Data(
'sessions_by_time_bucket',
[
'date_start' => $start,
'date_end' => $end,
'select' => [ 'pageviews', 'sessions' ],
'filters' => [ 'time_per_session' => '30-120' ], // sessions with 30–120 s total
]
);
The value is sanitized by sanitize_time_per_session_filter(). Both MIN and MAX are integers representing seconds; internally they are multiplied by 1000 to compare against the millisecond time_on_page column. If MAX is omitted (open-ended range), use the "MIN-" format.
The Statistics class
Obtain the singleton via:
$statistics = \Burst\burst_loader()->admin->statistics;
get_live_visitors_data()
Returns the number of visitors currently active on the site (within the last 10 minutes, excluding sessions that have been idle longer than the exit margin).
$count = $statistics->get_live_visitors_data();
// returns int
Added in v3.4.1: this method is also exposed to AI agents through the WordPress Abilities API as burst/live-visitors when the enable_abilities_api option is on. The ability response shape is [ 'visitors' => int ].
get_live_traffic_data()
Returns an array of the most recent page-hit objects within the last 10 minutes. Each object includes entry/exit/checkout flags.
$rows = $statistics->get_live_traffic_data();
Return shape (per object):
| Property | Type | Description |
|---|---|---|
active_time | float | time + time_on_page / 1000 |
utm_source | string | Session referrer |
page_url | string | Page URL |
time | int | Hit timestamp |
time_on_page | int | Time on page (ms) |
uid | string | Visitor UID |
page_id | int | WordPress page ID |
entry | bool | Whether this is the visitor's entry hit |
checkout | bool | Whether this page is the checkout page |
exit | bool | Whether the visitor appears to have exited |
Added in v3.4.1: this method is also exposed to AI agents through the WordPress Abilities API as burst/live-traffic when the enable_abilities_api option is on. The ability accepts an optional limit (1–100, default 100) and returns [ 'items' => array, 'total' => int ].
get_today_data( array $args )
Returns summary statistics for today's dashboard block.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$args['date_start'] | int | Start of today (timestamp). Default 0. |
$args['date_end'] | int | End of today (timestamp). Default 0. |
Return shape:
Show code
[
'live' => [ 'value' => string, 'tooltip' => string ],
'today' => [ 'value' => string, 'tooltip' => string ],
'mostViewed' => [ 'title' => string, 'value' => string, 'tooltip' => string ],
'referrer' => [ 'title' => string, 'value' => string, 'tooltip' => string ],
'pageviews' => [ 'title' => string, 'value' => string, 'tooltip' => string ],
'timeOnPage' => [ 'title' => string, 'value' => string, 'tooltip' => string ],
]
Added in v3.4.1: this method is also exposed to AI agents through the WordPress Abilities API as burst/today-summary when the enable_abilities_api option is on. The ability accepts optional date_start and date_end integers and returns a flattened, integer-only payload (live, today, most_viewed, top_referrer, pageviews, avg_time_on_page).
get_insights_data( array $args )
Returns chart-ready datasets and timestamp metadata for the insights panel.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$args['date_start'] | int | Start timestamp. Default 0. |
$args['date_end'] | int | End timestamp. Default 0. |
$args['metrics'] | string[] | Metrics to chart. Default ['pageviews', 'visitors']. |
$args['filters'] | array | Filters to apply. Default []. |
$args['group_by'] | string | Grouping interval (auto, hour, day, week, month). Default 'auto'. |
Changed in v3.4.0: the return shape replaces the pre-formatted labels array with raw timestamps plus interval and spans_multiple_years metadata, so the frontend can format dates locale-aware. A new group_by argument lets callers force a specific interval instead of deriving it from the range length.
Return shape:
Show code
[
'timestamps' => int[], // UTC period-start timestamps
'interval' => string, // 'hour' | 'day' | 'week' | 'month'
'spans_multiple_years' => bool, // true when the range crosses a calendar year
'datasets' => [
[
'data' => (int|float)[],
'backgroundColor' => string, // CSS custom property reference
'borderColor' => string, // CSS custom property reference
'label' => string,
'fill' => string,
],
// one entry per metric
],
]
The interval (hour / day / week / month) is determined automatically from the date range when group_by is 'auto':
| Range | Interval |
|---|---|
| ≤ 2 days | Hour |
| 3–48 days | Day |
| 49–364 days | Week |
| > 364 days | Month |
Added in v3.4.1: this method is also exposed to AI agents through the WordPress Abilities API as burst/data with type: 'insights' when the enable_abilities_api option is on. The ability reformats the response into a series array (one entry per requested metric, each with id, label, and [ timestamp, value ] points).
Changed in v3.4.2: the burst/data ability now accepts an interval hint (auto, hour, day, week, month) for insights requests. The value is forwarded to get_insights_data() as group_by. For backwards compatibility, when interval is omitted the first value of the legacy group_by input is used instead.
get_insights_date_modifiers( int $date_start, int $date_end, string $group_by = 'auto' )
Returns the date format strings and interval metadata used to build insight charts.
$modifiers = $statistics->get_insights_date_modifiers( $start, $end );
Changed in v3.4.0: added a third $group_by parameter ('auto', 'hour', 'day', 'week', 'month') to force a specific interval. The return shape replaces php_pretty_date_format with a spans_multiple_years boolean; date labels are now formatted on the frontend.
Return shape:
| Key | Type | Description |
|---|---|---|
interval | string | hour, day, week, or month |
interval_in_seconds | int | Interval length in seconds |
nr_of_intervals | int | Number of data points in the range |
sql_date_format | string | MySQL DATE_FORMAT() pattern |
php_date_format | string | PHP date() pattern for array keys |
spans_multiple_years | bool | true when date_start and date_end fall in different calendar years |
get_compare_data( array $args )
Returns current-period and previous-period summary metrics for comparison widgets.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$args['date_start'] | int | Start of current period (timestamp). |
$args['date_end'] | int | End of current period (timestamp). |
$args['compare_date_start'] | int|null | Start of comparison period. Defaults to the equivalent prior period. |
$args['compare_date_end'] | int|null | End of comparison period. |
$args['filters'] | array | Filters to apply to both periods. |
Return shape:
Show code
[
'current' => [
'pageviews' => int,
'sessions' => int,
'visitors' => int,
'first_time_visitors' => int,
'avg_time_on_page' => int,
'bounced_sessions' => int,
'bounce_rate' => float,
],
'previous' => [
'pageviews' => int,
'sessions' => int,
'visitors' => int,
'bounced_sessions' => int,
'bounce_rate' => float,
],
]
get_compare_goals_data( array $args )
Pro - CreatorAvailable in the Creator tier
Returns current-period and previous-period goal/conversion metrics.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$args['date_start'] | int | Start timestamp. |
$args['date_end'] | int | End timestamp. |
$args['filters'] | array | Filters, may include goal_id. |
$args['compare_date_start'] | int|null | Start of comparison period. |
$args['compare_date_end'] | int|null | End of comparison period. |
Return shape:
Show code
[
'view' => 'goals',
'current' => [
'pageviews' => int,
'visitors' => int,
'sessions' => int,
'first_time_visitors' => int,
'conversions' => int,
'conversion_rate' => float,
],
'previous' => [
'pageviews' => int,
'visitors' => int,
'sessions' => int,
'conversions' => int,
'conversion_rate' => float,
],
]
get_data( array $select, int $start, int $end, array $filters )
Low-level single-row query. Returns one associative array of metric values for the given period.
Show code
$row = $statistics->get_data(
[ 'pageviews', 'visitors', 'sessions' ],
strtotime( '2024-01-01' ),
strtotime( '2024-01-31' ),
[ 'device' => 'mobile' ]
);
// [ 'pageviews' => 1200, 'visitors' => 340, 'sessions' => 400 ]
get_devices_title_and_value_data( array $args )
Returns pageview counts grouped by device type.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$args['date_start'] | int | Start timestamp. Default 0. |
$args['date_end'] | int | End timestamp. Default 0. |
$args['filters'] | array | Filters to apply. Default []. |
Return shape:
Show code
[
'all' => [ 'count' => int ],
'desktop' => [ 'count' => int ],
'tablet' => [ 'count' => int ],
'mobile' => [ 'count' => int ],
'other' => [ 'count' => int ],
]
get_devices_subtitle_data( array $args )
Returns the most common browser and OS for each device type.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$args['date_start'] | int | Start timestamp. Default 0. |
$args['date_end'] | int | End timestamp. Default 0. |
$args['filters'] | array | Filters to apply. Default []. |
Return shape:
Show code
[
'desktop' => [ 'os' => string, 'browser' => string, 'device_id' => int ],
'tablet' => [ 'os' => string, 'browser' => string, 'device_id' => int ],
'mobile' => [ 'os' => string, 'browser' => string, 'device_id' => int ],
'other' => [ 'os' => string, 'browser' => string, 'device_id' => int ],
]
get_datatables_data( array $args )
Returns data for the sortable data table components in the dashboard. Colors emitted by this method are CSS custom-property strings (e.g. var(--color-blue-400)); the frontend's METRIC_COLORS map takes precedence when rendering.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$args['date_start'] | int | Start timestamp. Default 0. |
$args['date_end'] | int | End timestamp. Default 0. |
$args['metrics'] | string[] | Metrics to include as columns. Default ['pageviews']. |
$args['filters'] | array | Filters to apply. Default []. |
$args['group_by'] | string | Field to group rows by. |
$args['limit'] | int | Maximum number of rows. Default 0 (unlimited). |
$args['id'] | string | Datatable identifier (e.g. statistics_pages). Added in v3.4.2. Passed through to the burst_datatable_pre_data filter so integrations can short-circuit per-datatable, and used by the REST layer to enforce a per-datatable metric allow-list. |
Return shape:
Show code
[
'columns' => [
[ 'name' => string, 'id' => string, 'sortable' => 'true', 'right' => 'true' ],
// …
],
'data' => array[], // raw rows from the database
'metrics' => string[],
]
Integrations can short-circuit the default query by returning a prebuilt data array via the burst_datatable_pre_data filter. See burst_datatable_pre_data.
Changed in v3.4.2: the generic data/datatable and data/ecommerce/datatable REST endpoints now return 403 Forbidden. Callers must use the granular per-datatable endpoints data/datatable/{id} and data/ecommerce/datatable/{id} (e.g. data/datatable/statistics_pages). The endpoint segment {id} is set as $args['id'] and used to look up the allowed metrics from App::get_datatable_metric_allow_list(); requested metrics are intersected against that allow-list before the query runs. Unknown datatable IDs return 404. Update any custom JavaScript or server-side code that called the generic endpoints.
Changed in v3.4.1: when a metric key has no registered label, the column name now falls back to a humanized form of the key (e.g. my_custom_metric → My Custom Metric) via ucwords( str_replace( '_', ' ', $metric ) ) instead of triggering an undefined-index notice. Custom metrics added via burst_select_sql_for_metric therefore render with a sensible default header even when burst_allowed_metrics_labels is not used.
Added in v3.4.1: this method is also exposed to AI agents through the WordPress Abilities API as burst/data with type: 'datatable' when the enable_abilities_api option is on. The ability reformats the response into dimensions, metrics, rows, and row_count keys, and normalizes a few group_by aliases (e.g. utm_source → source).
Changed in v3.4.2: the burst/data ability now requires a datatable_id input when type is datatable. Accepted values are statistics_pages, statistics_parameters, statistics_referrers, sources_countries, sources_campaigns, sales_products, subscription_products, and sources_referrers. Requested metrics are intersected against the allow-list for that datatable; an unknown datatable_id returns burst_abilities_unknown_datatable. The pro burst/sales and burst/subscriptions-data abilities also accept the same allow-list-aware metrics (with a fallback when none of the requested metrics are valid) plus optional filters and a limit capped to 500.
get_dummy_datatable_data()
Added in v3.4.2. Returns a fixed-size array of synthetic datatable rows for preview, onboarding, and demo contexts. Each row is a fully populated record with realistic-looking values for every metric in the statistics_pages allow-list, including ecommerce metrics shaped as { currency, value }.
This method is wired up via the burst_datatable_pre_data filter on the App class: when $args['id'] is 'dummy_data', the filter returns the dummy rows and short-circuits the database query.
Show code
$rows = $statistics->get_dummy_datatable_data();
// [
// [
// 'page_url' => '/about-us',
// 'pageviews' => 2317,
// 'visitors' => 1689,
// 'sessions' => 1812,
// 'bounce_rate' => 42.3,
// 'avg_time_on_page' => 274,
// 'entrances' => 968,
// 'exit_rate' => 31.7,
// 'conversions' => 142,
// 'conversion_rate' => 6.1,
// 'sales' => 47,
// 'revenue' => [ 'currency' => 'USD', 'value' => 4823 ],
// 'sales_conversion_rate' => 2.0,
// 'page_value' => [ 'currency' => 'USD', 'value' => 2.08 ],
// ],
// // …15 rows total
// ]
Values are generated with wp_rand() and are not stable between calls; do not use them as fixtures in tests that assert exact numbers.
App::get_datatable_metric_allow_list()
Added in v3.4.2. Returns the per-datatable metric allow-list used by both the REST routes and the abilities API to gate which metrics each granular datatable endpoint can expose. The list is keyed by datatable id.
Show code
$allow_list = \Burst\burst_loader()->admin->app->get_datatable_metric_allow_list();
// [
// 'statistics_pages' => [ 'page_url', 'pageviews', 'visitors', ... ],
// 'statistics_parameters' => [ 'parameter', 'parameters', 'visitors', ... ],
// 'statistics_referrers' => [ 'referrer', 'visitors', 'sessions', ... ],
// 'dummy_data' => [ 'page_url', 'pageviews', 'visitors', ... ],
// // Pro adds: sources_countries, sources_campaigns, sales_products, subscription_products, sources_referrers
// ]
The result is filterable via burst_datatable_metric_allow_list. Add an entry for any custom datatable id used by your integration; without an entry the REST handler responds with 404 Unknown datatable endpoint. and the abilities API returns burst_abilities_unknown_datatable.
get_var( Query_Data $qd )
Executes a query and returns a single scalar value.
Show code
$qd = new Query_Data(
'monthly_pageviews',
[ 'date_start' => $start, 'date_end' => $end, 'select' => [ 'pageviews' ] ]
);
$val = $statistics->get_var( $qd );
Changed in v3.4.2.1: the executed SQL is automatically prefixed with a MAX_EXECUTION_TIME optimizer hint resolved via get_query_timeout_ms(). When MySQL aborts the query because the timeout was exceeded, get_var() logs the timeout and returns null rather than surfacing the error. Defaults are 30 s for foreground requests and 900 s for cron, both filterable via burst_query_timeout_ms and burst_query_timeout_ms_background.
get_row( Query_Data $qd, string $output_type = 'OBJECT' )
Executes a query and returns a single row.
$row = $statistics->get_row( $qd, ARRAY_A );
$output_type accepts OBJECT, ARRAY_A, or ARRAY_N.
Changed in v3.4.2.1: results are cached in the WordPress object cache for the duration returned by burst_query_results_cache_ttl (default 30 s; 300 s for date ranges longer than 30 days). When a persistent object cache is active, concurrent requests for the same cache key are deduplicated via a single-flight lock — only the first request runs the heavy query, followers either pick up the cached result or return null immediately to prevent thundering-herd fan-out. If MySQL aborts the query because the MAX_EXECUTION_TIME hint was exceeded, get_row() logs the timeout, writes a short-lived cooldown marker so the same key is not retried immediately, and returns null.
get_results( Query_Data $qd, string $output_type = 'OBJECT' )
Executes a query and returns all matching rows.
$rows = $statistics->get_results( $qd, ARRAY_A );
Changed in v3.4.2.1: results are cached, single-flighted, and timeout-protected with the same semantics as get_row(). When the query times out, get_results() logs the timeout, writes a cooldown marker for the cache key, and returns [] rather than surfacing the error. Followers that lose the single-flight race also return [] if the leader has not produced a cached result within burst_query_single_flight_wait_ms (default 1200 ms).
get_query_timeout_ms( Query_Data $qd )
Added in v3.4.2.1. Resolves the per-query MAX_EXECUTION_TIME (in milliseconds) applied to get_var(), get_row(), and get_results(). Background cron requests use a longer ceiling than foreground dashboard requests so that aggregation and backfill jobs are not killed mid-run.
| Context | Default | Filter |
|---|---|---|
| Foreground (REST, admin) | 30 000 ms (30 s) | burst_query_timeout_ms |
Background (wp_doing_cron()) | 900 000 ms (15 min) | burst_query_timeout_ms_background |
The foreground default can also be overridden via the burst_query_timeout_ms option (positive integer, in milliseconds). The filter always runs last and wins.
$timeout = $statistics->get_query_timeout_ms( $qd );
// e.g. 30000
build_raw_sql( Query_Data $data )
Builds and returns the raw SQL string from a Query_Data object without executing it. Useful for debugging.
$sql = $statistics->build_raw_sql( $qd );
error_log( $sql );
The returned SQL is built for direct use with $wpdb. Do not attempt to re-prepare it. The string returned here does not include the MAX_EXECUTION_TIME optimizer hint that get_var()/get_row()/get_results() prepend at execution time.
get_sql_select_for_metric( string $metric, Query_Data $query_data )
Returns the SQL fragment for a single metric. Respects the exclude_bounces flag.
$sql = $statistics->get_sql_select_for_metric( 'visitors', $qd );
// 'COUNT(DISTINCT statistics.uid)'
Changed in v3.3.0: browser_id, platform_id, device_id, and first_time_visit now resolve to sessions.* instead of statistics.*. For example:
Show code
$sql = $statistics->get_sql_select_for_metric( 'browser_id', $qd );
// 'sessions.browser_id'
$sql = $statistics->get_sql_select_for_metric( 'first_time_visit', $qd );
// 'sessions.first_time_visit'
Changed in v3.4.2.1: the bounces and bounce_rate metrics now read directly from sessions.bounce / sessions.ID instead of from the removed session_bounces derived table. The non-bounce predicate used when exclude_bounces is true is now COALESCE(sessions.bounce, 0) = 0. Custom code that filtered against session_bounces.bounce or session_bounces.session_id must be updated to use the sessions.* columns instead.
should_recommend_object_cache()
Added in v3.4.2.1. Static helper that returns true when Burst has recorded a slow analytics query and no persistent object cache is active. Used to drive the "persistent object cache recommended" admin task; safe to call from any context.
if ( \Burst\Admin\Statistics\Statistics::should_recommend_object_cache() ) {
// Surface a hint to enable Redis or Memcached.
}
Decision logic:
- Returns
falseimmediately whenwp_using_ext_object_cache()istrue. - Returns
falsewhen theburst_query_statstable does not yet exist. - Otherwise reads
MAX(max_execution_time)fromburst_query_statsand returnstruewhen it meets or exceeds the threshold returned by theburst_object_cache_recommendation_threshold_secondsfilter (default 10 seconds; clamped to a 0.1 s floor).
format_number( int $number, int $precision = 2 )
Formats a number with locale-aware separators and shorthand suffixes for large values (k, M, G, …).
echo $statistics->format_number( 123456 ); // '123k'
echo $statistics->format_number( 1234 ); // '1,234'
format_uplift( float $original_value, float $new_value )
Returns a formatted uplift string such as +12% or -5%, or an empty string when there is no change.
calculate_uplift( float $original_value, float $new_value )
Returns the percentage change as an integer.
calculate_uplift_status( float $original_value, float $new_value )
Returns 'positive', 'negative', or ''.
get_lookup_table_name_by_id( string $item, int $id )
Resolves a lookup-table ID to a human-readable name. Results are object-cached.
$name = $statistics->get_lookup_table_name_by_id( 'device', 2 );
// 'mobile'
$item must be one of: browser, browser_version, platform, device.
is_campaign_conversion_query( Query_Data $data )
Returns true when the query selects campaign parameters together with conversion or sales metrics.
is_parameter_conversion_query( Query_Data $data )
Returns true when the query selects URL parameters together with conversion or sales metrics.
Goal statistics
The Goal_Statistics class handles goal-specific queries.
$goal_stats = \Burst\burst_loader()->admin->goal_statistics;
get_goals_data( array $args )
Returns the full data payload for a goal detail card.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$args['goal_id'] | int | Goal ID to query. Default 0. |
$args['date_start'] | int | Start timestamp. Default 0. |
$args['date_end'] | int | End timestamp. Default 0. |
Return shape (abbreviated):
Show code
[
'today' => [ 'value' => int, 'tooltip' => string ],
'total' => [ 'value' => int, 'tooltip' => string ],
'topPerformer' => [ 'title' => string, 'value' => int, 'tooltip' => string ],
'conversionMetric' => [ 'title' => string, 'value' => int, 'tooltip' => string, 'icon' => string ],
'conversionPercentage'=> [ 'title' => string, 'value' => int, 'tooltip' => string ],
'bestDevice' => [ 'title' => string, 'value' => int, 'tooltip' => string, 'icon' => mixed ],
'dateCreated' => int,
'dateStart' => int,
'dateEnd' => int,
'status' => string,
'goalId' => int,
]
get_live_goals_count( array $args )
Returns the number of goal completions for a given goal since midnight today.
$count = $goal_stats->get_live_goals_count( [ 'goal_id' => 3 ] );
Hooks reference
burst_on_page_offset
Adjusts the number of seconds added to a visitor's last-seen time before they are considered to have left a page. Used in live traffic and live visitor calculations.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$offset | int | Offset in seconds. Default 60. |
Example:
add_filter( 'burst_on_page_offset', function( $offset ) {
return 90; // consider visitors active for 90 s after their last hit
} );
burst_live_traffic_args
Filters the Query_Data object used to fetch the raw live traffic rows before it is executed.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$qd | Query_Data | Query object for the live traffic query. |
Example:
Show code
add_filter( 'burst_live_traffic_args', function( $qd ) {
$qd->limit = 50; // cap live traffic at 50 rows
return $qd;
} );
burst_possible_filters_with_prefix
Defines the mapping from filter keys to their fully-qualified SQL column references. Extend this to add custom filterable columns.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$filters | array<string, string> | Map of filter key → table.column. |
Changed in v3.3.0: The built-in mappings for browser, browser_id, platform, platform_id, device, device_id, and new_visitor now resolve to sessions.* instead of statistics.*.
Changed in v3.4.2.1: The built-in bounces mapping now resolves to sessions.bounce instead of session_bounces.bounce. Custom code that overrode the mapping to reach into the old session_bounces derived table must be updated.
Example:
Show code
add_filter( 'burst_possible_filters_with_prefix', function( $filters ) {
$filters['my_custom_column'] = 'statistics.my_custom_column';
return $filters;
} );
burst_mappable_filters
Defines which filter keys should be resolved through the lookup tables (i.e. their string value is converted to an integer ID before querying).
Parameters:
| Parameter | Type | Description |
|---|---|---|
$mappable | string[] | List of filter keys that map to lookup table IDs. Default: ['browser', 'browser_version', 'platform', 'device']. |
Example:
Show code
add_filter( 'burst_mappable_filters', function( $mappable ) {
$mappable[] = 'my_lookup_filter';
return $mappable;
} );
burst_datatable_data
Filters the raw data rows returned for a datatable before they are sent to the client.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$data | array | Array of result rows (associative). |
$qd | Query_Data | The query object that produced the data. |
Example:
Show code
add_filter( 'burst_datatable_data', function( $data, $qd ) {
foreach ( $data as &$row ) {
$row['pageviews'] = max( 0, (int) $row['pageviews'] );
}
return $data;
}, 10, 2 );
burst_datatable_pre_data
Short-circuits get_datatables_data() so integrations can supply prebuilt rows without running the default SQL query. Return null to let the default query run, or an array of rows to skip it. When an array is returned, the outer method still applies the configured metric columns and labels.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$data | array|null | Prebuilt rows, or null to fall through to the default query. |
$args | array | The original $args array passed to get_datatables_data(), including the v3.4.2 id key when the call came from a granular datatable endpoint. |
Example:
Show code
add_filter( 'burst_datatable_pre_data', function( $data, $args ) {
if ( ( $args['id'] ?? '' ) !== 'my_custom_datatable' ) {
return $data;
}
return my_plugin_build_rows( $args );
}, 10, 2 );
burst_datatable_metric_allow_list
Added in v3.4.2. Filters the per-datatable metric allow-list returned by App::get_datatable_metric_allow_list(). Use this to register the metrics that a custom datatable id is allowed to expose. Without an entry for an id, REST requests to data/datatable/{id} (or data/ecommerce/datatable/{id}) return 404, and the burst/data ability rejects the id as unknown.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$allow_list | array<string, string[]> | Map of datatable id → list of allowed metric keys. |
Example:
Show code
add_filter( 'burst_datatable_metric_allow_list', function( $allow_list ) {
$allow_list['my_plugin_orders'] = [
'product',
'sales',
'revenue',
'avg_order_value',
];
return $allow_list;
} );
Pair this with a burst_datatable_pre_data callback (matched on $args['id']) if the rows do not come from the default Burst tables.
burst_select_sql_for_metric
Provides SQL for custom metric keys that are not built into the Statistics class. Return false to indicate no SQL is available for the metric.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$metric | string | The metric key that was not matched internally. |
Example:
Show code
add_filter( 'burst_select_sql_for_metric', function( $metric ) {
if ( $metric === 'my_metric' ) {
return 'COUNT(DISTINCT statistics.my_column)';
}
return $metric;
} );
burst_build_where_clause
Filters the assembled WHERE clause string before it is included in the final SQL.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$where | string | Current WHERE fragment (may be empty or start with AND). |
$data | Query_Data | The current query object. |
Example:
Show code
add_filter( 'burst_build_where_clause', function( $where, $data ) {
$where .= " AND statistics.page_type != 'attachment' ";
return $where;
}, 10, 2 );
burst_available_joins
Defines the available JOIN configurations that the query builder can auto-detect and include.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$joins | array | Map of alias → join configuration. |
$data | Query_Data | The current query object. |
Each join configuration has the shape:
Show code
[
'table' => 'burst_sessions', // table name without prefix, or raw subquery SQL
'on' => 'statistics.session_id = sessions.ID',
'type' => 'INNER', // INNER or LEFT
'depends_on' => [], // aliases that must be joined first
]
Changed in v3.3.0: The built-in session_bounces join now reads directly from burst_sessions (SELECT ID as session_id, bounce FROM burst_sessions) instead of aggregating bounce values from burst_statistics. Custom code that relied on the previous subquery shape should be updated accordingly.
:::caution Breaking change
Removed in v3.4.2.1: the built-in session_bounces join has been removed entirely. Bounce metrics are now resolved directly against sessions.bounce and sessions.ID via the always-present sessions join. Custom filters or burst_select_sql_for_metric callbacks that referenced session_bounces.bounce or session_bounces.session_id must be rewritten to use sessions.bounce and sessions.ID. If your integration genuinely needs the old derived-table shape, re-register it explicitly through this filter.
:::
Example:
Show code
add_filter( 'burst_available_joins', function( $joins, $data ) {
$joins['my_table'] = [
'table' => 'my_custom_table',
'on' => 'statistics.ID = my_table.statistic_id',
'type' => 'LEFT',
'depends_on' => [],
];
return $joins;
}, 10, 2 );
burst_allowed_metric_keys
Filters the list of metric keys permitted in strict (non-admin) mode.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$keys | string[] | Allowed metric keys. |
$is_strict | bool | Whether strict mode is active. |
Example:
Show code
add_filter( 'burst_allowed_metric_keys', function( $keys, $is_strict ) {
if ( $is_strict ) {
$keys[] = 'bounce_rate';
}
return $keys;
}, 10, 2 );
burst_allowed_metrics
Filters the full associative array of allowed metrics (key → label) after strict-mode filtering is applied.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$metrics | array<string, string> | Allowed metrics map. |
$is_strict | bool | Whether strict mode is active. |
Example:
Show code
add_filter( 'burst_allowed_metrics', function( $metrics, $is_strict ) {
$metrics['my_metric'] = 'My Metric';
return $metrics;
}, 10, 2 );
burst_statistics_allowed_filter_keys
Filters the list of filter keys accepted by Query_Data.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$keys | string[] | Allowed filter keys. |
$is_strict | bool | Whether strict mode is active. |
Example:
Show code
add_filter( 'burst_statistics_allowed_filter_keys', function( $keys, $is_strict ) {
$keys[] = 'my_custom_filter';
return $keys;
}, 10, 2 );
burst_statistics_allowed_group_by
Filters the list of accepted group_by values.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$group_by | string[] | Allowed group-by fields. |
$is_strict | bool | Whether strict mode is active. |
Example:
Show code
add_filter( 'burst_statistics_allowed_group_by', function( $group_by, $is_strict ) {
$group_by[] = 'my_custom_column';
return $group_by;
}, 10, 2 );
burst_statistics_allowed_order_by
Filters the list of accepted order_by values.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$order_by | string[] | Allowed order-by clauses (e.g. 'pageviews DESC'). |
Example:
Show code
add_filter( 'burst_statistics_allowed_order_by', function( $order_by, $is_strict ) {
$order_by[] = 'my_column DESC';
$order_by[] = 'my_column ASC';
return $order_by;
}, 10, 2 );
burst_allowed_metrics_labels
Filters the full map of metric keys to their translated display labels.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$labels | array<string, string> | Map of metric key → translated label. |
Example:
Show code
add_filter( 'burst_allowed_metrics_labels', function( $labels ) {
$labels['my_metric'] = __( 'My Metric', 'my-plugin' );
return $labels;
} );
burst_allowed_post_types
Filters the list of post types accepted as a valid value for the page_type filter in strict mode.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$post_types | array | Array of public post type slugs. Default: get_post_types( ['public' => true] ). |
Example:
Show code
add_filter( 'burst_allowed_post_types', function( $post_types ) {
$post_types[] = 'my_private_type';
return $post_types;
} );
burst_query_timeout_ms
Added in v3.4.2.1. Filters the foreground (REST/admin) query timeout in milliseconds applied as a MAX_EXECUTION_TIME optimizer hint on the queries executed by get_var(), get_row(), and get_results().
Parameters:
| Parameter | Type | Description |
|---|---|---|
$timeout_ms | int | Resolved foreground timeout (default 30 000 ms, or the value of the burst_query_timeout_ms option when it is positive). |
$qd | Query_Data | The query object that is about to run. |
Example:
Show code
add_filter( 'burst_query_timeout_ms', function( $timeout_ms, $qd ) {
if ( $qd->get_id() === 'top_pages_by_device' ) {
return 5000; // 5 s for this specific query family
}
return $timeout_ms;
}, 10, 2 );
burst_query_timeout_ms_background
Added in v3.4.2.1. Filters the background (cron) query timeout in milliseconds. Used instead of burst_query_timeout_ms whenever wp_doing_cron() is true, so aggregation and backfill jobs get more headroom than dashboard requests.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$timeout_ms | int | Resolved background timeout. Default 900 000 ms (15 min). |
$qd | Query_Data | The query object that is about to run. |
Example:
add_filter( 'burst_query_timeout_ms_background', function( $timeout_ms ) {
return 1800000; // 30 min for background queries on this site
} );
burst_query_results_cache_ttl
Added in v3.4.2.1. Filters the object-cache TTL, in seconds, used by get_row() and get_results() to store query results. Set to 0 to disable result caching for the query.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$ttl | int | Resolved TTL in seconds. Default 30 s; 300 s when the query's date range is longer than 30 days. The burst_query_results_cache_ttl option overrides the default when set to a non-negative integer. |
$qd | Query_Data | The query object whose result is about to be cached. |
Example:
Show code
add_filter( 'burst_query_results_cache_ttl', function( $ttl, $qd ) {
if ( $qd->get_id() === 'live_dashboard_widget' ) {
return 0; // never cache this query
}
return $ttl;
}, 10, 2 );
burst_query_single_flight_enabled
Added in v3.4.2.1. Filters whether single-flight deduplication is active for cached query results. By default it is enabled only when a shared external object cache is in use (wp_using_ext_object_cache() is true), because the local object cache is request-scoped and provides no cross-request coordination.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$enabled | bool | Whether single-flight is enabled for this query. |
$qd | Query_Data | The query object that is about to run. |
Example:
Show code
add_filter( 'burst_query_single_flight_enabled', function( $enabled, $qd ) {
if ( $qd->get_id() === 'expensive_compare_block' ) {
return true; // force single-flight even without an external cache
}
return $enabled;
}, 10, 2 );
burst_query_single_flight_wait_ms
Added in v3.4.2.1. Filters how long, in milliseconds, follower requests wait for the leader query to populate the cache before returning an empty result. Lower values reduce tail latency under contention; higher values increase cache-hit yield.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$wait_ms | int | Resolved wait in milliseconds. Default 1200. |
$qd | Query_Data | The query object that is being followed. |
burst_query_single_flight_lock_ttl
Added in v3.4.2.1. Filters the TTL (in seconds) of the cache lock used to elect a single-flight leader for a given cache key. Defaults to ceil( $timeout_ms / 1000 ) + 2 with a floor of 5 seconds, so the lock always outlives the underlying MAX_EXECUTION_TIME.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$lock_ttl_sec | int | Resolved lock TTL in seconds. |
$qd | Query_Data | The query object that is about to run. |
$timeout_ms | int | Effective query timeout in milliseconds. |
burst_query_timeout_cooldown_ttl
Added in v3.4.2.1. Filters the cooldown TTL (in seconds) written to the object cache after a query times out. While the cooldown marker is present, repeat calls for the same cache key short-circuit and return null/[] without re-running the query, preventing the same expensive query from repeatedly hitting the database after a timeout.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$cooldown_ttl | int | Resolved cooldown TTL in seconds. Defaults to max( 30, ceil( $timeout_ms / 1000 ) ). |
$qd | Query_Data | The query object whose timeout is being recorded. |
$timeout_ms | int | Effective query timeout in milliseconds. |
Example:
add_filter( 'burst_query_timeout_cooldown_ttl', function( $cooldown_ttl ) {
return 120; // 2-minute cooldown after any timeout
} );
burst_object_cache_recommendation_threshold_seconds
Added in v3.4.2.1. Filters the slowest-query threshold (in seconds) that triggers the "persistent object cache recommended" admin task surfaced by should_recommend_object_cache(). The effective threshold is clamped to a 0.1 s floor.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$threshold_seconds | float | Threshold in seconds. Default 10.0. |
Example:
add_filter( 'burst_object_cache_recommendation_threshold_seconds', function() {
return 5.0; // recommend an object cache when any query exceeds 5 s
} );
Custom queries: end-to-end example
The following example queries pageviews and unique visitors grouped by page URL for the current month, filtered to desktop visitors only, and returns the top 10 results.
Show code
use Burst\Admin\Statistics\Query_Data;
$statistics = \Burst\burst_loader()->admin->statistics;
$start = strtotime( 'first day of this month midnight' );
$end = time();
$qd = new Query_Data(
'top_desktop_pages_this_month',
[
'date_start' => $start,
'date_end' => $end,
'select' => [ 'page_url', 'pageviews', 'visitors' ],
'filters' => [ 'device' => 'desktop' ],
'group_by' => 'page_url',
'order_by' => 'pageviews DESC',
'limit' => 10,
]
);
$rows = $statistics->get_results( $qd, ARRAY_A );
foreach ( $rows as $row ) {
printf(
"%s — %s pageviews, %s visitors\n",
$row['page_url'],
$statistics->format_number( (int) $row['pageviews'] ),
$statistics->format_number( (int) $row['visitors'] )
);
}
Adding a custom WHERE clause (admin context only)
Show code
$qd = new Query_Data(
'blog_pageviews_this_month',
[
'date_start' => $start,
'date_end' => $end,
'select' => [ 'pageviews', 'page_url' ],
'group_by' => 'page_url',
'custom_where' => 'AND statistics.page_url LIKE %s',
'custom_where_parameters' => [ '%/blog/%' ],
]
);
$rows = $statistics->get_results( $qd, ARRAY_A );
custom_where and custom_select are only processed for users with admin access (non-strict mode). In strict mode these parameters are silently discarded. All custom SQL is validated against a blocklist of dangerous keywords and patterns before use.