API & MCP
Tool reference
Every Sonar MCP tool, its arguments, and an example of what it returns.
Each tool is called with tools/call, passing its name and arguments. See The Sonar MCP server for the request format. The examples below come from Sonar's demo sites; long lists are shortened with ….
Arguments marked optional can be left out. period is always one of 24h, 7d, 30d, 90d or all.
list_sites
The websites you can see, with the IDs the other tools need. No arguments.
[
{ "id": 49, "name": "Awesome Agency", "domain": "https://awesome-agency.example" },
{ "id": 50, "name": "Harbour Dental", "domain": "https://harbourdental.example" },
{ "id": 51, "name": "Northwind Coffee", "domain": "https://northwindcoffee.example" }
]
get_overview
Headline numbers for a period, the same numbers for the period before it, and the top 5 pages, sources and campaigns.
| Argument | Type | |
|---|---|---|
site_id |
integer | From list_sites |
period |
string | Optional. Defaults to 7d |
{
"period": "last 30 days",
"current": { "visitors": 91, "pageviews": 175, "conversions": 0, "avg_session_seconds": 33 },
"previous_period": { "visitors": 77, "pageviews": 190, "conversions": 0, "avg_session_seconds": 31 },
"top_pages": [
{ "path": "/", "visitors": 37 },
{ "path": "/new-patients/", "visitors": 25 },
…
],
"top_sources": [
{ "source": "direct", "visitors": 65 },
{ "source": "Google", "visitors": 21 },
…
],
"top_campaigns": []
}
conversions counts goal completions. previous_period is null for all.
top_pages
The pages with the most visitors. Query strings are ignored, so /fees/?ref=ad counts as /fees/.
| Argument | Type | |
|---|---|---|
site_id |
integer | |
period |
string | Optional. Defaults to 7d |
limit |
integer | Optional. 1 to 50, defaults to 10 |
[
{ "path": "/", "visitors": 37 },
{ "path": "/new-patients/", "visitors": 25 },
{ "path": "/fees/", "visitors": 24 },
{ "path": "/blog/", "visitors": 11 },
{ "path": "/blog/how-often-should-you-see-a-hygienist/", "visitors": 7 }
]
traffic_sources
Where visitors came from: search engines, other websites, or direct. Takes the same arguments as top_pages.
[
{ "source": "direct", "visitors": 65 },
{ "source": "Google", "visitors": 21 },
{ "source": "harbourdental.example", "visitors": 15 },
…
]
campaigns
Traffic from links with UTM tags, grouped by campaign, source or medium.
| Argument | Type | |
|---|---|---|
site_id |
integer | |
group_by |
string | Optional. campaign (default), source or medium |
from, to |
string | Optional. Dates as YYYY-MM-DD; to is included. Leave both out for all time |
{
"totals": { "pageviews": 510, "visitors": 410 },
"rows": [
{ "value": "web-design-search", "pageviews": 284, "visitors": 236 },
{ "value": "web-development-search", "pageviews": 81, "visitors": 65 },
{ "value": "autumn-newsletter", "pageviews": 78, "visitors": 64 },
…
]
}
list_sessions
Visits, newest first, 20 at a time.
| Argument | Type | |
|---|---|---|
site_id |
integer | |
period |
string | Optional. Defaults to 7d |
outcome |
string | Optional. converted, chatted or browsed (neither) |
source |
string | Optional. Only visits from this source, such as google |
device |
string | Optional. desktop, mobile or tablet |
landing |
string | Optional. Only visits that started on this path, such as /pricing/ |
before |
string | Optional. next_before from the previous result, for the next 20 |
{
"sessions": [
{
"id": "En6zYlyfOsqSAgGR-cdJ…",
"visitor_name": null,
"city": null,
"country": "United Kingdom",
"country_code": "GB",
"browser": "Chrome",
"os": "Mac OS",
"device_type": "desktop",
"source": "Direct",
"landing_path": "/",
"started_at": "2026-10-17T21:54:41.64+00:00",
"last_activity": "2026-10-17T21:56:25.872+00:00",
"duration_seconds": 104,
"page_count": 4,
"converted": true,
"chatted": false,
"is_bot": false
},
…
],
"total": 12,
"counts": { "live": 0, "chats": 0, "bots_hidden": 0 },
"next_before": null
}
total is the number of matching visits. When next_before isn't null, pass it as before to get the next page. visitor_name is set when someone on your team has named the visitor.
get_session
One visit in full: each page in order with the time spent on it, conversions, interactions such as form enquiries, and the chat transcript if there was one.
| Argument | Type | |
|---|---|---|
site_id |
integer | |
session_id |
string | An id from list_sessions, or a session_id from interactions, for the same site |
{
"id": "fFbBpqy0pVfZbnHJxl8b…",
"source": "Direct",
"landing_path": "/",
"duration_seconds": 104,
"page_count": 4,
"converted": true,
…
"pages": [
{ "path": "/", "title": "Awesome Agency | Websites, brands and apps that work", "entered_at": "2026-10-17T21:54:41.64+00:00", "seconds_on_page": 4 },
{ "path": "/contact/", "title": "Contact us | Awesome Agency", "entered_at": "2026-10-17T21:54:45.372+00:00", "seconds_on_page": 18 },
{ "path": "/thank-you/", "title": "Thank you | Awesome Agency", "entered_at": "2026-10-17T21:55:03.482+00:00", "seconds_on_page": 82 },
…
],
"events": [
{ "type": "conversion", "at": "2026-10-17T21:55:03.482+00:00", "label": "Converted: Thank you page" },
{ "type": "interaction", "at": "2026-10-17T21:56:23.372+00:00", "label": "Form submission: Contact us" },
…
],
"chat": null
}
The visit also has every field from list_sessions. seconds_on_page is null for the last page, because time is measured up to the next page. When the visitor chatted, chat has the messages in order, each with its role, content and created_at.
interactions
Form enquiries, orders and video responses sent to Sonar, with which sources and landing pages brought them. See Interactions for how to connect your forms and shop.
| Argument | Type | |
|---|---|---|
site_id |
integer | |
period |
string | Optional. Defaults to 30d |
{
"period": "90d",
"totals": {
"interactions": 19,
"linked": 18,
"sessions": 1659,
"rate": 0.0108,
"by_type": [{ "type": "form_submission", "n": 19, "linked": 18 }],
"order_value": null,
"currency": null
},
"by_source": [
{ "label": "Google", "sessions": 395, "interactions": 10, "converting": 10, "rate": 0.0253 },
{ "label": "Direct", "sessions": 1070, "interactions": 5, "converting": 5, "rate": 0.0047 },
…
],
"by_landing": [ … ],
"items": [
{
"id": 170,
"type": "form_submission",
"source": "gravity_forms",
"title": "Contact us",
"occurred_at": "2026-10-17T21:56:23.372+00:00",
"value": null,
"currency": null,
"match_method": "page_id",
"entry_page": "/",
"visit_source": "Direct",
"campaign": null,
"session_id": "-X2uBwsbg48fGYISWxKe…"
},
…
]
}
linkedcounts interactions matched to a visit.convertingcounts visits with at least one interaction, andrateisconvertingdivided bysessions(visits).by_sourceandby_landingare the top 10 traffic sources and landing pages.order_valueis the total of orders, when they're all in one currency.itemsare the latest 10 interactions. Pass an item'ssession_idtoget_sessionto see the visit behind it.
ask_sonar
Any question in plain English, answered from the site's data. Use it for anything the other tools don't cover.
| Argument | Type | |
|---|---|---|
site_id |
integer | |
question |
string | Up to 1,000 characters |
period |
string | Optional. Defaults to 7d. A period named in the question, such as "last month", takes priority |
{
"question": "Which day of the week gets the most visitors?",
"period": "last 30 days",
"rows": [
{ "day_of_week": "Wednesday", "visitors": 19, "pageviews": 40 },
{ "day_of_week": "Friday", "visitors": 18, "pageviews": 32 },
{ "day_of_week": "Tuesday", "visitors": 17, "pageviews": 21 },
…
]
}
The columns depend on the question. Up to 200 rows are returned, with a note when there were more. If Sonar can't answer a question, the result has isError: true; try rephrasing it.