curl --request GET \
--url https://api.scripe.io/v1/analytics/cross-workspace/overview \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.scripe.io/v1/analytics/cross-workspace/overview"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.scripe.io/v1/analytics/cross-workspace/overview', 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.scripe.io/v1/analytics/cross-workspace/overview",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.scripe.io/v1/analytics/cross-workspace/overview"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.scripe.io/v1/analytics/cross-workspace/overview")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.scripe.io/v1/analytics/cross-workspace/overview")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": {
"range": {
"from": "2023-11-07T05:31:56Z",
"to": "2023-11-07T05:31:56Z",
"days": 123
},
"workspaces": [
{
"posts": {
"current": 123,
"previous": 123
},
"impressions": {
"current": 123,
"previous": 123
},
"engagement": {
"current_rate": 123,
"previous_rate": 123
},
"followers": {
"current": 123,
"growth": 123
},
"activity": {
"current_posts_per_week": 123,
"previous_posts_per_week": 123
},
"workspace": {
"id": "org_a1b2c3d4e5f6g7h8",
"name": "<string>",
"is_default": true,
"reach": "member"
},
"projects": {
"count": 123
}
}
],
"totals": {
"posts": {
"current": 123,
"previous": 123
},
"impressions": {
"current": 123,
"previous": 123
},
"engagement": {
"current_rate": 123,
"previous_rate": 123
},
"followers": {
"current": 123,
"growth": 123
},
"activity": {
"current_posts_per_week": 123,
"previous_posts_per_week": 123
}
}
},
"meta": {
"selection": "all_reachable",
"workspaces_reachable": 123,
"workspaces_returned": 123,
"max_workspaces": 123,
"truncated": true,
"skipped": [
{
"workspace_id": "<string>",
"reason": "no_readable_projects"
}
],
"projects_truncated": [
"<string>"
],
"totals_omitted_reason": "<string>"
}
}{
"error": {
"code": "not_found",
"message": "<string>",
"request_id": "req_a1b2c3d4e5f6",
"docs_url": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "not_found",
"message": "<string>",
"request_id": "req_a1b2c3d4e5f6",
"docs_url": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "not_found",
"message": "<string>",
"request_id": "req_a1b2c3d4e5f6",
"docs_url": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "not_found",
"message": "<string>",
"request_id": "req_a1b2c3d4e5f6",
"docs_url": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "not_found",
"message": "<string>",
"request_id": "req_a1b2c3d4e5f6",
"docs_url": "<string>",
"details": "<unknown>"
}
}Analytics overview across every reachable workspace
The same current-vs-previous-period rollup as
GET /v1/analytics/overview, computed for EVERY workspace the
caller can reach and returned in one response, each row attributed
to the workspace it came from.
Reach is Clerk membership or the billing-based agency-owner
grant — the workspaces an agency the caller owns pays for — and
workspaces[].workspace.reach says which one applies. Inside each
workspace, project visibility is the same admin-or-assignment rule
every other read applies, so a plain member of a client workspace
still sees only the projects they are assigned to.
data.totals is the same figures over the union of every project
above, computed by one query rather than summed here: an
engagement rate has no meaningful average. It is null when there
was nothing to total or when the union was too large for a further
aggregate pass — meta.totals_omitted_reason says which.
A reachable workspace whose projects the caller cannot read is
named in meta.skipped rather than dropped, because a missing
workspace reads as “this client posted nothing”. When
meta.truncated is true the response holds the first
meta.max_workspaces of meta.workspaces_reachable; name the
rest explicitly in workspaceId to cover them.
OAuth only. An API key is bound to one workspace at creation,
so there is nothing to aggregate across; a key gets
400 invalid_request. projectId is rejected here — use
GET /v1/analytics/overview for specific projects.
Required scopes: analytics:read and workspace:read (the
response enumerates workspaces).
curl --request GET \
--url https://api.scripe.io/v1/analytics/cross-workspace/overview \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.scripe.io/v1/analytics/cross-workspace/overview"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.scripe.io/v1/analytics/cross-workspace/overview', 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.scripe.io/v1/analytics/cross-workspace/overview",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.scripe.io/v1/analytics/cross-workspace/overview"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.scripe.io/v1/analytics/cross-workspace/overview")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.scripe.io/v1/analytics/cross-workspace/overview")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": {
"range": {
"from": "2023-11-07T05:31:56Z",
"to": "2023-11-07T05:31:56Z",
"days": 123
},
"workspaces": [
{
"posts": {
"current": 123,
"previous": 123
},
"impressions": {
"current": 123,
"previous": 123
},
"engagement": {
"current_rate": 123,
"previous_rate": 123
},
"followers": {
"current": 123,
"growth": 123
},
"activity": {
"current_posts_per_week": 123,
"previous_posts_per_week": 123
},
"workspace": {
"id": "org_a1b2c3d4e5f6g7h8",
"name": "<string>",
"is_default": true,
"reach": "member"
},
"projects": {
"count": 123
}
}
],
"totals": {
"posts": {
"current": 123,
"previous": 123
},
"impressions": {
"current": 123,
"previous": 123
},
"engagement": {
"current_rate": 123,
"previous_rate": 123
},
"followers": {
"current": 123,
"growth": 123
},
"activity": {
"current_posts_per_week": 123,
"previous_posts_per_week": 123
}
}
},
"meta": {
"selection": "all_reachable",
"workspaces_reachable": 123,
"workspaces_returned": 123,
"max_workspaces": 123,
"truncated": true,
"skipped": [
{
"workspace_id": "<string>",
"reason": "no_readable_projects"
}
],
"projects_truncated": [
"<string>"
],
"totals_omitted_reason": "<string>"
}
}{
"error": {
"code": "not_found",
"message": "<string>",
"request_id": "req_a1b2c3d4e5f6",
"docs_url": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "not_found",
"message": "<string>",
"request_id": "req_a1b2c3d4e5f6",
"docs_url": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "not_found",
"message": "<string>",
"request_id": "req_a1b2c3d4e5f6",
"docs_url": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "not_found",
"message": "<string>",
"request_id": "req_a1b2c3d4e5f6",
"docs_url": "<string>",
"details": "<unknown>"
}
}{
"error": {
"code": "not_found",
"message": "<string>",
"request_id": "req_a1b2c3d4e5f6",
"docs_url": "<string>",
"details": "<unknown>"
}
}Authorizations
Pass Authorization: Bearer scripe_sk_live_<...> (or
scripe_sk_test_<...> for test keys) on every request. Keys
are scoped to a single workspace and can be revoked from the
Scripe dashboard.
The same header also accepts an OAuth 2.1 access token
(scripe_oat_*); both credentials share one scope vocabulary
and every operation below documents the scope it requires.
An API key can hold every scope named on this surface except
webhooks:manage, which is grantable to OAuth tokens only
today — the webhook-endpoint operations answer
403 scope_missing to every API key. Operations that name no
scope accept any valid token of the workspace.
Headers
Pin the API version. Format YYYY-MM-DD. Omit to receive the
currently rolling default. Unknown versions return 400 version_unsupported.
"2026-08-10"
Query Parameters
One Clerk organisation id, or a comma-separated list, from GET /v1/workspaces. Omit (or pass all) for every reachable workspace. A named workspace the caller cannot reach is refused with workspace_unavailable, never silently dropped.
"org_a1b2c3d4e5f6g7h8"
ISO 8601 or YYYY-MM-DD lower bound (inclusive).
ISO 8601 or YYYY-MM-DD upper bound (inclusive).