curl --request POST \
--url https://app.goosybear.ai/api/v1/tools/analytics.query_metrics \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"metric_keys": [],
"period": "<string>",
"from": "<string>",
"to": "<string>",
"compare": true,
"by": "platform",
"site": "<string>",
"site_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"page_path": "<string>",
"workspace": "<string>"
}
'import requests
url = "https://app.goosybear.ai/api/v1/tools/analytics.query_metrics"
payload = {
"metric_keys": [],
"period": "<string>",
"from": "<string>",
"to": "<string>",
"compare": True,
"by": "platform",
"site": "<string>",
"site_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"page_path": "<string>",
"workspace": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
metric_keys: [],
period: '<string>',
from: '<string>',
to: '<string>',
compare: true,
by: 'platform',
site: '<string>',
site_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
page_path: '<string>',
workspace: '<string>'
})
};
fetch('https://app.goosybear.ai/api/v1/tools/analytics.query_metrics', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.goosybear.ai/api/v1/tools/analytics.query_metrics",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'metric_keys' => [
],
'period' => '<string>',
'from' => '<string>',
'to' => '<string>',
'compare' => true,
'by' => 'platform',
'site' => '<string>',
'site_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'page_path' => '<string>',
'workspace' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.goosybear.ai/api/v1/tools/analytics.query_metrics"
payload := strings.NewReader("{\n \"metric_keys\": [],\n \"period\": \"<string>\",\n \"from\": \"<string>\",\n \"to\": \"<string>\",\n \"compare\": true,\n \"by\": \"platform\",\n \"site\": \"<string>\",\n \"site_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"page_path\": \"<string>\",\n \"workspace\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.goosybear.ai/api/v1/tools/analytics.query_metrics")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"metric_keys\": [],\n \"period\": \"<string>\",\n \"from\": \"<string>\",\n \"to\": \"<string>\",\n \"compare\": true,\n \"by\": \"platform\",\n \"site\": \"<string>\",\n \"site_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"page_path\": \"<string>\",\n \"workspace\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.goosybear.ai/api/v1/tools/analytics.query_metrics")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"metric_keys\": [],\n \"period\": \"<string>\",\n \"from\": \"<string>\",\n \"to\": \"<string>\",\n \"compare\": true,\n \"by\": \"platform\",\n \"site\": \"<string>\",\n \"site_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"page_path\": \"<string>\",\n \"workspace\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"ok": true,
"window": {
"from": "<string>",
"to": "<string>",
"label": "<string>"
},
"previous_window": {
"from": "<string>",
"to": "<string>",
"label": "<string>"
},
"grain": "<string>",
"definition_version": 123,
"min_delta_periods": 123,
"metrics": [
{
"key": "<string>",
"label": "<string>",
"module": "<string>",
"formula": "<string>",
"source": "<string>",
"provenance": "<string>",
"aggregation": "<string>",
"definition_version": 123,
"is_directional": true,
"honesty_note": "<string>",
"floor": {
"kind": "<string>",
"threshold": 123
},
"recompute_required": true,
"sample": {
"denominator": 123,
"low_sample": true,
"note": "<string>"
},
"current": {
"value": 123,
"display": "<string>",
"row_count": 1,
"currency_code": "<string>",
"store": "metric_rollups",
"suppressed": "<string>"
},
"previous": {
"value": 123,
"display": "<string>",
"row_count": 1,
"currency_code": "<string>",
"store": "metric_rollups",
"suppressed": "<string>"
},
"change": {
"absolute": 123,
"percent": 123
},
"change_unavailable": "<string>",
"state": "reported",
"by_platform": [
{
"platform": "<string>",
"label": "<string>",
"state": "measured",
"value": 123,
"display": "<string>",
"detail": "<string>"
}
]
}
],
"all_empty": true,
"answer_note": "<string>",
"split_note": "<string>",
"site_filter": {
"site_id": "<string>",
"page_path": "<string>"
},
"workspace": "<string>",
"working_in": {
"label": "<string>",
"note": "<string>",
"workspace": "<string>",
"source": "call-override"
}
}{
"ok": false,
"code": "<string>",
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
},
"retry_after_seconds": 123
}{
"ok": false,
"code": "<string>",
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
},
"retry_after_seconds": 123
}{
"ok": false,
"code": "<string>",
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
},
"retry_after_seconds": 123
}{
"ok": false,
"code": "<string>",
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
},
"retry_after_seconds": 123
}{
"ok": false,
"code": "<string>",
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
},
"retry_after_seconds": 123
}Call analytics.query_metrics
‘how many visitors did we get last week?’, ‘what’s our conversion rate?’ — read this workspace’s analytics numbers over a window: the same figures, from the same reads, the analytics pages show. Name dictionary keys (analytics.list_available_metrics lists them) and a preset period or an explicit from/to; set compare to also read the preceding window. Every row carries a state: empty is a final answer, read_failed means nothing is known, and suppressed means a small-count privacy floor withholds the figure. It changes nothing. Not for which metrics exist or report here — that is analytics.list_available_metrics.
Every result names the workspace it ran in. Say which workspace the answer is about. When the account has more than one workspace and none is selected, this tool refuses with workspace_ambiguous and lists the choices — offer them, never pick one.
curl --request POST \
--url https://app.goosybear.ai/api/v1/tools/analytics.query_metrics \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"metric_keys": [],
"period": "<string>",
"from": "<string>",
"to": "<string>",
"compare": true,
"by": "platform",
"site": "<string>",
"site_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"page_path": "<string>",
"workspace": "<string>"
}
'import requests
url = "https://app.goosybear.ai/api/v1/tools/analytics.query_metrics"
payload = {
"metric_keys": [],
"period": "<string>",
"from": "<string>",
"to": "<string>",
"compare": True,
"by": "platform",
"site": "<string>",
"site_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"page_path": "<string>",
"workspace": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
metric_keys: [],
period: '<string>',
from: '<string>',
to: '<string>',
compare: true,
by: 'platform',
site: '<string>',
site_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
page_path: '<string>',
workspace: '<string>'
})
};
fetch('https://app.goosybear.ai/api/v1/tools/analytics.query_metrics', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.goosybear.ai/api/v1/tools/analytics.query_metrics",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'metric_keys' => [
],
'period' => '<string>',
'from' => '<string>',
'to' => '<string>',
'compare' => true,
'by' => 'platform',
'site' => '<string>',
'site_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'page_path' => '<string>',
'workspace' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.goosybear.ai/api/v1/tools/analytics.query_metrics"
payload := strings.NewReader("{\n \"metric_keys\": [],\n \"period\": \"<string>\",\n \"from\": \"<string>\",\n \"to\": \"<string>\",\n \"compare\": true,\n \"by\": \"platform\",\n \"site\": \"<string>\",\n \"site_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"page_path\": \"<string>\",\n \"workspace\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.goosybear.ai/api/v1/tools/analytics.query_metrics")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"metric_keys\": [],\n \"period\": \"<string>\",\n \"from\": \"<string>\",\n \"to\": \"<string>\",\n \"compare\": true,\n \"by\": \"platform\",\n \"site\": \"<string>\",\n \"site_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"page_path\": \"<string>\",\n \"workspace\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.goosybear.ai/api/v1/tools/analytics.query_metrics")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"metric_keys\": [],\n \"period\": \"<string>\",\n \"from\": \"<string>\",\n \"to\": \"<string>\",\n \"compare\": true,\n \"by\": \"platform\",\n \"site\": \"<string>\",\n \"site_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"page_path\": \"<string>\",\n \"workspace\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"ok": true,
"window": {
"from": "<string>",
"to": "<string>",
"label": "<string>"
},
"previous_window": {
"from": "<string>",
"to": "<string>",
"label": "<string>"
},
"grain": "<string>",
"definition_version": 123,
"min_delta_periods": 123,
"metrics": [
{
"key": "<string>",
"label": "<string>",
"module": "<string>",
"formula": "<string>",
"source": "<string>",
"provenance": "<string>",
"aggregation": "<string>",
"definition_version": 123,
"is_directional": true,
"honesty_note": "<string>",
"floor": {
"kind": "<string>",
"threshold": 123
},
"recompute_required": true,
"sample": {
"denominator": 123,
"low_sample": true,
"note": "<string>"
},
"current": {
"value": 123,
"display": "<string>",
"row_count": 1,
"currency_code": "<string>",
"store": "metric_rollups",
"suppressed": "<string>"
},
"previous": {
"value": 123,
"display": "<string>",
"row_count": 1,
"currency_code": "<string>",
"store": "metric_rollups",
"suppressed": "<string>"
},
"change": {
"absolute": 123,
"percent": 123
},
"change_unavailable": "<string>",
"state": "reported",
"by_platform": [
{
"platform": "<string>",
"label": "<string>",
"state": "measured",
"value": 123,
"display": "<string>",
"detail": "<string>"
}
]
}
],
"all_empty": true,
"answer_note": "<string>",
"split_note": "<string>",
"site_filter": {
"site_id": "<string>",
"page_path": "<string>"
},
"workspace": "<string>",
"working_in": {
"label": "<string>",
"note": "<string>",
"workspace": "<string>",
"source": "call-override"
}
}{
"ok": false,
"code": "<string>",
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
},
"retry_after_seconds": 123
}{
"ok": false,
"code": "<string>",
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
},
"retry_after_seconds": 123
}{
"ok": false,
"code": "<string>",
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
},
"retry_after_seconds": 123
}{
"ok": false,
"code": "<string>",
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
},
"retry_after_seconds": 123
}{
"ok": false,
"code": "<string>",
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
},
"retry_after_seconds": 123
}Authorizations
An API key minted at Settings › API & MCP. Send it as Authorization: Bearer <key>. A key carries its holder's own permissions, resolved on every call — revoking a membership closes the key's reach immediately. Keep it in an environment variable (GOOSY_API_KEY), never in a committed file.
Headers
Body
Dictionary metric keys to read. Use analytics.list_available_metrics to discover them.
1 - 12 elementsrev_leads, rev_booked_calls, rev_calls_showed, rev_show_rate, rev_sales_won, rev_revenue_won, rev_revenue_by_source, rev_open_pipeline_value, email_sent, email_delivered, email_delivery_rate, email_opened, email_open_rate, email_clicked, email_click_rate, email_replied, email_complaint_rate, email_bounce_rate, list_size, list_new_subscribers, list_unsubscribe_rate, ads_spend, ads_impressions, ads_clicks, ads_ctr, ads_cpc, ads_cpm, ads_reach, ads_frequency, ads_conversions, ads_cost_per_lead, ads_cost_per_booked_call, ads_attributed_revenue, vid_views, vid_watch_time_minutes, vid_avg_view_duration, vid_impressions, vid_impressions_ctr, vid_subscribers_gained, vid_net_subscribers, vid_breakout_score, soc_posts_published, soc_impressions, soc_engagements, soc_engagement_rate, soc_meta_impressions, soc_meta_engagements, soc_meta_engagement_rate, soc_fb_impressions, soc_fb_engagements, soc_fb_engagement_rate, soc_ig_impressions, soc_ig_reach, soc_ig_engagements, soc_ig_engagement_rate, soc_ig_profile_visits, soc_ig_new_follows, sys_produced, hyros_attributed_revenue, hyros_attributed_leads, web_sessions, web_active_users, web_conversions, web_revenue, web_search_impressions, web_search_clicks, web_search_ctr, web_search_position, site_views, site_visitors, site_sessions, site_conversions, site_conversion_rate, site_scroll_50, site_cta_clicks, site_form_starts, site_video_plays, site_video_completion, site_consent_shown, site_consent_accept_rate, sales_open_pipeline, sales_open_deals, sales_weighted_forecast, sales_won_value, sales_won_deals, sales_win_rate, sales_days_to_close, sales_journey_funnel The window ending at the start of today (today is excluded), as the span the member names, counted in whole days: 14d for two weeks, 30d for 30 days, 3d for the last three days. Three ids are the analytics page's own presets and read whole COMPLETE periods: 7d = the last 7 complete days, 28d = the last 4 complete weeks, 90d = the last 3 complete calendar months (92 days on a May-Aug read, not 90); use one of them only when the member's span is that one. Any other count, 1d to 366d, reads that many complete days. Omit when supplying from/to, which is for named calendar dates ('in March').
^[1-9][0-9]{0,2}d$Granularity the stored rows are read at. LEAVE THIS UNSET unless you have a specific reason: day is where the analytics store is written, and asking for a coarser grain reads a different set of rows that is usually empty. A window is summed from day rows regardless of how long it is, so a month-long window does NOT need grain=month.
hour, day, week, month, quarter, year, lifetime Leave it out unless the member asks for ad numbers by platform. Splits each ad sum (ads_spend, ads_impressions, ads_clicks, ads_conversions) by ad platform — Meta, Google, LinkedIn, TikTok, Snapchat — each with its state: measured (a figure), quiet (connected, nothing ran), unread (we do not read that platform's spend yet) or not_connected. A platform with no figure is never a zero. Other keys come back unsplit. The split is read on a signed-in member's session only: a call made with an API key answers the unsplit figures and a split_note saying so.
platform Leave it out unless the member names one of the brand's sites: a read without site is the whole brand's, and no value of it means every site. Narrows site_* metrics to that site, by its name as the member says it, like Riverside Bakery, or by its id. The name is looked up in this workspace, so no page.list call is needed first. Add page_path for one page of it; a path (it starts with /) is always page_path, never site. Only with site_* keys.
1 - 200Leave it out unless the member names one page: narrows site_* metrics to that page by its path, like /pricing, and / is the home page alone, never every page. That path on every site unless site is also given. Only with site_* keys.
1 - 1024^\/.*Response
The call was admitted and dispatched. ok says whether the tool succeeded — a refusal the tool itself produced is still a 200, exactly as it is a successful JSON-RPC result over MCP.
- Option 1
- Option 2
The tool ran and answered.