SaveToken API
One endpoint reduces the input tokens in logs, JSON, and tool or MCP schemas before you send them to an LLM provider. SaveToken returns the smaller text. You send it to Claude, OpenAI, or any provider with your own key. SaveToken never calls a provider and never receives your provider key.
Overview
The API is REST over HTTPS. It accepts JSON request bodies, returns JSON responses, and uses standard HTTP status codes.
| Base URL | https://api.savetoken.org |
| Auth | API key in a request header (see Authentication) |
| Content type | application/json |
POST /v1/optimize does the work.
GET /v1/health, POST /v1/keys/free,
GET /v1/keys/{transaction_id}, and
POST /v1/support need no key; every
other endpoint does. Send the key from a server you control — never embed
a tsk_ key in browser or mobile-app code.
Quickstart
Step 1 — Get a key
Mint a free key. No account, no card.
curl -X POST https://api.savetoken.org/v1/keys/free
{
"key": "tsk_9f2c7b1e8a4d6053c1e2f4a6b8d0c3e5",
"api_key": "tsk_9f2c7b1e8a4d6053c1e2f4a6b8d0c3e5",
"key_id": "b0a1c2d3-e4f5-6789-abcd-0123456789ef",
"daily_quota": 100,
"note": "store this now — it is never shown again (hashed at rest)"
}
Step 2 — Send a request
Pass the text you would otherwise send to your model. The call shape is the same in every language.
curl -X POST https://api.savetoken.org/v1/optimize \
-H "Authorization: Bearer tsk_9f2c7b1e8a4d6053c1e2f4a6b8d0c3e5" \
-H "Content-Type: application/json" \
-d '{"text": "2026-09-08T11:04:22Z INFO db pool acquired conn=17 ..."}'
import requests
resp = requests.post(
"https://api.savetoken.org/v1/optimize",
headers={"Authorization": "Bearer tsk_9f2c7b1e8a4d6053c1e2f4a6b8d0c3e5"},
json={"text": "2026-09-08T11:04:22Z INFO db pool acquired conn=17 ..."},
)
result = resp.json()
print(result["verdict"], result["tokens_in"], result["tokens_out"])
const resp = await fetch("https://api.savetoken.org/v1/optimize", {
method: "POST",
headers: {
"Authorization": "Bearer tsk_9f2c7b1e8a4d6053c1e2f4a6b8d0c3e5",
"Content-Type": "application/json",
},
body: JSON.stringify({ text: "2026-09-08T11:04:22Z INFO db pool acquired conn=17 ..." }),
});
const result = await resp.json();
console.log(result.verdict, result.tokens_in, result.tokens_out);
Step 3 — Read the response
{
"request_id": "b3f1a09c-4e77-4b2a-9c1d-2f6e8a0d5b41",
"optimized": "2026-09-08T11:04:22Z INFO db pool acquired conn=17 ...",
"mode": "frame_filter",
"tokens_in": 1139,
"tokens_out": 630,
"dollars_saved": 0.0031,
"verdict": "WIN",
"warnings": []
}
Send optimized to your LLM provider in place of the
original text. verdict is WIN
only when optimized is smaller than the input and was
verified equivalent to it — that is the only case where
dollars_saved is above zero.
REDUCED means the token count genuinely dropped and a
structural safety check passed, but this exact content hasn't been run through
reference-model verification yet — dollars_saved stays
$0 (billed savings are verified-only), but
estimated_savings shows the real, unverified figure so a
genuine reduction never reads as "nothing happened."
NEUTRAL means no reduction happened at all for this
input; fall back to the original text.
PASS_THROUGH means there was nothing to reduce — for
example, the entire input was held as a stable prefix.
Authentication
Send your API key on every authenticated request, in either header:
| Authorization: Bearer tsk_… | Standard bearer header. |
| X-Api-Key: tsk_… | Alternative. Same effect. |
Keys are hashed with SHA-256 at rest. SaveToken cannot display a key after it is issued — store it when you create it. Revoke a key from the dashboard.
Optimize a tool result
Reduce a noisy tool or function result before it re-enters an agent's prompt.
Prerequisite: an API key.
- Take the raw tool output — a web-search payload, an API response, a file dump.
- Send it as
texttoPOST /v1/optimize. - Use
optimizedfrom the response as the tool message content. - If
verdictisNEUTRALorREDUCED, an agent pipeline should still treat it cautiously — REDUCED means a real drop that isn't reference-model verified for this exact content, not a guarantee it's safe for automated consumption.
Optimize a conversation history
Keep a long chat inside the context window by reducing older turns.
Prerequisite: an API key.
- Send the conversation as
messagesinstead oftext. - Set
stable_prefix_message_countto the number of leading messages to leave byte-identical — this keeps a provider prompt-cache prefix intact. - Send the returned
optimizedstring as the context for your next model call.
{
"messages": [
{"role": "system", "content": "You are a support agent."},
{"role": "user", "content": "My deploy fails with exit code 137."},
{"role": "assistant", "content": "Exit 137 is an out-of-memory kill. Raise the container memory limit and redeploy."}
],
"stable_prefix_message_count": 1
}
Reduce the input tokens in a block of text or a message array. Requires authentication.
Request body
| Field | Type | Description | |
|---|---|---|---|
| text | string | required* | The text to reduce. Provide exactly one of text or messages. |
| messages | array | required* | Message objects, each {"role", "content"}. role is one of system, user, assistant, tool. Provide exactly one of text or messages. |
| stable_prefix_chars | integer | optional | Default 0. Leading characters to leave unchanged, so a provider prompt-cache prefix stays byte-identical. Applies to the text form. |
| stable_prefix_message_count | integer | optional | Default 0. Leading messages to leave unchanged. Applies to the messages form. |
| options | object | optional | Overrides for the automatic mechanism selection. See below. Most callers omit this. |
* Provide exactly one of text or messages. Sending both, or neither, returns BAD_REQUEST.
options object
| Field | Type | Default | Description |
|---|---|---|---|
| enable_schema | boolean | false | Unlock the med and high schema-compaction levels. The lossless low level runs automatically on tool and MCP schemas whether or not this is set. |
| schema_level | string | "low" | One of low, med, high. Higher levels trim more from schema descriptions. med and high apply only when enable_schema is true. |
| freeze_tool_defs | boolean | false | Exclude tool and function definitions from reduction. |
| allow_user_compress | boolean | false | Allow reduction of user-role message content. |
Response body
| Field | Type | Description |
|---|---|---|
| request_id | string | Unique per call. Quote it in support requests. |
| optimized | string | The reduced text. Send this to your LLM provider in place of the input. |
| mode | string | The mechanism that ran: pass, protect, strip, toon, jsonl_toon, schema, json_minify, log_collapse, or frame_filter. |
| tokens_in | integer | Input token count. |
| tokens_out | integer | Output token count. |
| dollars_saved | number | Billed/verified cost saved on this call, in USD — only above zero on a WIN. Not an estimate. |
| estimated_savings | number | Cost saved on any real token drop, verified or not — labelled an estimate on purpose. Equals dollars_saved on a WIN; nonzero on REDUCED; $0 on NEUTRAL/PASS_THROUGH. |
| verdict | string | WIN: optimized is smaller than the input and verified equivalent to it — the only verdict where dollars_saved is above zero. REDUCED: a real token drop, structural safety check passed, not yet reference-model verified for this exact content. NEUTRAL: no reduction happened at all; fall back to the input. PASS_THROUGH: nothing to reduce, for example when the whole input was held as a stable prefix. |
| warnings | array | Strings describing non-fatal notes about the input, such as an auto-redacted secret. |
Errors
| Status | code | Cause |
|---|---|---|
| 400 | BAD_REQUEST | Both or neither of text and messages set, or a malformed body. |
| 401 | UNAUTHORIZED | Missing, invalid, or revoked key. |
| 429 | RATE_LIMIT | Per-minute burst cap, free-tier daily cap, or paid monthly cap reached. See Rate limits. |
| 500 | INTERNAL | Server-side failure. Quote request_id in support requests. |
Mint a free, rate-limited API key. No authentication. No request body.
Response body
| Field | Type | Description |
|---|---|---|
| key | string | The API key. Prefix tsk_. Shown once. |
| api_key | string | The same value as key. |
| key_id | string | Identifier for the key. Safe to log. |
| daily_quota | integer | Calls allowed per day on this key. |
| note | string | A reminder that the key is not recoverable. |
Errors
| Status | code | Cause |
|---|---|---|
| 429 | RATE_LIMIT | More than 20 mints from one IP address in a day. |
| 400 | BAD_REQUEST | The free tier is disabled. |
Retrieve the key minted for a completed checkout. No authentication. Polled by the checkout page after payment.
Response body
| Field | Type | Description |
|---|---|---|
| api_key | string | The paid key. Returned once. |
| note | string | A reminder that the key is not recoverable. |
404 means the key is not minted yet, or has already
been retrieved once. A second request never re-reveals a key.
List recent calls made with your key, newest first. Requires authentication.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
| limit | integer | 50 | Maximum rows to return. |
| offset | integer | 0 | Rows to skip. |
Response body
| Field | Type | Description |
|---|---|---|
| key_id | string | The key the results belong to. |
| calls | array | Objects with request_id, ts, tokens_in, tokens_out, dollars_saved, verdict, mode. |
Return the running cost total for your key. Requires authentication.
Response body
| Field | Type | Description |
|---|---|---|
| key_id | string | The key the total belongs to. |
| tenant_id | string | The account the key belongs to. |
| lifetime_dollars_saved | number | Sum of dollars_saved across every call, in USD. |
Return service status. No authentication. Response is {"status": "ok"}.
File an issue. No authentication — usable by a human via the help center's form, or directly by an agent that just received an error and wants to report it with context attached.
Request body
| Field | Type | Description | |
|---|---|---|---|
| message | string | required | 1–4000 characters. |
| request_id | string | optional | A request_id from a prior error response — attach it so the report includes exactly what failed. |
| contact | string | optional | Set as the email's Reply-To so a reply goes straight to you. An agent filing on a user's behalf can omit this — the ticket still gets read, just with no reply path. |
curl -X POST https://api.savetoken.org/v1/support \
-H "Content-Type: application/json" \
-d '{"message": "getting 500 on every call today", "request_id": "b3f1a09c-4e77-4b2a-9c1d-2f6e8a0d5b41"}'
Response body
| Field | Type | Description |
|---|---|---|
| status | string | "received". |
Errors
| Status | code | Cause |
|---|---|---|
| 429 | RATE_LIMIT | More than 10 requests from one IP address in an hour. |
| 400 | BAD_REQUEST | message missing or over 4000 characters. |
Errors
Every error response has the same shape:
{
"request_id": "b3f1a09c-4e77-4b2a-9c1d-2f6e8a0d5b41",
"error": true,
"code": "BAD_REQUEST",
"message": "exactly one of text or messages must be set"
}
| Status | code | Meaning |
|---|---|---|
| 400 | BAD_REQUEST | The request body is malformed or violates a field constraint. |
| 401 | UNAUTHORIZED | The API key is missing, invalid, or revoked. |
| 429 | RATE_LIMIT | A rate or quota limit was reached. |
| 500 | INTERNAL | A server-side failure. Retry, then file it with request_id. |
Rate limits
| Limit | Applies to |
|---|---|
| 300 requests / minute / key | Every key. A burst guard, not a plan limit. |
| 100 requests / day / key | Free-tier keys. |
| 5,000 requests / month / key | Pro keys. Rolling 30-day window. Hit the cap? Email contact@savetoken.org and we'll bump you. |
| 20 mints / day / IP address | POST /v1/keys/free. |
A request past any limit returns 429 RATE_LIMIT with a message naming the limit reached.
Roadmap
- Official Python and TypeScript SDKs — in progress.
- Framework helpers for LangChain, LangGraph, and LlamaIndex — planned.
- Key-management API endpoints — planned. Use the dashboard to revoke a key today.