Track usage and spend
Read your wallet balance and transactions, look up pricing, report on call volume, and alert before funds run low.
All routes on this page need a key with read permission.
| Question | Route |
|---|---|
| How much do I have left? | GET /v1/wallet/balance |
| What was I charged? | GET /v1/wallet/transactions |
| What do things cost? | GET /v1/billing/pricing |
| What did I use, by type? | GET /v1/billing/usage |
| How many calls, how long, how successful? | GET /v1/analytics/calls and /summary |
| Which agents are busiest? | GET /v1/analytics/agents |
| What happened on each call? | GET /v1/call-logs |
Check your wallet balance
curl https://backend.synthesys.app/api/v1/wallet/balance \
-H "SYNTH_API_KEY: YOUR_API_KEY"{
"success": true,
"data": {
"id": "5e1a9c34-7b20-4d68-a1f3-2c8e0d6b4f97",
"balance": 123.5,
"currency": "usd",
"autoRecharge": {
"enabled": true,
"threshold": 20,
"amount": 100
},
"lastRechargeAt": "2026-10-20T14:32:00.000Z",
"createdAt": "2026-01-15T10:00:00.000Z",
"updatedAt": "2026-10-31T08:15:00.000Z"
}
}balance is a decimal amount, not cents, so 123.5 means 123.50. Auto-recharge settings are read-only here. Change them in the dashboard, at Settings > Payments & Add-Ons.
Add up what you spent
Transactions are the record of what moved in your wallet, newest first. Fields use snake_case.
Query parameters
limitintegerdefault 50offsetintegerdefault 0start_datestringcreated_at to include, as an ISO 8601 date or time.end_datestringcreated_at to include, as an ISO 8601 date or time.{
"success": true,
"data": [
{
"id": "c47e1b02-3d95-4a68-8f10-9b7a6c5d4e32",
"wallet_id": "5e1a9c34-7b20-4d68-a1f3-2c8e0d6b4f97",
"transaction_type": "debit",
"amount": 2.4,
"balance_before": 125.9,
"balance_after": 123.5,
"description": "Structured output analysis: 3 result(s)",
"created_at": "2026-10-31T08:15:00.000Z"
}
],
"pagination": { "total": 1, "limit": 50, "offset": 0 }
}transaction_type is debit for charges and credit for money added, such as recharges and refunds. amount is positive for both.
pagination.total is the true number of matching transactions.
const headers = { SYNTH_API_KEY: 'YOUR_API_KEY' };
const now = new Date();
const start = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1)).toISOString();
const limit = 100;
let spent = 0;
let added = 0;
for (let offset = 0; ; offset += limit) {
const params = new URLSearchParams({ start_date: start, limit, offset });
const res = await fetch(`https://backend.synthesys.app/api/v1/wallet/transactions?${params}`, { headers });
const { data } = await res.json();
for (const tx of data) {
if (tx.transaction_type === 'debit') spent += tx.amount;
if (tx.transaction_type === 'credit') added += tx.amount;
}
if (data.length < limit) break;
}
console.log({ spent: spent.toFixed(2), added: added.toFixed(2) });Look up pricing
GET /v1/billing/pricing returns a map of item to unit price. The values below are examples.
curl https://backend.synthesys.app/api/v1/billing/pricing \
-H "SYNTH_API_KEY: YOUR_API_KEY"{
"success": true,
"data": {
"call_per_minute": 0.3,
"phone_number_purchase": 2,
"phone_number_monthly": 2,
"voice_clone_instant": 10,
"voice_clone_professional": 50,
"message_per_message": 0.02,
"structured_output_analysis": 0.01
}
}call_per_minute is what one minute of a call costs. Read the response as a map, so a new price type doesn't break your code.
See usage by type
GET /v1/billing/usage totals your charges by type for a period. period is current_month, last_month or last_30_days, and calendar months are in UTC.
curl "https://backend.synthesys.app/api/v1/billing/usage?period=last_month" \
-H "SYNTH_API_KEY: YOUR_API_KEY"This response is trimmed to one usage type.
{
"success": true,
"data": {
"billingPeriod": {
"start": "2026-09-01T00:00:00.000Z",
"end": "2026-09-30T23:59:59.999Z",
"year": 2026,
"month": 9
},
"usageByType": {
"call_minutes": { "quantity": 412, "totalAmount": 123.6, "recordCount": 96 }
},
"totalAmount": 123.6,
"totalRecords": 96
}
}Report on calls
The analytics routes give you totals for your account over a date range of up to 366 days. They leave out test calls. See What's counted for the exact definitions.
curl "https://backend.synthesys.app/api/v1/analytics/calls?start_date=2026-10-01&end_date=2026-10-31" \
-H "SYNTH_API_KEY: YOUR_API_KEY"successRate is null when no call has finished, so handle that before you display or average it. cost is what you were charged. A date with no time means that whole day in UTC.
GET /v1/analytics/agents ranks your agents by calls in the range, and GET /v1/analytics/summary returns the totals alone.
Build your own call report
For a breakdown the analytics routes don't offer, use call logs. This route uses page and limit, and returns pagination.totalPages. Filter with start_date, end_date, agent_id and status.
duration is in seconds, and can be null. This example totals calls and minutes for each agent over 30 days.
const headers = { SYNTH_API_KEY: 'YOUR_API_KEY' };
const start = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString();
const byAgent = {};
for (let page = 1; ; page++) {
const params = new URLSearchParams({ start_date: start, limit: 100, page });
const res = await fetch(`https://backend.synthesys.app/api/v1/call-logs?${params}`, { headers });
const { data, pagination } = await res.json();
for (const log of data) {
const name = log.agentName ?? log.agent_id ?? 'Unknown';
byAgent[name] ??= { calls: 0, minutes: 0 };
byAgent[name].calls += 1;
byAgent[name].minutes += (log.duration ?? 0) / 60;
}
if (page >= pagination.totalPages) break;
}
console.table(byAgent);Alert on low balance
Run this on a schedule, such as every 15 minutes. It estimates how long your funds will last from the last 7 days of charges, and sends one alert each time the balance falls below your threshold.
Set the threshold well above the cost of one maximum-length call, because a call needs that much available to start. If auto-recharge is on, set your threshold below its trigger, so an alert means the recharge didn't work.
The state file stops repeat alerts. Replace the alert URL with your own endpoint.
// low-balance.mjs. Requires Node 18 or later.
import { existsSync, rmSync, writeFileSync } from 'node:fs';
const API = 'https://backend.synthesys.app/api';
const headers = { SYNTH_API_KEY: process.env.SYNTH_API_KEY };
const THRESHOLD = 50;
const ALERT_URL = 'https://example.com/alerts';
const STATE_FILE = 'low-balance.alerted';
async function get(path) {
const res = await fetch(API + path, { headers });
if (!res.ok) throw new Error(`${path} returned ${res.status}`);
return (await res.json()).data;
}
async function dailySpend() {
const start = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
let total = 0;
for (let offset = 0; ; offset += 100) {
const params = new URLSearchParams({ start_date: start, limit: 100, offset });
const data = await get(`/v1/wallet/transactions?${params}`);
for (const tx of data) if (tx.transaction_type === 'debit') total += tx.amount;
if (data.length < 100) return total / 7;
}
}
const wallet = await get('/v1/wallet/balance');
if (wallet.balance >= THRESHOLD) {
rmSync(STATE_FILE, { force: true });
} else if (!existsSync(STATE_FILE)) {
const perDay = await dailySpend();
const daysLeft = perDay > 0 ? Math.floor(wallet.balance / perDay) : null;
const res = await fetch(ALERT_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: `Synthesys balance is ${wallet.balance} ${wallet.currency}.`,
balance: wallet.balance,
average_daily_spend: Number(perDay.toFixed(2)),
days_left: daysLeft,
auto_recharge_enabled: wallet.autoRecharge.enabled,
}),
});
if (res.ok) writeFileSync(STATE_FILE, new Date().toISOString());
}