If you run analytics for several clients, you can have their sites report into one WordPress install as connected sites. With Vireo Pro, one AI assistant connected to that install can then answer about all of them: which clients are up or down this month, and what’s behind it on any one site.
Who it’s for
Agencies and freelancers with client sites that report into one Vireo install, usually on the 25-site or unlimited plan. Each connected site uses one site from your plan, so the plan size decides how many clients one install can hold. See How many sites.
The client sites don’t need WordPress. Any site you can add a script tag to can be a connected site: a static site, a landing page, a docs site.
If you already manage your clients’ WordPress sites with MainWP, you don’t need connected sites for them. Run Vireo Pro on the MainWP dashboard and the assistant reads every child site through MainWP. See With a MainWP dashboard.
Before you start
- On your agency’s WordPress site, install Vireo Pro and activate the license.
- Add each client site under Vireo Analytics, Settings, Tracking, Connected sites, and paste its tag into that site. See Connected sites.
- Connect your AI assistant to this install, once. Every client site is reachable through that one connection.
Ask across every site
Start broad:
Which of my client sites are up or down this month?
The assistant calls get-sites-overview. It returns every site, busiest first, with this period’s pageviews and visitors next to the previous period of the same length, the change in percent, and the last day each site recorded a visit:
{
"start": "2026-09-12", "end": "2026-10-11",
"prior_start": "2026-08-13", "prior_end": "2026-09-11",
"sites": [
{"id": 0, "name": "Agency site", "this_site": true, "last_data": "2026-10-11", "pageviews": 3000, "prior_pageviews": 2384, "pageviews_change_pct": 25.8},
{"id": 2, "name": "Client B", "this_site": false, "last_data": "2026-10-11", "pageviews": 1500, "prior_pageviews": 745, "pageviews_change_pct": 101.3},
{"id": 1, "name": "Client A", "this_site": false, "last_data": "2026-10-11", "pageviews": 300, "prior_pageviews": 596, "pageviews_change_pct": -49.7},
{"id": 3, "name": "Client C", "this_site": false, "last_data": "2026-09-11", "pageviews": 0, "prior_pageviews": 894, "pageviews_change_pct": -100}
]
}
That’s trimmed for space: each site also has its url, visitors, prior_visitors and visitors_change_pct.
A last_data date well in the past, like Client C’s, usually means the site’s tag was removed or broke. That’s worth asking about directly:
Has any client site stopped recording visits?
Drill into one site
Every site has an id: 0 is the install you connected to, and connected sites are numbered from 1. Ask about a client by name and the assistant looks the id up with get-sites, then passes it as site to the report it needs:
What were Client B’s top pages last week?
That’s get-top-pages with "site": 2. The same works for totals, traffic sources, referrers, countries and the other reports listed below. Leave site out, or pass 0, for the install you connected to.
With a MainWP dashboard
Vireo Pro has MainWP support built in. Install Pro on your MainWP dashboard site and activate the license, and Pro adds itself to MainWP’s Extensions page. Each child site needs MainWP Child and Vireo Analytics, and there’s nothing to set up on the child beyond that.
If you used the separate Vireo Analytics for MainWP plugin, deactivate it. Pro does the same job, and until the old plugin is off, Pro leaves MainWP alone and shows a notice saying so.
On the dashboard you get:
- A Vireo Analytics widget on MainWP’s Overview, listing each child’s visitors, pageviews and change over 7 or 30 days, from the last sync.
- A Vireo Analytics page for each child, with its visitors, pageviews, top pages and top referrers for the last 7 or 30 days, fetched live.
- Tokens for MainWP’s Pro Reports:
[vireo.visitors],[vireo.pageviews],[vireo.visitors.change],[vireo.pageviews.change],[vireo.top.pages]and[vireo.top.referrers]. Each one is filled in for the report’s site and dates. A child without Vireo gets a short reason instead of a raw token. - The assistant tools on this page, across every child.
Vireo’s page under MainWP’s Extensions lists every child with the Vireo Analytics version it runs, and an Open button for its Vireo page. A child that can’t report says why instead of showing a version:
- not synced yet
- Vireo Analytics isn’t installed, or is installed but not active
- its Vireo Analytics is too old: the widget, page and tokens need 0.9.0 or later
- it doesn’t share its stats with the dashboard
- you don’t have access to it in MainWP
- it didn’t answer
Ask about MainWP children
Connect your AI assistant to the MainWP dashboard. get-sites lists every child you can see in MainWP, with ids like mainwp:12 (the MainWP site id) and "source": "mainwp", and the assistant passes that id as site:
Which of my MainWP sites are down this month?
get-sites-overview fetches every child’s totals at once. A child that doesn’t answer within 10 seconds, has no Vireo, runs a Vireo too old to answer, or has turned reporting off is listed under unreachable with a reason, instead of dropping out of the numbers without a word.
A child answers every tool its own Vireo has. With only the free plugin, that’s the free reports. A child with Vireo Pro active and licensed answers Pro’s reports too, such as campaigns, engagement and revenue, because Pro records them on that site.
A child with only the free plugin needs Vireo Analytics 0.12.4 or later. Older versions can’t read a request that carries a date range or any other input, and refuse it.
Which tools take a site
Over MCP and the Abilities API, these accept site:
get-totals, get-daily-stats, get-hourly-stats, get-changes, get-highlights, get-top-pages, get-top-referrers, get-traffic-sources, get-referrer-detail, get-ai-referrals, get-countries, get-devices, get-events, get-event-detail, get-404s, get-bot-stats, get-vitals, get-annotations.
get-sites returns the same list under accepts_site, so the assistant doesn’t have to guess.
These don’t:
- Pro’s own reports, such as campaigns, engagement, site search, funnels, live visitors and revenue. Pro records them for the WordPress site it’s installed on, not for connected sites, so there’s nothing to report for a client site.
get-post-statsandget-year-review, which are built from this site’s WordPress posts.get-search-consoleandget-goals, which are kept for this site only.
That list is for connected sites. A MainWP child runs the tool on its own Vireo, so it answers whatever that site has installed.
Ask one of these about a client site and you get an error rather than this site’s numbers under the client’s name. A Pro report says it’s “recorded for this WordPress site only” and points at the tools that do take site. The others refuse site as an unknown input.
When a site can’t be read
A request for another site is refused, with no numbers, rather than answered with the wrong site’s data:
| Situation | Error | Over REST |
|---|---|---|
| No connected site has that id | vireo_site_unknown: “No connected site has the id 9. Call get-sites for the list.” |
404 |
A tool that doesn’t take site, such as one of Pro’s own reports |
vireo_site_unsupported |
400 |
| Pro isn’t active with a license | vireo_site_pro |
403 |
| The user can’t read that site’s numbers | vireo_site_forbidden |
403 |
A MainWP child adds its own refusals. Each message names the site, such as mainwp:12, and says what went wrong:
| Situation | Error | Over REST |
|---|---|---|
| The child is beyond your plan’s sites | vireo_site_over_plan |
403 |
| The child didn’t answer, or answered with something unexpected | vireo_remote_unreachable |
502 |
| The child’s Vireo is too old to answer tool calls | vireo_remote_old |
501 |
| The child refused: its MainWP administrator can’t read its analytics, or it has turned reporting to MainWP off | vireo_remote_denied |
403 |
The free tools outside the per-site list refuse site as an unknown input instead (ability_invalid_input, 400). Every answer, from this site or a client’s, is flattened before it reaches the assistant: line breaks, control and invisible characters, and brackets that could pass text off as an instruction are taken out of anything a visitor could have written.
How many sites
Plans are priced by sites, and a connected site counts as one. Each WordPress install can connect one site, plus one for every site your license has unused. A 25-site plan active on your agency’s site alone can hold 25 client sites there; an unlimited plan, 100. get-sites reports the install’s limit and how many sites are connected (count).
MainWP children count too. Connected sites use the allowance first, and the children take what’s left, in the order they were added to MainWP. A child beyond the allowance still appears in get-sites, with "over_plan": true, so the assistant can tell you, but its numbers can’t be read until you add sites to your plan. get-sites also reports how many children it found as remote_count.
If the allowance later shrinks, for example because you activated the license on another WordPress site, the client sites already connected keep counting and stay readable. You just can’t add more until you add sites to your plan.
Over REST
The same data is available to scripts with an application password:
curl -u "$VIREO_AUTH" "https://example.com/wp-json/vireo-analytics/v1/stats/sites"
curl -u "$VIREO_AUTH" "https://example.com/wp-json/vireo-analytics/v1/stats/sites-overview?start=2026-09-12&end=2026-10-11"
For one client site, run an ability through the Abilities API with site in its input:
curl -g -u "$VIREO_AUTH" \
"https://example.com/wp-json/wp-abilities/v1/abilities/vireo-analytics/get-totals/run?input[site]=2"
{"pageviews":1500,"visitors":1200,"days":30,"avg_daily":50}
The free plugin’s own /stats/... routes, such as /stats/totals, don’t take site. They always answer for the install you’re calling, and ignore a site parameter.