API & MCP
The Sonar MCP server
How the Sonar MCP server works, how to authenticate with an API key, and what requests and responses look like.
Sonar's data is available through an MCP server. MCP (the Model Context Protocol) is the standard way AI assistants such as Claude, Cursor and ChatGPT connect to other tools, and it's also a simple JSON API you can call from your own code.
Use it to:
- Ask an AI assistant about your analytics. See Connecting AI assistants.
- Pull Sonar data into your own scripts and reports, such as a weekly Slack message or a client dashboard. See Code examples.
Everything is read-only: the server can't change your sites, settings or data.
Before you start
- You need a Pro or Agency plan.
- Create an API key in Sonar: open the avatar menu, then Settings → API keys. The key starts with
sonar_and is shown only once, so copy it straight away. - A key can read every site you can see in Sonar, and nothing else. Revoke it from the same page if it's ever shared by mistake; it stops working straight away.
The endpoint
POST https://sonar-app.co.uk/api/mcp
Every request needs these headers:
| Header | Value |
|---|---|
Authorization |
Bearer sonar_YOUR_KEY |
Content-Type |
application/json |
Accept |
application/json, text/event-stream |
The Accept header must list both types, as the MCP standard requires; without it the server answers 406.
Requests
Requests use JSON-RPC 2.0. There are two methods you'll use:
tools/listreturns every tool, with a description and a JSON Schema for its arguments. See the Tool reference.tools/callruns one tool.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_overview",
"arguments": { "site_id": 50, "period": "30d" }
}
}
The server keeps no state between requests, so you don't need to send initialize first or keep a session open. MCP client libraries still send it, and that's fine.
Responses
A tool's result is JSON, returned as text inside result.content, as MCP requires:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\"period\": \"last 30 days\", \"current\": {\"visitors\": 91, \"pageviews\": 175, \"conversions\": 0, \"avg_session_seconds\": 33}, ...}"
}
]
}
}
Parse result.content[0].text as JSON to get the data. With jq:
jq -r '.result.content[0].text | fromjson'
Errors
| What went wrong | HTTP status | What you get |
|---|---|---|
| No key, or not a Sonar key | 401 |
error.message: "Missing or malformed API key…" |
| Key revoked or unknown | 401 |
error.message: "This API key is not valid…" |
| Plan doesn't include the API | 403 |
error.message: "API access is part of the Pro and Agency plans…" |
| A tool couldn't run, such as a site you can't see or an invalid argument | 200 |
result.isError is true, and result.content[0].text explains why |
Good to know
- Periods are
24h,7d,30d,90dorall(all time), and always end now.campaignsalso takes exactfromandtodates. - Bots are left out, as they are in the app, so the numbers match your dashboard.
- Visitors' IP addresses are never returned. Visit IDs (
idandsession_id) are encrypted: use them withget_sessionas they are, on the same site. - Ask Sonar answers take a few seconds, because Sonar writes and runs a query for each question. Other tools usually answer in under a second.
- Results are capped: 50 rows for top lists, 100 campaign rows, 20 visits per page, and 200 rows for Ask Sonar.