Skip to main content

Statistics API

Burst Statistics exposes a PHP API for querying analytics data programmatically. The entry point is the Statistics class for prebuilt dashboard datasets, and the Statistics_Query builder for custom queries. Statistics_Query constructs and sanitizes SQL through a fluent interface and executes it through the shared Query_Executor.


Architecture overview

Show code
Statistics (class-statistics.php) ← extends Statistics_Data; install + object-cache task
└── Statistics_Data (class-statistics-data.php) ← prebuilt dashboard datasets
└── Goal_Statistics (class-goal-statistics.php) ← goal-specific queries
└── Geo_Statistics (class-geo-statistics.php) ← core country-level geo query layer

Statistics_Query (class-statistics-query.php) ← fluent query builder: create()/select()/date_range()/filters()/fetch()
├── Statistics_Allowlist ← strict-mode metric / filter / group_by / order_by allowlists
├── Statistics_Sanitizer ← input sanitization + custom-SQL safety validation
├── Metric handlers (Metrics\*) ← per-metric SELECT SQL, registered in Metric_Registry
├── FROM strategies (Query_Shapes\*) ← special-case FROM/JOIN shapes, registered in From_Strategy_Registry
└── Query / Query_Executor (Admin\Database\*) ← SQL assembly + caching / single-flight / timeout execution

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

TablePurpose
burst_statisticsRaw page-hit records
burst_sessionsSession records (browser, device, referrer, etc.)
burst_browsersBrowser lookup table
burst_browser_versionsBrowser version lookup table
burst_platformsOS/platform lookup table
burst_devicesDevice type lookup table
burst_referrersReferrer lookup table
burst_locationsCountry/region/city lookup table
burst_goalsGoal definitions
burst_goal_statisticsGoal completion events
burst_known_uidsKnown visitor UIDs with first/last seen timestamps
burst_query_statsQuery execution time statistics

burst_statistics schema

Session-level attributes (browser_id, browser_version_id, platform_id, device_id, first_time_visit, bounce) live on burst_sessions. Custom SQL or filters that reference these attributes must use the sessions.* prefix, not statistics.*.

ColumnTypeDescription
IDint AUTO_INCREMENTPrimary key
page_urlvarchar(191)URL of the visited page
page_idint(11)WordPress post/page ID
page_typevarchar(191)Post type (e.g. post, page). Not-found hits are stored with the value 404.
timeintUnix timestamp of the hit
uidvarchar(64)Visitor unique identifier
time_on_pageintTime spent on page (milliseconds)
parametersTEXTSerialized URL parameters
fragmentvarchar(255)URL fragment
session_idintFK → burst_sessions.ID

burst_sessions schema (selected columns)

ColumnTypeDescription
browser_idintFK → burst_browsers.ID
browser_version_idintFK → burst_browser_versions.ID
platform_idintFK → burst_platforms.ID
device_idintFK → burst_devices.ID
city_codeintFK → burst_locations.city_code
first_time_visittinyint1 if this session is the visitor's first
bouncetinyint1 if the session is a bounce (default 1)

burst_locations schema

The location lookup resolves a session's city_code to country, region and city data. Country-only tracking stores one negative city_code placeholder row per country.

ColumnTypeDescription
city_codeintPrimary key (negative for country-only rows)
cityvarchar(255)City name
state_codevarchar(18)State/region code
statevarchar(255)State/region name
country_codechar(2)ISO 3166-1 alpha-2 country code
continent_codechar(5)Continent code

burst_query_stats schema

ColumnTypeDescription
IDint AUTO_INCREMENTPrimary key
sql_hashvarchar(64)Deterministic hash of the query fingerprint payload
sql_queryTEXTRaw SQL that was executed
avg_execution_timefloatRolling average execution time in seconds
max_execution_timefloatSlowest recorded execution in seconds
min_execution_timefloatFastest recorded execution in seconds
execution_countintTotal number of executions recorded
last_updatedintUnix timestamp of the most recent update
date_range_daysintLength (in days) of the date range used by the query

The burst_query_stats table is pruned to the 100 slowest queries; queries that span more than 365 days are not recorded.


The Statistics_Query class

Statistics_Query is the query builder. Create an instance with a stable query id, chain setter methods to describe the query, then execute it with fetch(), fetch_row(), or fetch_var(). Every value is validated and sanitized before use. The query id generates 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\Statistics_Query;

$qd = Statistics_Query::create( 'top_pages_by_device' )
->date_range( strtotime( '2024-01-01' ), strtotime( '2024-01-31' ) )
->select( [ 'pageviews', 'visitors' ] )
->filters( [ 'device' => 'mobile' ] )
->group_by( 'page_url' )
->order_by( 'pageviews DESC' )
->limit( 10 );

$rows = $qd->fetch( ARRAY_A );

Builder methods

MethodDescription
create( string $id )Static. Returns a new builder. The id is used for deterministic fingerprinting in burst_query_stats.
select( string|string[] $metrics )Metrics to retrieve (see Available metrics)
select_raw( string $sql, array $params = [] )Append a raw SELECT expression with prepared %s/%d/%f parameters (admin-only; ignored in strict mode)
date_range( int $start, int $end )Start/end of the date range (Unix timestamps)
filters( array|string|null $filters )Key/value filters (see Available filters). Accepts an array, a JSON-encoded array string, or an empty value
group_by( string|string[] $columns )Field(s) to group by. Accepts a token, a comma-separated string, a JSON array, or an array
order_by( string|string[] $clause )Order clause(s), e.g. 'pageviews DESC'
limit( int $n )Maximum rows to return (0 = unlimited)
where( string $column, mixed $value, string $operator = '=', string $dtype = '%s' )Add a WHERE condition
where_in( string $column, array $values, string $dtype = '%s' )Add a WHERE IN condition
where_null( string $column ) / where_not_null( string $column )Add IS NULL / IS NOT NULL conditions
where_group( array $conditions )Add a grouped AND/OR WHERE condition
where_raw( string $expr, array $params = [] )Add a raw WHERE expression (admin-only; ignored in strict mode)
having_raw( string $condition )Add a raw HAVING condition (admin-only; ignored in strict mode)
join( string $alias, string $table, string $on, string $type = 'INNER' )Add a JOIN. $table is the table name without prefix, or a raw subquery
with( string ...$keys )Pull in one or more named joins from the registry (e.g. 'sessions', 'goals', 'locations'), resolving their dependencies
set_custom_where( string $sql, array $params )Set a raw WHERE fragment (admin-only; validated and prepared)
set_date_modifiers( array $value )Set date interval settings for period grouping
apply_args( array $args )Bulk-apply select, group_by, order_by, filters, and limit from an array
fetch( string $output_type = ARRAY_A )Execute and return all rows
fetch_row( string $output_type = ARRAY_A )Execute and return the first row, or null
fetch_var()Execute and return the first column of the first row (cache-bypassed)
prepare_sql()Build and return the prepared SQL string without executing it

Bounce exclusion is applied automatically when filters['bounces'] = 'exclude' (or '!'-prefixed); there is no separate setter.

Strict mode

Statistics_Query automatically applies strict mode for non-administrator users. The allowlists are resolved by Statistics_Allowlist. In strict mode:

  • Only a limited set of metrics may be selected (see burst_allowed_metric_keys filter).
  • Only a limited set of filter keys are accepted.
  • select_raw(), where_raw(), having_raw(), and set_custom_where() are 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 = Statistics_Query::create( 'pageviews_excluding_mobile' )
->date_range( $start, $end )
->select( [ 'pageviews', 'page_url' ] )
->filters( [ 'device' => '!mobile' ] ); // exclude mobile

The referrer filter does not include NULL referrer rows when applied in include mode. NULL referrers are included when the filter is applied in exclude mode (!example.com).

404 hits and the status filter

Hits recorded for missing pages are stored in burst_statistics with page_type set to 404. Every Statistics_Query excludes those rows by default, so pageview and visitor metrics never count not-found hits unless a status filter is explicitly set.

The virtual status filter overrides the default and selects rows by HTTP status:

ValueResult
200Real pages only (the default set)
404Not-found hits only
allBoth real pages and 404 hits

status is a virtual filter: there is no status column, so it resolves against page_type (404 matches the stored 404 value, 200 matches every other row). It is accepted in both admin and strict mode.

Show code
$qd = Statistics_Query::create( 'top_404_pages' )
->date_range( $start, $end )
->select( [ 'page_url', 'pageviews' ] )
->filters( [ 'status' => '404' ] ) // return only not-found hits
->group_by( 'page_url' )
->order_by( 'pageviews DESC' );

Query fingerprinting

Every query executed via fetch(), fetch_row(), or fetch_var() 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.

Statistics_Query::get_id()

Returns the sanitized query id supplied to create().

$id = $qd->get_id();
// 'top_pages_by_device'

Statistics_Query::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,
// 'exclude_bounces' => false,
// 'date_range_days' => 30,
// ]

Statistics_Query::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 select():

KeyLabelNotes
pageviewsPageviews
visitorsVisitorsDistinct UIDs
sessionsSessionsDistinct session IDs
first_time_visitorsNew visitors
avg_time_on_pageAvg. time on pageMilliseconds
avg_session_durationAvg. session duration
bounce_rateBounce ratePercentage
bouncesBounced visitors
conversionsGoal completionsRequires goal_id filter
conversion_rateGoal conv. rate
page_urlPage
referrerReferrer
hostDomain
deviceDevice
browserBrowser
platformPlatform
device_idDevice (ID)Resolved from sessions.device_id
browser_idBrowser (ID)Resolved from sessions.browser_id
platform_idPlatform (ID)Resolved from sessions.platform_id
country_codeCountryResolved from locations.country_code
continentContinentResolved from locations.continent_code
stateStateResolved from locations.state
state_codeState codeResolved from locations.state_code
cityCityResolved from locations.city
countCountAlias for pageviews
periodPeriodUsed with group_by: 'period'
timeTimeRaw timestamp column
time_on_pageTime on page
uidUID
page_idPage ID

Session-level metrics (browser_id, browser_version_id, platform_id, device_id, first_time_visit) resolve against sessions.* and automatically add the sessions join via their metric handlers. The non-bounce predicate used when bounces are excluded is COALESCE(sessions.bounce, 0) = 0. Each metric is generated by a handler class registered in Metric_Registry; custom keys are resolved through the burst_select_sql_for_metric filter.

Geo metrics resolve against the burst_locations table and automatically add the locations join. The free plugin ships the GeoIP Country database, so country_code and continent are populated without Pro.

Pro - CreatorAvailable in the Creator tier

state, state_code and city are only populated when the City-level GeoIP database is active.

Geographic Insights

The following metrics are available in Burst Pro:

Pro - CreatorAvailable in the Creator tier

KeyLabel
sourceSource (UTM)
source_categorySource category
mediumMedium (UTM)
campaignCampaign (UTM)
termTerm (UTM)
contentContent (UTM)
parameterURL Parameter
productProduct
salesSales
revenueRevenue
page_valuePage value
sales_conversion_rateSales conv. rate
avg_order_valueAvg. order value
adds_to_cartAdded to cart
entrancesEntrances
exit_rateExit rate
time_per_sessionTime per session

source_category classifies each session's traffic into search, social, referral, aiReferral, paid, email or direct based on the referrer host, UTM campaign parameters and known paid click IDs.

Referral Source Analysis


Available filters

Filters narrow query results. Pass them as an associative array to filters().

KeyDescriptionValues
page_urlFilter by page URLString, supports * wildcard suffix
page_idFilter by WordPress page IDInteger
page_typeFilter by post typePublic post type string
statusFilter by HTTP status, resolved against page_type200, 404, all
referrerFilter by referrer URLString
deviceFilter by device typedesktop, tablet, mobile, other
browserFilter by browser nameString
platformFilter by OS/platformString
device_idFilter by device lookup IDInteger
browser_idFilter by browser lookup IDInteger
platform_idFilter by platform lookup IDInteger
country_codeFilter by country codeISO 3166-1 alpha-2 string, validated against the country list
continent_codeFilter by continent codeString, validated against the continent list
stateFilter by state/regionString
cityFilter by cityString
goal_idFilter by goal ID, or all to aggregate across active goals (admin only)Integer or all
bouncesInclude/exclude bounced sessions (admin only)include, exclude
new_visitorFilter first-time visitors (admin only)include, exclude
entry_exit_pagesFilter entry or exit pages (admin only)entry, exit
hostFilter by domain/host (admin only)String
parameterFilter by URL parameter (admin only)key, key=value

The geo filters (country_code, continent_code, state, city) resolve against the burst_locations table and add the locations join automatically. country_code and continent_code are validated against the country and continent lists; invalid codes are discarded.

Without a status filter every query excludes page_type 404 rows, so 404 hits stay out of the standard datasets. See 404 hits and the status filter.

Pro - CreatorAvailable in the Creator tier

The following filters are available in Burst Pro:

KeyDescriptionValuesNotes
time_per_sessionFilter sessions by total time spent"MIN-MAX" or "MIN-" in secondsE.g. "30-120" for 30–120 s; "600-" for 600 s and above.
Show code
$qd = Statistics_Query::create( 'sessions_by_time_bucket' )
->date_range( $start, $end )
->select( [ 'pageviews', 'sessions' ] )
->filters( [ 'time_per_session' => '30-120' ] ); // sessions with 30–120 s total

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

Statistics extends Statistics_Data and exposes the prebuilt dashboard datasets. 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

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):

PropertyTypeDescription
active_timefloattime + time_on_page / 1000
utm_sourcestringSession referrer
page_urlstringPage URL
timeintHit timestamp
time_on_pageintTime on page (ms)
uidstringVisitor UID
page_idintWordPress page ID
country_codestringVisitor country code
entryboolWhether this is the visitor's entry hit
checkoutboolWhether this page is the checkout page
exitboolWhether the visitor appears to have exited

The country_code column is appended to the live traffic query by core Geo_Statistics through the burst_live_traffic_args filter.

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:

ParameterTypeDescription
$args['date_start']intStart of today (timestamp). Default 0.
$args['date_end']intEnd of today (timestamp). Default 0.

Return shape:

Show code
[
'live' => [ 'value' => string ],
'today' => [ 'value' => string ],
'mostViewed' => [ 'title' => string, 'value' => string ],
'referrer' => [ 'title' => string, 'value' => string ],
'pageviews' => [ 'title' => string, 'value' => string ],
'timeOnPage' => [ 'title' => string, 'value' => string ],
]

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:

ParameterTypeDescription
$args['date_start']intStart timestamp. Default 0.
$args['date_end']intEnd timestamp. Default 0.
$args['metrics']string[]Metrics to chart. Default ['pageviews', 'visitors'].
$args['filters']arrayFilters to apply. Default [].
$args['group_by']stringGrouping interval (auto, hour, day, week, month, year). Default 'auto'.
$args['compare_mode']stringComparison mode: previous_period or year_over_year. Default '' (no comparison).

The return shape uses raw timestamps plus interval and spans_multiple_years metadata so the frontend can format dates locale-aware. The 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' | 'year'
'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,
'metric_key' => string, // the metric this dataset charts
'is_comparison' => bool, // false for current-period series
],
// one entry per metric
],
]

The interval (hour / day / week / month / year) is determined automatically from the date range when group_by is 'auto':

RangeInterval
≤ 2 daysHour
3–48 daysDay
49–364 daysWeek
365–1095 daysMonth
> 1095 daysYear

Interval slots are advanced with real calendar steps, so month, week and year buckets align with the grouped SQL keys across leap years and DST boundaries.

Comparison datasets. When compare_mode is set and exactly one metric is selected, an extra dataset entry is appended with is_comparison set to true. It carries the comparison-period values aligned to the current-period x-axis, plus a comparison_timestamps array (the real comparison-period period-start timestamps) and a compare_mode string, so the frontend can render a dashed comparison line and show the correct dates in the tooltip.

ModeComparison window
previous_periodThe period of equal length immediately before the current range
year_over_yearThe same calendar range shifted back one year

The appended comparison entry has this shape:

Show code
[
'data' => (int|float)[],
'backgroundColor' => string,
'borderColor' => string,
'label' => string,
'fill' => 'false',
'metric_key' => string,
'is_comparison' => true,
'comparison_timestamps' => int[], // actual comparison-period period-start timestamps
'compare_mode' => string, // 'previous_period' | 'year_over_year'
]

No comparison entry is appended when compare_mode is empty or when more than one metric is requested.

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).

The burst/data ability accepts an interval hint (auto, hour, day, week, month, year) for insights requests. The value is forwarded to get_insights_data() as group_by. 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 );

The optional $group_by parameter ('auto', 'hour', 'day', 'week', 'month', 'year') forces a specific interval. The return shape includes a spans_multiple_years boolean; date labels are formatted on the frontend.

Return shape:

KeyTypeDescription
intervalstringhour, day, week, month, or year
interval_in_secondsintInterval length in seconds
nr_of_intervalsintNumber of data points in the range
sql_date_formatstringMySQL DATE_FORMAT() pattern
php_date_formatstringPHP date() pattern for array keys
spans_multiple_yearsbooltrue when date_start and date_end fall in different calendar years

Period counts for week, month and year intervals are calendar-aware: real boundaries are used so every generated interval slot matches a possible grouped SQL key, instead of dividing the raw range by a fixed number of seconds.


get_compare_data( array $args )

Returns current-period and previous-period summary metrics for comparison widgets.

Parameters:

ParameterTypeDescription
$args['date_start']intStart of current period (timestamp).
$args['date_end']intEnd of current period (timestamp).
$args['compare_date_start']int|nullStart of comparison period. Defaults to the equivalent prior period.
$args['compare_date_end']int|nullEnd of comparison period.
$args['filters']arrayFilters 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.

Goal Conversion Tracking

Parameters:

ParameterTypeDescription
$args['date_start']intStart timestamp.
$args['date_end']intEnd timestamp.
$args['filters']arrayFilters, may include goal_id.
$args['compare_date_start']int|nullStart of comparison period.
$args['compare_date_end']int|nullEnd 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:

ParameterTypeDescription
$args['date_start']intStart timestamp. Default 0.
$args['date_end']intEnd timestamp. Default 0.
$args['filters']arrayFilters 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:

ParameterTypeDescription
$args['date_start']intStart timestamp. Default 0.
$args['date_end']intEnd timestamp. Default 0.
$args['filters']arrayFilters 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:

ParameterTypeDescription
$args['date_start']intStart timestamp. Default 0.
$args['date_end']intEnd timestamp. Default 0.
$args['metrics']string[]Metrics to include as columns. Default ['pageviews'].
$args['filters']arrayFilters to apply. Default [].
$args['group_by']stringField to group rows by. Default [].
$args['limit']intMaximum number of rows. Default 0 (unlimited).
$args['id']stringDatatable identifier (e.g. statistics_pages). Passed through to the burst_datatable_pre_data filter so integrations can short-circuit per-datatable, and used as the Statistics_Query id and 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.

After the response is assembled (and after burst_datatable_data has run) it is passed through the burst_datatable_response filter, which receives the full { columns, data, metrics } array plus the original $args. Integrations use this to attach extra top-level keys to a specific datatable response.

The generic data/datatable and data/ecommerce/datatable REST endpoints 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. When no metrics are supplied the full allow-list for that datatable is used. The endpoint also enforces the datatable's required capability (from App::get_datatable_capability_requirements()) and returns 403 Access denied. when the user lacks it. Unknown datatable IDs return 404. When a metric key has no registered label, the column name falls back to a humanized form of the key (e.g. my_custom_metricMy Custom Metric) via ucwords( str_replace( '_', ' ', $metric ) ). 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.

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_sourcesource).

The burst/data ability 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, sources_referrers, outgoing-links, search-terms, and forms. 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()

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_config()

Single source of truth for datatable access control. Returns a map of datatable id → configuration, where each entry has a metrics allow-list and the capability a user must hold to query that datatable. Both get_datatable_metric_allow_list() and get_datatable_capability_requirements() derive from this method, and the result is filterable via burst_datatable_config.

Show code
$config = \Burst\burst_loader()->admin->app->get_datatable_config();
// [
// 'statistics_pages' => [
// 'metrics' => [ 'page_url', 'pageviews', 'visitors', ... ],
// 'capability' => 'view_burst_statistics',
// ],
// 'sources_countries' => [
// 'metrics' => [ 'country_code', 'visitors', 'bounce_rate' ],
// 'capability' => 'view_burst_statistics',
// ],
// 'outgoing-links' => [
// 'metrics' => [ 'url', 'clicks', 'previous_clicks', 'previous_clicks_yoy' ],
// 'capability' => 'view_burst_statistics',
// ],
// 'not-found-pages' => [
// 'metrics' => [ 'page_url', 'hits' ],
// 'capability' => 'view_burst_statistics',
// ],
// // The free Search, Reading Engagement and Search Console features
// // register search-terms, reading-engagement and the read-only
// // search_console datatable (view_burst_statistics).
// // Core registers sources_countries with country-level metrics
// // (view_burst_statistics); Pro extends it with region/city and ecommerce
// // metrics and adds sources_campaigns (view_burst_statistics), sales_products
// // and subscription_products (view_sales_burst_statistics), sources_referrers
// // and forms (view_burst_statistics).
// ]

The granular datatable endpoints enforce the per-datatable capability before running the query; a caller without it receives 403 Access denied.. Ecommerce datatables (sales_products, subscription_products) require view_sales_burst_statistics.


App::get_datatable_capability_requirements()

Returns a map of datatable id → required capability, derived from App::get_datatable_config().

Show code
$caps = \Burst\burst_loader()->admin->app->get_datatable_capability_requirements();
// [
// 'statistics_pages' => 'view_burst_statistics',
// 'sources_countries' => 'view_burst_statistics',
// 'sales_products' => 'view_sales_burst_statistics',
// // …
// ]

App::get_datatable_metric_allow_list()

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 and is derived from App::get_datatable_config().

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', 'source_category', 'visitors', 'sessions', ... ],
// 'sources_countries' => [ 'country_code', 'visitors', 'bounce_rate' ],
// 'dummy_data' => [ 'page_url', 'pageviews', 'visitors', ... ],
// 'outgoing-links' => [ 'url', 'clicks', 'previous_clicks', 'previous_clicks_yoy' ],
// 'not-found-pages' => [ 'page_url', 'hits' ],
// 'search_console' => [ 'query', 'clicks', 'impressions', 'click_through_rate', 'position' ],
// // Pro extends sources_countries and adds: sources_campaigns, sales_products, subscription_products, sources_referrers, forms
// ]

Register metrics for a custom datatable id through burst_datatable_config. Without an entry the REST handler responds with 404 Unknown datatable endpoint. and the abilities API returns burst_abilities_unknown_datatable.


The search_console datatable

When the Google Search Console integration is connected, Burst exposes the stored search-query rows as the read-only search_console datatable. The rows are supplied through the burst_datatable_pre_data filter for the search_console id, read from the burst_search_terms table for the matched property over the requested date range, so the default SQL query never runs. The datatable requires the view_burst_statistics capability.

MetricDescription
querySearch query
clicksClicks in the date range
impressionsImpressions in the date range
click_through_rateClick-through rate as a percentage (0–100)
positionImpression-weighted average position

Clicks and impressions are summed across days; click_through_rate is recomputed from the summed totals and position is the impression-weighted average, matching how Search Console aggregates rather than averaging daily ratios. When no property has been matched for the site, the datatable returns an empty data array.


The not-found-pages datatable

Burst records hits to missing pages with page_type 404 and exposes the top not-found URLs as the not-found-pages datatable. The rows are supplied through the burst_datatable_pre_data filter for the not-found-pages id, so the default SQL query never runs. The datatable requires the view_burst_statistics capability and is mapped to the engagement tab.

MetricDescription
page_urlURL of the missing page
hitsNumber of 404 hits in the date range

Rows are ordered by hits descending. The same aggregation is available outside the datatable through the not_found_pages data type on the burst_get_data filter, which returns an unlimited array of { page_url, hits } rows for the requested date range:

Show code
$rows = apply_filters(
'burst_get_data',
[],
'not_found_pages',
[
'date_start' => strtotime( '2024-01-01' ),
'date_end' => strtotime( '2024-01-31' ),
]
);
// [
// [ 'page_url' => '/old-post', 'hits' => 128 ],
// [ 'page_url' => '/typo-link', 'hits' => 44 ],
// // …
// ]

Both paths accept the standard filters argument and force the status filter to 404 internally, so they always return not-found hits regardless of the caller's filters.


Executing queries

Queries are executed by calling the fetch methods directly on the Statistics_Query object. Each method assembles the SQL, applies the per-query MAX_EXECUTION_TIME optimizer hint, and runs it through the shared Query_Executor, which handles object-cache result caching, single-flight coordination, timeout cooldown, and slow-query telemetry.

Statistics_Query::fetch( string $output_type = ARRAY_A )

Executes the query and returns all matching rows.

Show code
$rows = Statistics_Query::create( 'monthly_pageviews' )
->date_range( $start, $end )
->select( [ 'page_url', 'pageviews' ] )
->group_by( 'page_url' )
->fetch( ARRAY_A );

$output_type accepts OBJECT, ARRAY_A, or ARRAY_N. 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 [] immediately to prevent thundering-herd fan-out. When the query times out, fetch() logs the timeout, writes a short-lived cooldown marker for the cache key, and returns [] rather than surfacing the error.

Statistics_Query::fetch_row( string $output_type = ARRAY_A )

Executes the query and returns the first matching row, or null.

Show code
$row = Statistics_Query::create( 'monthly_totals' )
->date_range( $start, $end )
->select( [ 'pageviews', 'visitors' ] )
->fetch_row( 'OBJECT' );

$output_type accepts OBJECT, ARRAY_A, or ARRAY_N. Results are cached, single-flighted, and timeout-protected with the same semantics as fetch(). If the query times out, fetch_row() logs the timeout, writes a cooldown marker, and returns null.

Statistics_Query::fetch_var()

Executes the query and returns a single scalar value (the first column of the first row). This call is cache-bypassed — result caching and single-flight are disabled — so it always hits the database. When MySQL aborts the query because the timeout was exceeded, fetch_var() logs the timeout and returns null.

Show code
$val = Statistics_Query::create( 'monthly_pageviews' )
->date_range( $start, $end )
->select( [ 'pageviews' ] )
->fetch_var();

Statistics_Query::prepare_sql()

Builds and returns the prepared SQL string from the query object without executing it. Useful for debugging.

Show code
$sql = Statistics_Query::create( 'debug_query' )
->date_range( $start, $end )
->select( [ 'pageviews' ] )
->prepare_sql();
error_log( $sql );
caution

The returned SQL is built for direct use with $wpdb and already includes the MAX_EXECUTION_TIME optimizer hint applied at execution time. Do not attempt to re-prepare it.


Query timeouts

Every executed query is prefixed with a MAX_EXECUTION_TIME optimizer hint. Background cron requests use a longer ceiling than foreground dashboard requests so that aggregation and backfill jobs are not killed mid-run.

ContextDefaultFilter
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.


should_recommend_object_cache()

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 false immediately when wp_using_ext_object_cache() is true.
  • Returns false when the burst_query_stats table does not yet exist.
  • Otherwise reads MAX(max_execution_time) from burst_query_stats and returns true when it meets or exceeds the threshold returned by the burst_object_cache_recommendation_threshold_seconds filter (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.


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:

ParameterTypeDescription
$args['goal_id']int|stringGoal ID to query, or 'all' to aggregate across every active goal. Default 0.
$args['date_start']intStart timestamp. Default 0.
$args['date_end']intEnd timestamp. Default 0.

When goal_id is 'all', the counts aggregate distinct converting visitors across all goals with status active, the date range starts at the earliest active goal's creation date, and the returned goalId is the string 'all'.

Return shape (abbreviated):

Show code
[
'today' => [ 'value' => int, 'tooltip' => string ],
'total' => [ 'value' => int, 'tooltip' => string ],
'topPerformer' => [ 'title' => string, 'value' => int ],
'conversionMetric' => [ 'title' => string, 'value' => int, 'tooltip' => string, 'icon' => string ],
'conversionPercentage'=> [ 'title' => string, 'value' => int, 'tooltip' => string ],
'bestDevice' => [ 'title' => string, 'value' => int, 'icon' => mixed ],
'dateCreated' => int,
'dateStart' => int,
'dateEnd' => int,
'status' => string,
'goalId' => int|string,
]

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 ] );

Pass 'all' as the goal_id to count distinct converting visitors across every active goal since midnight today; the method returns 0 when there are no active goals.

$count = $goal_stats->get_live_goals_count( [ 'goal_id' => 'all' ] );

Pro statistics queries

Pro - CreatorAvailable in the Creator tier

The following queries are available in Burst Pro. They back the data/page-parameters and data/page-parameter-counts endpoint types and the outgoing-links datatable.

get_page_parameters_data( array $args )

Returns the parameter=value variations recorded for a single page URL within a date range, with pageview and visitor counts. Backs the progressively loaded parameter-variations accordion under each pages row.

Parameters:

ParameterTypeDescription
$args['date_start']intStart timestamp. Default 0.
$args['date_end']intEnd timestamp. Default 0.
$args['page_url']stringPage URL to query. Required.

Returns a datatable-shaped array with columns (parameter, pageviews, visitors) and data. When any required input (date_start, date_end or page_url) is missing it returns the columns with an empty data array. Results are capped at 200 rows.

get_page_parameter_counts( array $args )

Returns a flat map of [ page_url => parameter_variation_count ] for the date range. Used to render the "n variations" badge and the expandable-row indicator on the pages datatable without inflating that query.

Parameters:

ParameterTypeDescription
$args['date_start']intStart timestamp. Default 0.
$args['date_end']intEnd timestamp. Default 0.

Returns an associative array keyed by page URL; an empty array when the date range is missing.

When the track_external_links option is enabled, the outgoing-links datatable exposes click counts for tracked external links across three windows. Rows are supplied through the burst_datatable_pre_data filter for the outgoing-links id, and the response is augmented with a scraping_progress integer (0–100) via burst_datatable_response while Burst scrapes the site for external links.

MetricDescription
urlExternal link URL
clicksClicks in the current period
previous_clicksClicks in the previous equal-length period
previous_clicks_yoyClicks in the same window one year earlier

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:

ParameterTypeDescription
$offsetintOffset 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 Statistics_Query object used to fetch the raw live traffic rows before it is executed. Core Geo_Statistics uses this hook to append the visitor country_code to the live traffic query.

Parameters:

ParameterTypeDescription
$qdStatistics_QueryQuery 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_statistics_query

Action that fires just before a Statistics_Query is executed (fetch(), fetch_row(), fetch_var(), or prepare_sql()). Use it to mutate the query object — add named joins, custom WHERE conditions, or extra SELECT expressions — based on the selected metrics or filters. This replaces the removed burst_build_where_clause filter.

Callbacks should append to any existing custom WHERE (read it with get_custom_where()) rather than overwrite it, so multiple handlers — for example core geo clauses and Pro campaign/ecommerce clauses — compose regardless of registration order.

Parameters:

ParameterTypeDescription
$qdStatistics_QueryThe query object that is about to run.

Example:

Show code
add_action( 'burst_statistics_query', function( $qd ) {
// Admin context only: custom WHERE is ignored in strict mode.
if ( in_array( 'country_code', $qd->get_select(), true ) ) {
$where = $qd->get_custom_where() . ' AND locations.country_code IS NOT NULL';
$qd->set_custom_where( $where, [] );
}
} );

burst_datatable_data

Filters the raw data rows returned for a datatable before they are sent to the client.

Parameters:

ParameterTypeDescription
$dataarrayArray of result rows (associative).
$qdStatistics_QueryThe 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_response

Filters the fully assembled datatable response ({ columns, data, metrics }) returned by get_datatables_data(), after burst_datatable_data has run. Use it to attach extra top-level keys to a specific datatable response (for example a progress indicator).

Parameters:

ParameterTypeDescription
$responsearrayThe assembled response with columns, data and metrics keys.
$argsarrayThe original $args passed to get_datatables_data(), including id.

Example:

Show code
add_filter( 'burst_datatable_response', function( $response, $args ) {
if ( ( $args['id'] ?? '' ) !== 'my_custom_datatable' ) {
return $response;
}
$response['my_meta'] = 'value';
return $response;
}, 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:

ParameterTypeDescription
$dataarray|nullPrebuilt rows, or null to fall through to the default query.
$argsarrayThe original $args array passed to get_datatables_data(), including the 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_config

Filters the per-datatable configuration returned by App::get_datatable_config(). Each entry pairs a metric allow-list with the capability required to query that datatable. Use this to register a custom datatable id, its allowed metrics, and its capability gate. 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.

Existing entries can also be extended: Pro merges extra metrics into the core sources_countries config (region, city and ecommerce metrics) through this filter rather than redefining the entry. The core Search Console feature uses it to register the read-only search_console datatable, and the core Errors feature registers the not-found-pages datatable the same way.

Parameters:

ParameterTypeDescription
$configarray<string, array{metrics: string[], capability: string}>Map of datatable id → { metrics, capability }.

Example:

Show code
add_filter( 'burst_datatable_config', function( $config ) {
$config['my_plugin_orders'] = [
'metrics' => [ 'product', 'sales', 'revenue', 'avg_order_value' ],
'capability' => 'view_sales_burst_statistics',
];
return $config;
} );

Pair this with a burst_datatable_pre_data callback (matched on $args['id']) if the rows do not come from the default Burst tables.

@deprecated Use burst_datatable_config instead of burst_datatable_metric_allow_list. Metric allow-lists are now derived from the config returned by burst_datatable_config, which also carries the per-datatable capability requirement.


burst_select_sql_for_metric

Provides SQL for custom metric keys that are not built into Metric_Registry. The filter is chain-safe: the first argument is the SQL accumulated by earlier handlers (initially an empty string), so multiple registrants (core geo, Pro campaigns/ecommerce, your own) compose without clobbering each other. Return the incoming $sql unchanged when your callback does not own the metric. The query object is passed as a third argument so callbacks can pull in required joins via $query_data->with().

Parameters:

ParameterTypeDescription
$sqlstringSQL accumulated by earlier handlers (empty string when none has resolved the metric).
$metricstringThe metric key that was not matched internally.
$query_dataStatistics_QueryThe current query object.

Example:

Show code
add_filter( 'burst_select_sql_for_metric', function( $sql, $metric, $query_data ) {
if ( $sql !== '' ) {
return $sql; // already resolved by another handler
}
if ( $metric === 'my_metric' ) {
return 'COUNT(DISTINCT statistics.my_column)';
}
return $sql;
}, 10, 3 );

burst_default_metric

Filters the fallback metric used when an invalid or unknown metric key is supplied to select().

Parameters:

ParameterTypeDescription
$metricstringThe fallback metric key. Default 'pageviews'.

Example:

add_filter( 'burst_default_metric', function( $metric ) {
return 'visitors';
} );

burst_allowed_metric_keys

Filters the list of metric keys permitted in strict (non-admin) mode.

Parameters:

ParameterTypeDescription
$keysstring[]Allowed metric keys.
$is_strictboolWhether 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. Core Geo_Statistics uses this hook to register the geo metrics (country_code, state, state_code, city, continent) in non-strict mode.

Parameters:

ParameterTypeDescription
$metricsarray<string, string>Allowed metrics map.
$is_strictboolWhether 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 Statistics_Query.

Parameters:

ParameterTypeDescription
$keysstring[]Allowed filter keys.
$is_strictboolWhether 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:

ParameterTypeDescription
$group_bystring[]Allowed group-by fields.
$is_strictboolWhether 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:

ParameterTypeDescription
$order_bystring[]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:

ParameterTypeDescription
$labelsarray<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:

ParameterTypeDescription
$post_typesarrayArray 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_possible_filters_with_prefix

@deprecated Filter key → column mappings are now registered with Filter_Registry::register( $key, 'table.column' ) (free-tier mappings in Metric_Bootstrap and core Geo_Statistics, Pro mappings in the Pro Statistics class). Register a custom filterable column with Filter_Registry::register( 'my_custom_filter', 'statistics.my_custom_column' ). A filter whose column references a joined table alias automatically pulls in that named join.


burst_mappable_filters

@deprecated The set of filters resolved through lookup tables is now fixed (browser, browser_version, platform, device). To add a custom filterable column, register it with Filter_Registry::register() and reference its qualified SQL column directly.


burst_build_where_clause

@deprecated Use the burst_statistics_query action instead. Mutate the Statistics_Query object directly (add WHERE conditions via where(), where_group(), or set_custom_where()) from a callback hooked to burst_statistics_query.


burst_available_joins

@deprecated JOIN definitions are now registered with Join_Registry::register( $alias, [ 'table' => ..., 'on' => ..., 'type' => ..., 'depends_on' => [] ] ) for static joins, or Join_Registry::register_dynamic( $alias, $factory ) for joins whose SQL depends on the query (e.g. date-filtered subqueries). The locations join (used by the geo metrics and filters) is registered by core Geo_Statistics. Pull a registered join into a query with $qd->with( $alias ), which resolves any depends_on chain. 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
]

There is no built-in session_bounces join. Bounce metrics resolve directly against sessions.bounce and sessions.ID via the always-present sessions join.


burst_query_timeout_ms

Filters the foreground (REST/admin) query timeout in milliseconds applied as a MAX_EXECUTION_TIME optimizer hint on the queries executed by fetch(), fetch_row(), and fetch_var().

Parameters:

ParameterTypeDescription
$timeout_msintResolved foreground timeout (default 30 000 ms, or the value of the burst_query_timeout_ms option when it is positive).
$qdStatistics_QueryThe 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

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:

ParameterTypeDescription
$timeout_msintResolved background timeout. Default 900 000 ms (15 min).
$qdStatistics_QueryThe 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

Filters the object-cache TTL, in seconds, used by fetch() and fetch_row() to store query results. Set to 0 to disable result caching for the query.

Parameters:

ParameterTypeDescription
$ttlintResolved 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.
$qdStatistics_QueryThe 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

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:

ParameterTypeDescription
$enabledboolWhether single-flight is enabled for this query.
$qdStatistics_QueryThe 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

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:

ParameterTypeDescription
$wait_msintResolved wait in milliseconds. Default 1200.
$qdStatistics_QueryThe query object that is being followed.

burst_query_single_flight_lock_ttl

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:

ParameterTypeDescription
$lock_ttl_secintResolved lock TTL in seconds.
$qdStatistics_QueryThe query object that is about to run.
$timeout_msintEffective query timeout in milliseconds.

burst_query_timeout_cooldown_ttl

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:

ParameterTypeDescription
$cooldown_ttlintResolved cooldown TTL in seconds. Defaults to max( 30, ceil( $timeout_ms / 1000 ) ).
$qdStatistics_QueryThe query object whose timeout is being recorded.
$timeout_msintEffective 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

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:

ParameterTypeDescription
$threshold_secondsfloatThreshold 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\Statistics_Query;

$statistics = \Burst\burst_loader()->admin->statistics;

$start = strtotime( 'first day of this month midnight' );
$end = time();

$qd = Statistics_Query::create( 'top_desktop_pages_this_month' )
->date_range( $start, $end )
->select( [ 'page_url', 'pageviews', 'visitors' ] )
->filters( [ 'device' => 'desktop' ] )
->group_by( 'page_url' )
->order_by( 'pageviews DESC' )
->limit( 10 );

$rows = $qd->fetch( 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 = Statistics_Query::create( 'blog_pageviews_this_month' )
->date_range( $start, $end )
->select( [ 'pageviews', 'page_url' ] )
->group_by( 'page_url' )
->where( 'statistics.page_url', '%/blog/%', 'LIKE' );

$rows = $qd->fetch( ARRAY_A );

For a raw fragment, use set_custom_where() with prepared parameters:

$qd->set_custom_where( 'AND statistics.page_url LIKE %s', [ '%/blog/%' ] );
caution

set_custom_where(), select_raw(), where_raw(), and having_raw() are only processed for users with admin access (non-strict mode). In strict mode these are silently discarded. All custom SQL is validated against a blocklist of dangerous keywords and patterns before use.