Count cost from usage
Every response reports the tokens it used in usage, and the x-syntic-billing-source header says how it was paid: subscription (inside your plan’s allowance, no per-token charge) or api_credit (charged to your usage-credit wallet at the model’s rate). With those two pieces you can log the cost of each request in your own system.
The rates
Dollars per million tokens, from Pricing:
| Model | Input | Cached input | Output |
|---|---|---|---|
Amara S1.2 | 2.50 | 0.40 | 12.00 |
Amara Quick | 1.50 | 0.25 | 8.00 |
Amara Deep | 2.50 | 0.25 | 12.50 |
Syntic Code | 2.50 | 0.25 | 12.50 |
Syntic Code Fast | 4.50 | 0.70 | 20.00 |
Cached input means prompt tokens served from the prompt cache. They are reported as usage.cached_tokens (some responses nest it as usage.prompt_tokens_details.cached_tokens) and are part of prompt_tokens, not in addition to it.
import os
import requests
RATES = { # $ per 1M tokens: input, cached input, output
"Amara S1.2": (2.50, 0.40, 12.00),
"Amara Quick": (1.50, 0.25, 8.00),
"Amara Deep": (2.50, 0.25, 12.50),
"Syntic Code": (2.50, 0.25, 12.50),
"Syntic Code Fast": (4.50, 0.70, 20.00),
}
def cost_usd(model: str, usage: dict, billing_source: str) -> float:
if billing_source != "api_credit":
return 0.0 # inside the plan allowance
rin, rcache, rout = RATES[model]
details = usage.get("prompt_tokens_details") or {}
cached = min(usage.get("cached_tokens") or details.get("cached_tokens") or 0, usage["prompt_tokens"])
fresh = usage["prompt_tokens"] - cached
return (fresh * rin + cached * rcache + usage["completion_tokens"] * rout) / 1_000_000
requested = "Amara Quick"
r = requests.post(
"https://app.syntic.ai/v1/chat/completions",
headers={"Authorization": f"Bearer {os.environ['SYNTIC_API_KEY']}"},
json={"model": requested, "stream": False, "messages": [{"role": "user", "content": "Two facts about otters."}]},
timeout=(10, 300),
)
r.raise_for_status()
answered_by = r.headers.get("x-syntic-model-downgraded") or requested # a fallback changes the rate
source = r.headers.get("x-syntic-billing-source", "subscription")
usage = r.json()["usage"]
print(f"{answered_by}: {usage['total_tokens']} tokens, ${cost_usd(answered_by, usage, source):.6f} ({source})")Streamed requests
With streaming, usage arrives on a chunk near the end of the stream. Keep the last usage you see and compute the cost after [DONE]. Read x-syntic-billing-source from the response headers before you start reading events.
Things that make the real bill differ
- Fallbacks. If the model you asked for is not in your plan, the default model answers and
x-syntic-model-downgradednames it. Use that name for the rate, as the code above does. - Cancelled streams. If you disconnect before the final usage chunk, Syntic bills an estimate. Your log will have no usage for that request; record it as “unknown”.
- Rounding. The Console is the authority for what was charged. Your own figure is for monitoring and per-customer attribution, and should match within a fraction of a cent per request.
Keep the rate table in configuration, not code, and check it against Pricing when prices change.