Analytics API Examples
This guide shows practical ways to call the Coldtivate analytics API from partner dashboards and integrations. It assumes you already have an analytics API token with the scopes needed for the endpoints you call.
For token creation, scopes, revocation, and security rules, see Analytics API Authentication.
Setup
Use the production base URL unless Coldtivate gives you a staging or partner-specific URL during onboarding.
export COLDTIVATE_API_BASE_URL="https://api.coldtivate.org/api/v1"
export COLDTIVATE_API_TOKEN="ctv_live_example_token"
Examples use anonymised IDs:
| Value | Meaning |
|---|---|
123 |
Example cooling unit ID |
45 |
Example location ID |
2026-01-01 to 2026-01-31 |
Example reporting window |
Do not put API tokens in browser-side JavaScript, mobile apps, query parameters, or logs. Use these JavaScript examples from a trusted backend service or server-rendered integration.
First Request
Request weekly user analytics for January 2026.
curl "$COLDTIVATE_API_BASE_URL/analytics/users?start_date=2026-01-01&end_date=2026-01-31&period=week&page=1&page_size=100" \
-H "Authorization: Bearer $COLDTIVATE_API_TOKEN"
Successful analytics responses use this shape:
{
"data": [],
"meta": {
"company_id": 42,
"filters": {
"start_date": "2026-01-01",
"end_date": "2026-01-31",
"period": "week"
},
"pagination": {
"page": 1,
"page_size": 100,
"total_count": 0,
"total_pages": 0,
"has_next": false,
"has_previous": false
}
}
}
Users Dashboard
Use the users endpoint to build registered user, active user, signup, and per-cooling-unit user widgets. Requires the users scope.
curl
curl "$COLDTIVATE_API_BASE_URL/analytics/users?start_date=2026-01-01&end_date=2026-01-31&period=week&page=1&page_size=100" \
-H "Authorization: Bearer $COLDTIVATE_API_TOKEN"
Python
import os
import requests
BASE_URL = os.environ.get("COLDTIVATE_API_BASE_URL", "https://api.coldtivate.org/api/v1")
TOKEN = os.environ["COLDTIVATE_API_TOKEN"]
response = requests.get(
f"{BASE_URL}/analytics/users",
headers={"Authorization": f"Bearer {TOKEN}"},
params={
"start_date": "2026-01-01",
"end_date": "2026-01-31",
"period": "week",
"page": 1,
"page_size": 100,
},
timeout=30,
)
response.raise_for_status()
payload = response.json()
print(payload["data"])
print(payload["meta"]["pagination"])
JavaScript
const baseUrl = process.env.COLDTIVATE_API_BASE_URL || "https://api.coldtivate.org/api/v1";
const token = process.env.COLDTIVATE_API_TOKEN;
const params = new URLSearchParams({
start_date: "2026-01-01",
end_date: "2026-01-31",
period: "week",
page: "1",
page_size: "100",
});
const response = await fetch(`${baseUrl}/analytics/users?${params}`, {
headers: {
Authorization: `Bearer ${token}`,
},
});
if (!response.ok) {
throw new Error(`Users analytics request failed: ${response.status}`);
}
const payload = await response.json();
console.log(payload.data);
console.log(payload.meta.pagination);
Utilization Dashboard
Use the utilization endpoint to build cooling unit occupancy, check-in, check-out, crate, and kg stored widgets. Requires the utilization scope.
curl
curl "$COLDTIVATE_API_BASE_URL/analytics/utilization?start_date=2026-01-01&end_date=2026-01-31&cooling_unit_id=123&period=day&page=1&page_size=100" \
-H "Authorization: Bearer $COLDTIVATE_API_TOKEN"
Python
import os
import requests
BASE_URL = os.environ.get("COLDTIVATE_API_BASE_URL", "https://api.coldtivate.org/api/v1")
TOKEN = os.environ["COLDTIVATE_API_TOKEN"]
response = requests.get(
f"{BASE_URL}/analytics/utilization",
headers={"Authorization": f"Bearer {TOKEN}"},
params={
"start_date": "2026-01-01",
"end_date": "2026-01-31",
"cooling_unit_id": 123,
"period": "day",
"page": 1,
"page_size": 100,
},
timeout=30,
)
response.raise_for_status()
for unit in response.json()["data"]:
print(unit["cooling_unit_id"], unit["utilization_rate"])
JavaScript
const baseUrl = process.env.COLDTIVATE_API_BASE_URL || "https://api.coldtivate.org/api/v1";
const token = process.env.COLDTIVATE_API_TOKEN;
const params = new URLSearchParams({
start_date: "2026-01-01",
end_date: "2026-01-31",
cooling_unit_id: "123",
period: "day",
page: "1",
page_size: "100",
});
const response = await fetch(`${baseUrl}/analytics/utilization?${params}`, {
headers: {
Authorization: `Bearer ${token}`,
},
});
if (!response.ok) {
throw new Error(`Utilization analytics request failed: ${response.status}`);
}
const payload = await response.json();
for (const unit of payload.data) {
console.log(unit.cooling_unit_id, unit.utilization_rate);
}
Revenue Dashboard
Use the revenue endpoint to build paid revenue, pending payment, payment method, and trend widgets. Requires the revenue scope.
curl
curl "$COLDTIVATE_API_BASE_URL/analytics/revenue?start_date=2026-01-01&end_date=2026-01-31&payment_status=paid&period=month&page=1&page_size=100" \
-H "Authorization: Bearer $COLDTIVATE_API_TOKEN"
Python
import os
import requests
BASE_URL = os.environ.get("COLDTIVATE_API_BASE_URL", "https://api.coldtivate.org/api/v1")
TOKEN = os.environ["COLDTIVATE_API_TOKEN"]
response = requests.get(
f"{BASE_URL}/analytics/revenue",
headers={"Authorization": f"Bearer {TOKEN}"},
params={
"start_date": "2026-01-01",
"end_date": "2026-01-31",
"payment_status": "paid",
"period": "month",
"page": 1,
"page_size": 100,
},
timeout=30,
)
response.raise_for_status()
for unit in response.json()["data"]:
for total in unit["currency_totals"]:
print(unit["cooling_unit_id"], total["currency"], total["total_revenue"])
JavaScript
const baseUrl = process.env.COLDTIVATE_API_BASE_URL || "https://api.coldtivate.org/api/v1";
const token = process.env.COLDTIVATE_API_TOKEN;
const params = new URLSearchParams({
start_date: "2026-01-01",
end_date: "2026-01-31",
payment_status: "paid",
period: "month",
page: "1",
page_size: "100",
});
const response = await fetch(`${baseUrl}/analytics/revenue?${params}`, {
headers: {
Authorization: `Bearer ${token}`,
},
});
if (!response.ok) {
throw new Error(`Revenue analytics request failed: ${response.status}`);
}
const payload = await response.json();
for (const unit of payload.data) {
for (const total of unit.currency_totals) {
console.log(unit.cooling_unit_id, total.currency, total.total_revenue);
}
}
Impact Dashboard
Use the impact endpoint to build food loss prevented, income increase, CO2, and community served widgets. Requires the impact scope.
curl
curl "$COLDTIVATE_API_BASE_URL/analytics/impact?start_date=2026-01-01&end_date=2026-01-31&location_id=45&period=month&page=1&page_size=100" \
-H "Authorization: Bearer $COLDTIVATE_API_TOKEN"
Python
import os
import requests
BASE_URL = os.environ.get("COLDTIVATE_API_BASE_URL", "https://api.coldtivate.org/api/v1")
TOKEN = os.environ["COLDTIVATE_API_TOKEN"]
response = requests.get(
f"{BASE_URL}/analytics/impact",
headers={"Authorization": f"Bearer {TOKEN}"},
params={
"start_date": "2026-01-01",
"end_date": "2026-01-31",
"location_id": 45,
"period": "month",
"page": 1,
"page_size": 100,
},
timeout=30,
)
response.raise_for_status()
for item in response.json()["data"]:
print(item["cooling_unit_id"], item["food_loss_prevented_kg"], item["income_increase"])
JavaScript
const baseUrl = process.env.COLDTIVATE_API_BASE_URL || "https://api.coldtivate.org/api/v1";
const token = process.env.COLDTIVATE_API_TOKEN;
const params = new URLSearchParams({
start_date: "2026-01-01",
end_date: "2026-01-31",
location_id: "45",
period: "month",
page: "1",
page_size: "100",
});
const response = await fetch(`${baseUrl}/analytics/impact?${params}`, {
headers: {
Authorization: `Bearer ${token}`,
},
});
if (!response.ok) {
throw new Error(`Impact analytics request failed: ${response.status}`);
}
const payload = await response.json();
for (const item of payload.data) {
console.log(item.cooling_unit_id, item.food_loss_prevented_kg, item.income_increase);
}
Sensor Data Dashboard
Use the sensor-data endpoint to build temperature and humidity charts for a cooling unit. Requires the sensor_data scope. For long ranges, use aggregation=hourly or aggregation=daily to reduce payload size.
curl
curl "$COLDTIVATE_API_BASE_URL/sensor-data?cooling_unit_id=123&specification_type=TEMPERATURE,HUMIDITY&start_date=2026-01-01&end_date=2026-01-31&aggregation=hourly&page=1&page_size=100" \
-H "Authorization: Bearer $COLDTIVATE_API_TOKEN"
Python
import os
import requests
BASE_URL = os.environ.get("COLDTIVATE_API_BASE_URL", "https://api.coldtivate.org/api/v1")
TOKEN = os.environ["COLDTIVATE_API_TOKEN"]
response = requests.get(
f"{BASE_URL}/sensor-data",
headers={"Authorization": f"Bearer {TOKEN}"},
params={
"cooling_unit_id": 123,
"specification_type": "TEMPERATURE,HUMIDITY",
"start_date": "2026-01-01",
"end_date": "2026-01-31",
"aggregation": "hourly",
"page": 1,
"page_size": 100,
},
timeout=30,
)
response.raise_for_status()
for reading in response.json()["data"]:
print(
reading["timestamp"],
reading["specification_type"],
reading["value"],
reading["sample_count"],
)
JavaScript
const baseUrl = process.env.COLDTIVATE_API_BASE_URL || "https://api.coldtivate.org/api/v1";
const token = process.env.COLDTIVATE_API_TOKEN;
const params = new URLSearchParams({
cooling_unit_id: "123",
specification_type: "TEMPERATURE,HUMIDITY",
start_date: "2026-01-01",
end_date: "2026-01-31",
aggregation: "hourly",
page: "1",
page_size: "100",
});
const response = await fetch(`${baseUrl}/sensor-data?${params}`, {
headers: {
Authorization: `Bearer ${token}`,
},
});
if (!response.ok) {
throw new Error(`Sensor data request failed: ${response.status}`);
}
const payload = await response.json();
for (const reading of payload.data) {
console.log(
reading.timestamp,
reading.specification_type,
reading.value,
reading.sample_count
);
}
Pagination
Each analytics response includes meta.pagination. Use has_next to keep requesting pages until all records have been read.
Python
import os
import requests
BASE_URL = os.environ.get("COLDTIVATE_API_BASE_URL", "https://api.coldtivate.org/api/v1")
TOKEN = os.environ["COLDTIVATE_API_TOKEN"]
def fetch_all_pages(endpoint, params):
page = 1
results = []
while True:
response = requests.get(
f"{BASE_URL}{endpoint}",
headers={"Authorization": f"Bearer {TOKEN}"},
params={**params, "page": page, "page_size": 100},
timeout=30,
)
response.raise_for_status()
payload = response.json()
results.extend(payload["data"])
pagination = payload["meta"]["pagination"]
if not pagination["has_next"]:
return results
page += 1
units = fetch_all_pages(
"/analytics/utilization",
{
"start_date": "2026-01-01",
"end_date": "2026-01-31",
"period": "day",
},
)
print(f"Fetched {len(units)} utilization records")
JavaScript
const baseUrl = process.env.COLDTIVATE_API_BASE_URL || "https://api.coldtivate.org/api/v1";
const token = process.env.COLDTIVATE_API_TOKEN;
async function fetchAllPages(endpoint, params) {
let page = 1;
const results = [];
while (true) {
const pageParams = new URLSearchParams({
...params,
page: String(page),
page_size: "100",
});
const response = await fetch(`${baseUrl}${endpoint}?${pageParams}`, {
headers: {
Authorization: `Bearer ${token}`,
},
});
if (!response.ok) {
throw new Error(`Analytics request failed: ${response.status}`);
}
const payload = await response.json();
results.push(...payload.data);
if (!payload.meta.pagination.has_next) {
return results;
}
page += 1;
}
}
const units = await fetchAllPages("/analytics/utilization", {
start_date: "2026-01-01",
end_date: "2026-01-31",
period: "day",
});
console.log(`Fetched ${units.length} utilization records`);
Error Handling
Handle status codes explicitly. Do not retry every error the same way.
| Status | Meaning | Recommended handling |
|---|---|---|
400 Bad Request |
Invalid query parameter, date range, pagination value, or filter. | Fix the request before retrying. |
401 Unauthorized |
Token is missing, invalid, expired, or revoked. | Stop retrying with that token and alert the integration owner. |
403 Forbidden |
Token is valid but lacks the required endpoint scope. | Request a token with the minimum required scope. |
429 Too Many Requests |
Token exceeded the rate limit. | Back off until X-RateLimit-Reset. |
Example 400 response:
{
"error": {
"code": "invalid_request",
"message": "period must be one of day, week, or month.",
"details": {
"field": "period"
}
}
}
Example 401 response:
Example 403 response:
Example 429 response:
Python Error Handling
import time
import requests
def raise_or_backoff(response):
if response.status_code == 429:
reset = int(response.headers.get("X-RateLimit-Reset", "0"))
wait_seconds = max(reset - int(time.time()), 1)
time.sleep(wait_seconds)
return "retry"
if response.status_code == 401:
raise RuntimeError("Analytics token is missing, invalid, expired, or revoked.")
if response.status_code == 403:
raise RuntimeError("Analytics token does not include the required scope.")
if response.status_code == 400:
raise RuntimeError(f"Invalid analytics request: {response.text}")
response.raise_for_status()
return "ok"
JavaScript Error Handling
function sleep(milliseconds) {
return new Promise((resolve) => setTimeout(resolve, milliseconds));
}
async function handleAnalyticsError(response) {
if (response.status === 429) {
const reset = Number(response.headers.get("X-RateLimit-Reset") || "0");
const waitSeconds = Math.max(reset - Math.floor(Date.now() / 1000), 1);
await sleep(waitSeconds * 1000);
return "retry";
}
if (response.status === 401) {
throw new Error("Analytics token is missing, invalid, expired, or revoked.");
}
if (response.status === 403) {
throw new Error("Analytics token does not include the required scope.");
}
if (response.status === 400) {
const body = await response.text();
throw new Error(`Invalid analytics request: ${body}`);
}
if (!response.ok) {
throw new Error(`Analytics request failed: ${response.status}`);
}
return "ok";
}
Rate Limiting And Backoff
The default analytics API rate limit is 100 requests per minute per token.
Every analytics response includes these headers:
| Header | Meaning |
|---|---|
X-RateLimit-Limit |
Maximum requests allowed in the current window. |
X-RateLimit-Remaining |
Requests remaining in the current window. |
X-RateLimit-Reset |
Unix timestamp when the current window resets. |
If X-RateLimit-Remaining is low, slow down non-urgent dashboard refreshes. If the API returns 429, wait until X-RateLimit-Reset before retrying. Add a small jitter when many workers may retry at the same time.
Python Backoff Request
import random
import time
import requests
def analytics_get(url, headers, params, max_attempts=3):
for attempt in range(max_attempts):
response = requests.get(url, headers=headers, params=params, timeout=30)
if response.status_code != 429:
response.raise_for_status()
return response
reset = int(response.headers.get("X-RateLimit-Reset", "0"))
wait_seconds = max(reset - int(time.time()), 1)
jitter = random.uniform(0, 1)
time.sleep(wait_seconds + jitter)
raise RuntimeError("Analytics API rate limit did not clear before max_attempts.")
JavaScript Backoff Request
function sleep(milliseconds) {
return new Promise((resolve) => setTimeout(resolve, milliseconds));
}
async function analyticsGet(url, options = {}, maxAttempts = 3) {
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
const response = await fetch(url, options);
if (response.status !== 429) {
if (!response.ok) {
throw new Error(`Analytics request failed: ${response.status}`);
}
return response;
}
const reset = Number(response.headers.get("X-RateLimit-Reset") || "0");
const waitSeconds = Math.max(reset - Math.floor(Date.now() / 1000), 1);
const jitterMilliseconds = Math.floor(Math.random() * 1000);
await sleep(waitSeconds * 1000 + jitterMilliseconds);
}
throw new Error("Analytics API rate limit did not clear before maxAttempts.");
}
Verification
Before deploying an integration, verify each endpoint with the token and filters that the dashboard will use:
for endpoint in users utilization revenue impact; do
curl -i "$COLDTIVATE_API_BASE_URL/analytics/$endpoint?start_date=2026-01-01&end_date=2026-01-31&page=1&page_size=10" \
-H "Authorization: Bearer $COLDTIVATE_API_TOKEN"
done
curl -i "$COLDTIVATE_API_BASE_URL/sensor-data?cooling_unit_id=123&start_date=2026-01-01&end_date=2026-01-31&page=1&page_size=10" \
-H "Authorization: Bearer $COLDTIVATE_API_TOKEN"
Expected result for a correctly scoped token is 200 OK for each endpoint. If an endpoint returns 403, the token does not include that endpoint's scope.