WordPress’s Abilities API gives every plugin one way to describe what it can do, so other code can find and call it without learning each plugin’s own API. Vireo registers its reports as abilities. Your plugin, a theme, a WP-CLI script or an AI tool can call them and get the same numbers as the dashboard.
You need WordPress 6.9 or newer, where the Abilities API is part of core.
What’s there
Vireo’s abilities are in the vireo-analytics category and named vireo-analytics/get-.... The free plugin registers 22; Vireo Pro adds its own, including get-revenue-forecast and get-subscriptions for stores. Every one is read-only. The API reference lists them all with their inputs.
List them from PHP:
foreach ( wp_get_abilities() as $ability ) {
if ( 0 === strpos( $ability->get_name(), 'vireo-analytics/' ) ) {
echo $ability->get_name() . ': ' . $ability->get_label() . "\n";
}
}
Or over REST, with an application password:
curl -u "your-username:xxxx xxxx xxxx xxxx xxxx xxxx" \
"https://example.com/wp-json/wp-abilities/v1/abilities?category=vireo-analytics&per_page=50"
Each entry includes the ability’s input schema, so you can see what it accepts.
Run one from PHP
$ability = wp_get_ability( 'vireo-analytics/get-top-pages' );
$result = $ability ? $ability->execute( array( 'limit' => 2 ) ) : null;
if ( is_wp_error( $result ) ) {
error_log( $result->get_error_message() );
} else {
foreach ( $result['rows'] as $page ) {
printf( "%d %s\n", $page['pageviews'], $page['title'] );
}
}
execute() returns the data as a PHP array, or a WP_Error. As JSON:
{"rows":[
{"post_id":4,"path":"/2026/10/11/how-to-pack-for-a-month/","title":"How to pack for a month","pageviews":6,"visitors":6},
{"post_id":6,"path":"/2026/10/11/pricing/","title":"Pricing","pageviews":3,"visitors":3}
]}
What comes back
Every ability returns an object, never a bare list:
- Abilities that return a list put it under
rows: daily stats, hourly stats, top pages, top referrers, traffic sources, countries, events, vitals, annotations and goals. Nothing to report is{"rows": []}. - The others return their own object, such as
get-totalswithpageviews,visitors,days,avg_daily, and from 0.13.0avg_time_on_page_seconds,timed_viewsandbounce_rate_percent.
The shape is the same whether you call the ability from PHP, over REST or through MCP. The API reference lists what each one returns.
Run one over REST
Read-only abilities run with GET at /wp-json/wp-abilities/v1/abilities/{name}/run. The name keeps its slash. Pass input as query parameters in the input[...] form:
curl -g -u "your-username:xxxx xxxx xxxx xxxx xxxx xxxx" \
"https://example.com/wp-json/wp-abilities/v1/abilities/vireo-analytics/get-totals/run?input[start]=2026-09-12&input[end]=2026-10-11"
{"pageviews":15,"visitors":15,"days":30,"avg_daily":1,"avg_time_on_page_seconds":null,"timed_views":0,"bounce_rate_percent":null}
curl’s -g stops it reading the square brackets as a pattern.
With no input at all, an ability uses its defaults:
curl -u "your-username:xxxx xxxx xxxx xxxx xxxx xxxx" \
"https://example.com/wp-json/wp-abilities/v1/abilities/vireo-analytics/get-traffic-sources/run"
What doesn’t work:
POSTreturns 405, “Read-only abilities require GET method.”?input={"limit":1}as one JSON string returns 400, “input is not of type object”. Useinput[limit]=1.- An input the ability doesn’t declare returns 400, for example “foo is not a valid property of Object”.
Inputs and defaults
startandend, asYYYY-MM-DD, default to the last 30 days in your site’s timezone. Unlike the/stats/REST routes, they’re optional.limitruns from 1 to 100. Each ability has its own default.- Some abilities need one more input, such as
post_idforget-post-statsorhostforget-referrer-detail. The reference marks which. - Anything else is refused. An undeclared input key makes
execute()return aWP_Errorwith the codeability_invalid_input, rather than being ignored.
Permissions
An ability runs as the current user, and Vireo checks the same capability as the dashboard, view_vireo_analytics. In PHP, that’s whoever is logged in for the request. For a cron job or WP-CLI, there’s no user unless you set one:
wp_set_current_user( $user_id_with_dashboard_access );
Without the capability, execute() returns a WP_Error with the code ability_invalid_permissions, and REST answers 401 or 403.
Pro’s abilities also need an active Vireo Pro license, and the store abilities need a supported store to be active: WooCommerce, Easy Digital Downloads, FluentCart, Freemius, Paid Memberships Pro, Restrict Content, MemberPress, LifterLMS, GiveWP or SureCart. Where those aren’t met, the abilities aren’t registered, so wp_get_ability() returns null. The one exception is get-goals, which the free plugin registers everywhere and refuses without Pro.
To narrow access further, for example to a list of users, use the vireo_abilities_permission_callback filter. It can refuse a call; it can’t grant one to a user without the capability.
Strings from visitors
Paths, search terms, referrers and event values can be set by anyone who sends a request to your site. Before any ability returns them, Vireo strips control characters, line breaks and brackets, and cuts long values short. They’re safe to show to an AI model as data, but treat them as untrusted text in your own HTML: escape them as you would any user input.