curl --request GET \
--url https://api.zivio.net/api/v4/projects/summary \
--header 'Authorization: Bearer <token>' \
--header 'zivio-tenant-id: <api-key>'import requests
url = "https://api.zivio.net/api/v4/projects/summary"
headers = {
"Authorization": "Bearer <token>",
"zivio-tenant-id": "<api-key>"
}
response = requests.get(url, headers=headers)
print(response.text)const options = {
method: 'GET',
headers: {Authorization: 'Bearer <token>', 'zivio-tenant-id': '<api-key>'}
};
fetch('https://api.zivio.net/api/v4/projects/summary', 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.zivio.net/api/v4/projects/summary",
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>",
"zivio-tenant-id: <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"
"net/http"
"io"
)
func main() {
url := "https://api.zivio.net/api/v4/projects/summary"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("zivio-tenant-id", "<api-key>")
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.zivio.net/api/v4/projects/summary")
.header("Authorization", "Bearer <token>")
.header("zivio-tenant-id", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.zivio.net/api/v4/projects/summary")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
request["zivio-tenant-id"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"summary": {
"resource": "projects",
"group_by": [
"status"
],
"measures": [
"rate"
],
"filters": {},
"defaults_applied": [
"measures"
],
"currency": {
"mode": "per_currency"
},
"meta": {
"group_count": 1,
"returned": 1,
"truncated": false,
"order": "rate desc"
},
"groups": [
{
"dimensions": [
{
"key": "status",
"id": "in_progress",
"label": "Project is underway"
}
],
"rate": {
"amount": "500000.00",
"currency": "GBP",
"cents": 50000000
},
"share": 100,
"project_count": 12
}
],
"totals": {
"rate": {
"amount": "500000.00",
"currency": "GBP",
"cents": 50000000
},
"project_count": 12
}
}
}{
"error": "invalid_token",
"error_description": "The access token provided is expired, revoked, malformed, or invalid for other reasons"
}{
"error": "insufficient_scope",
"error_description": "The request requires higher privileges than provided by the access token",
"required_scope": "projects:read",
"provided_scopes": [
"welcome:read"
]
}{
"error": "measure 'rate' is not permitted"
}Aggregate project spend grouped by one or more dimensions
Pagination-free aggregation computed in SQL. Groups the projects you are permitted to see by one or more dimensions and (always) currency, returning exact per-currency subtotals plus optional converted roll-ups. Reuses the standard filter[...] grammar (e.g. filter[state]=in_progress). The role-aware measure default is echoed in defaults_applied. Large cross-products are bounded — see meta (group_count / returned / truncated). Forbidden or unknown measures/dimensions/filters return 422.
curl --request GET \
--url https://api.zivio.net/api/v4/projects/summary \
--header 'Authorization: Bearer <token>' \
--header 'zivio-tenant-id: <api-key>'import requests
url = "https://api.zivio.net/api/v4/projects/summary"
headers = {
"Authorization": "Bearer <token>",
"zivio-tenant-id": "<api-key>"
}
response = requests.get(url, headers=headers)
print(response.text)const options = {
method: 'GET',
headers: {Authorization: 'Bearer <token>', 'zivio-tenant-id': '<api-key>'}
};
fetch('https://api.zivio.net/api/v4/projects/summary', 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.zivio.net/api/v4/projects/summary",
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>",
"zivio-tenant-id: <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"
"net/http"
"io"
)
func main() {
url := "https://api.zivio.net/api/v4/projects/summary"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("zivio-tenant-id", "<api-key>")
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.zivio.net/api/v4/projects/summary")
.header("Authorization", "Bearer <token>")
.header("zivio-tenant-id", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.zivio.net/api/v4/projects/summary")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
request["zivio-tenant-id"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"summary": {
"resource": "projects",
"group_by": [
"status"
],
"measures": [
"rate"
],
"filters": {},
"defaults_applied": [
"measures"
],
"currency": {
"mode": "per_currency"
},
"meta": {
"group_count": 1,
"returned": 1,
"truncated": false,
"order": "rate desc"
},
"groups": [
{
"dimensions": [
{
"key": "status",
"id": "in_progress",
"label": "Project is underway"
}
],
"rate": {
"amount": "500000.00",
"currency": "GBP",
"cents": 50000000
},
"share": 100,
"project_count": 12
}
],
"totals": {
"rate": {
"amount": "500000.00",
"currency": "GBP",
"cents": 50000000
},
"project_count": 12
}
}
}{
"error": "invalid_token",
"error_description": "The access token provided is expired, revoked, malformed, or invalid for other reasons"
}{
"error": "insufficient_scope",
"error_description": "The request requires higher privileges than provided by the access token",
"required_scope": "projects:read",
"provided_scopes": [
"welcome:read"
]
}{
"error": "measure 'rate' is not permitted"
}Authorizations
OAuth 2.0 client credentials. The access token from POST /oauth/token is sent as a bearer token. Request only the scopes you need.
Your Zivio organisation identifier. The regional API endpoints serve every Zivio organisation, so each request must identify yours.
Query Parameters
One or more dimensions, comma-separated (max 4), combined into a composite grouping. Permitted: category, subcategory, tier3, supplier, owner, org, status, month, quarter, year.
Comma-separated measures (defaults per role if omitted): rate, budget, target_cost, original_rate, original_budget, sales_rate, sales_budget, estimated_savings, projected_recognized_savings, project_count.
Date column used for month/quarter/year buckets.
created_at, submitted_at, approved_at, hired_at, start_date, due_on, ended_at, closed_at Optional ISO currency code for an indicative converted roll-up (per-currency subtotals remain exact).
Standard filter grammar, e.g. filter[state]=in_progress&filter[created_at][gte]=2025-01-01.
Response
Spend summary grouped by the requested dimensions and currency
The response is of type object.

