curl --request POST \
--url https://api.closebot.com/analytics/query \
--header 'Content-Type: application/json' \
--header 'X-CB-KEY: <api-key>' \
--data '
{
"metric": "<string>",
"start": "2023-11-07T05:31:56Z",
"end": "2023-11-07T05:31:56Z",
"resolution": "<string>",
"groupBy": [
"<string>"
],
"timeZone": "<string>",
"timeBasis": "<string>",
"filters": {
"sourceIds": [
"<string>"
],
"dimensions": [
{
"dimension": "<string>",
"operator": "<string>",
"values": [
"<string>"
]
}
]
}
}
'import requests
url = "https://api.closebot.com/analytics/query"
payload = {
"metric": "<string>",
"start": "2023-11-07T05:31:56Z",
"end": "2023-11-07T05:31:56Z",
"resolution": "<string>",
"groupBy": ["<string>"],
"timeZone": "<string>",
"timeBasis": "<string>",
"filters": {
"sourceIds": ["<string>"],
"dimensions": [
{
"dimension": "<string>",
"operator": "<string>",
"values": ["<string>"]
}
]
}
}
headers = {
"X-CB-KEY": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-CB-KEY': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
metric: '<string>',
start: '2023-11-07T05:31:56Z',
end: '2023-11-07T05:31:56Z',
resolution: '<string>',
groupBy: ['<string>'],
timeZone: '<string>',
timeBasis: '<string>',
filters: {
sourceIds: ['<string>'],
dimensions: [{dimension: '<string>', operator: '<string>', values: ['<string>']}]
}
})
};
fetch('https://api.closebot.com/analytics/query', 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://api.closebot.com/analytics/query",
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' => '<string>',
'start' => '2023-11-07T05:31:56Z',
'end' => '2023-11-07T05:31:56Z',
'resolution' => '<string>',
'groupBy' => [
'<string>'
],
'timeZone' => '<string>',
'timeBasis' => '<string>',
'filters' => [
'sourceIds' => [
'<string>'
],
'dimensions' => [
[
'dimension' => '<string>',
'operator' => '<string>',
'values' => [
'<string>'
]
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-CB-KEY: <api-key>"
],
]);
$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://api.closebot.com/analytics/query"
payload := strings.NewReader("{\n \"metric\": \"<string>\",\n \"start\": \"2023-11-07T05:31:56Z\",\n \"end\": \"2023-11-07T05:31:56Z\",\n \"resolution\": \"<string>\",\n \"groupBy\": [\n \"<string>\"\n ],\n \"timeZone\": \"<string>\",\n \"timeBasis\": \"<string>\",\n \"filters\": {\n \"sourceIds\": [\n \"<string>\"\n ],\n \"dimensions\": [\n {\n \"dimension\": \"<string>\",\n \"operator\": \"<string>\",\n \"values\": [\n \"<string>\"\n ]\n }\n ]\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-CB-KEY", "<api-key>")
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://api.closebot.com/analytics/query")
.header("X-CB-KEY", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"metric\": \"<string>\",\n \"start\": \"2023-11-07T05:31:56Z\",\n \"end\": \"2023-11-07T05:31:56Z\",\n \"resolution\": \"<string>\",\n \"groupBy\": [\n \"<string>\"\n ],\n \"timeZone\": \"<string>\",\n \"timeBasis\": \"<string>\",\n \"filters\": {\n \"sourceIds\": [\n \"<string>\"\n ],\n \"dimensions\": [\n {\n \"dimension\": \"<string>\",\n \"operator\": \"<string>\",\n \"values\": [\n \"<string>\"\n ]\n }\n ]\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.closebot.com/analytics/query")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-CB-KEY"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"metric\": \"<string>\",\n \"start\": \"2023-11-07T05:31:56Z\",\n \"end\": \"2023-11-07T05:31:56Z\",\n \"resolution\": \"<string>\",\n \"groupBy\": [\n \"<string>\"\n ],\n \"timeZone\": \"<string>\",\n \"timeBasis\": \"<string>\",\n \"filters\": {\n \"sourceIds\": [\n \"<string>\"\n ],\n \"dimensions\": [\n {\n \"dimension\": \"<string>\",\n \"operator\": \"<string>\",\n \"values\": [\n \"<string>\"\n ]\n }\n ]\n }\n}"
response = http.request(request)
puts response.read_body{
"points": [
{
"timestamp": "2023-11-07T05:31:56Z",
"value": 123,
"group": {}
}
],
"sourcesMissingTimeZone": [
{
"sourceId": "<string>",
"sourceName": "<string>"
}
]
}Run an analytics query for the active agency.
curl --request POST \
--url https://api.closebot.com/analytics/query \
--header 'Content-Type: application/json' \
--header 'X-CB-KEY: <api-key>' \
--data '
{
"metric": "<string>",
"start": "2023-11-07T05:31:56Z",
"end": "2023-11-07T05:31:56Z",
"resolution": "<string>",
"groupBy": [
"<string>"
],
"timeZone": "<string>",
"timeBasis": "<string>",
"filters": {
"sourceIds": [
"<string>"
],
"dimensions": [
{
"dimension": "<string>",
"operator": "<string>",
"values": [
"<string>"
]
}
]
}
}
'import requests
url = "https://api.closebot.com/analytics/query"
payload = {
"metric": "<string>",
"start": "2023-11-07T05:31:56Z",
"end": "2023-11-07T05:31:56Z",
"resolution": "<string>",
"groupBy": ["<string>"],
"timeZone": "<string>",
"timeBasis": "<string>",
"filters": {
"sourceIds": ["<string>"],
"dimensions": [
{
"dimension": "<string>",
"operator": "<string>",
"values": ["<string>"]
}
]
}
}
headers = {
"X-CB-KEY": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-CB-KEY': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
metric: '<string>',
start: '2023-11-07T05:31:56Z',
end: '2023-11-07T05:31:56Z',
resolution: '<string>',
groupBy: ['<string>'],
timeZone: '<string>',
timeBasis: '<string>',
filters: {
sourceIds: ['<string>'],
dimensions: [{dimension: '<string>', operator: '<string>', values: ['<string>']}]
}
})
};
fetch('https://api.closebot.com/analytics/query', 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://api.closebot.com/analytics/query",
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' => '<string>',
'start' => '2023-11-07T05:31:56Z',
'end' => '2023-11-07T05:31:56Z',
'resolution' => '<string>',
'groupBy' => [
'<string>'
],
'timeZone' => '<string>',
'timeBasis' => '<string>',
'filters' => [
'sourceIds' => [
'<string>'
],
'dimensions' => [
[
'dimension' => '<string>',
'operator' => '<string>',
'values' => [
'<string>'
]
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-CB-KEY: <api-key>"
],
]);
$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://api.closebot.com/analytics/query"
payload := strings.NewReader("{\n \"metric\": \"<string>\",\n \"start\": \"2023-11-07T05:31:56Z\",\n \"end\": \"2023-11-07T05:31:56Z\",\n \"resolution\": \"<string>\",\n \"groupBy\": [\n \"<string>\"\n ],\n \"timeZone\": \"<string>\",\n \"timeBasis\": \"<string>\",\n \"filters\": {\n \"sourceIds\": [\n \"<string>\"\n ],\n \"dimensions\": [\n {\n \"dimension\": \"<string>\",\n \"operator\": \"<string>\",\n \"values\": [\n \"<string>\"\n ]\n }\n ]\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-CB-KEY", "<api-key>")
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://api.closebot.com/analytics/query")
.header("X-CB-KEY", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"metric\": \"<string>\",\n \"start\": \"2023-11-07T05:31:56Z\",\n \"end\": \"2023-11-07T05:31:56Z\",\n \"resolution\": \"<string>\",\n \"groupBy\": [\n \"<string>\"\n ],\n \"timeZone\": \"<string>\",\n \"timeBasis\": \"<string>\",\n \"filters\": {\n \"sourceIds\": [\n \"<string>\"\n ],\n \"dimensions\": [\n {\n \"dimension\": \"<string>\",\n \"operator\": \"<string>\",\n \"values\": [\n \"<string>\"\n ]\n }\n ]\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.closebot.com/analytics/query")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-CB-KEY"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"metric\": \"<string>\",\n \"start\": \"2023-11-07T05:31:56Z\",\n \"end\": \"2023-11-07T05:31:56Z\",\n \"resolution\": \"<string>\",\n \"groupBy\": [\n \"<string>\"\n ],\n \"timeZone\": \"<string>\",\n \"timeBasis\": \"<string>\",\n \"filters\": {\n \"sourceIds\": [\n \"<string>\"\n ],\n \"dimensions\": [\n {\n \"dimension\": \"<string>\",\n \"operator\": \"<string>\",\n \"values\": [\n \"<string>\"\n ]\n }\n ]\n }\n}"
response = http.request(request)
puts response.read_body{
"points": [
{
"timestamp": "2023-11-07T05:31:56Z",
"value": 123,
"group": {}
}
],
"sourcesMissingTimeZone": [
{
"sourceId": "<string>",
"sourceName": "<string>"
}
]
}Authorizations
CloseBot API Key Authorization
Body
Metric to query. Call GET /analytics/metrics for the full catalog of valid keys, or pass "formula" together with Closebot.Api.Controllers.AnalyticsController.AnalyticsQueryRequest.Formula to compute a derived metric.
Inclusive start. A UTC instant in utc mode; a source-local wall-clock value in sourceLocal mode. Must align to a 15-minute boundary.
Exclusive end. A UTC instant in utc mode; a source-local wall-clock value in sourceLocal mode. Must align to a 15-minute boundary.
Bucket size. Time buckets: 15m, hour, day, week, month (multiples of the 15-minute base). Week buckets start Monday 00:00 of the active time basis. Non-time: source (one point per source name over the whole range, not grouped by time), total (the whole range as one point), hourOfWeek (every bucket folded onto one week of day-of-week x hour-of-day cells).
Show child attributes
Show child attributes
Optional dimension to split the series by — source, channel, bot, persona, or the metric's own detail dimensions. At most one: a chart segments by a single thing, and allowing two would multiply the series out past anything readable.
IANA time zone id (e.g. "America/Denver") used by the hourOfWeek fold when Closebot.Api.Controllers.AnalyticsController.AnalyticsQueryRequest.TimeBasis is utc, to decide which local weekday and hour each bucket belongs to. Ignored by every other resolution, and ignored entirely in sourceLocal mode — that fold reads each row's own source-local timestamp instead. Defaults to UTC.
"utc" (default) or "sourceLocal". See Closebot.Api.Controllers.AnalyticsController.AnalyticsTimeBasis. In sourceLocal mode Closebot.Api.Controllers.AnalyticsController.AnalyticsQueryRequest.Start and Closebot.Api.Controllers.AnalyticsController.AnalyticsQueryRequest.End are read as wall-clock values rather than UTC instants, so a month means each source's own local month.
Show child attributes
Show child attributes
Response
OK
Show child attributes
Show child attributes
Sources that contributed rows to this result but have no resolvable timezone, so were bucketed as UTC. Populated only in sourceLocal mode; null otherwise. Lets the chart say which sources need fixing instead of being quietly wrong.
Show child attributes
Show child attributes

