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 URLhttps://api.savetoken.org
AuthAPI key in a request header (see Authentication)
Content typeapplication/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.

shell
curl -X POST https://api.savetoken.org/v1/keys/free
json — response
{
  "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.

shell
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 ..."}'
python
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"])
typescript
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

json — 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.

  1. Take the raw tool output — a web-search payload, an API response, a file dump.
  2. Send it as text to POST /v1/optimize.
  3. Use optimized from the response as the tool message content.
  4. If verdict is NEUTRAL or REDUCED, 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.

  1. Send the conversation as messages instead of text.
  2. Set stable_prefix_message_count to the number of leading messages to leave byte-identical — this keeps a provider prompt-cache prefix intact.
  3. Send the returned optimized string as the context for your next model call.
json — request body
{
  "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
}
POST/v1/optimize

Reduce the input tokens in a block of text or a message array. Requires authentication.

Request body

FieldTypeDescription
textstringrequired*The text to reduce. Provide exactly one of text or messages.
messagesarrayrequired*Message objects, each {"role", "content"}. role is one of system, user, assistant, tool. Provide exactly one of text or messages.
stable_prefix_charsintegeroptionalDefault 0. Leading characters to leave unchanged, so a provider prompt-cache prefix stays byte-identical. Applies to the text form.
stable_prefix_message_countintegeroptionalDefault 0. Leading messages to leave unchanged. Applies to the messages form.
optionsobjectoptionalOverrides 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

FieldTypeDefaultDescription
enable_schemabooleanfalseUnlock 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_levelstring"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_defsbooleanfalseExclude tool and function definitions from reduction.
allow_user_compressbooleanfalseAllow reduction of user-role message content.

Response body

FieldTypeDescription
request_idstringUnique per call. Quote it in support requests.
optimizedstringThe reduced text. Send this to your LLM provider in place of the input.
modestringThe mechanism that ran: pass, protect, strip, toon, jsonl_toon, schema, json_minify, log_collapse, or frame_filter.
tokens_inintegerInput token count.
tokens_outintegerOutput token count.
dollars_savednumberBilled/verified cost saved on this call, in USD — only above zero on a WIN. Not an estimate.
estimated_savingsnumberCost 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.
verdictstringWIN: 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.
warningsarrayStrings describing non-fatal notes about the input, such as an auto-redacted secret.

Errors

StatuscodeCause
400BAD_REQUESTBoth or neither of text and messages set, or a malformed body.
401UNAUTHORIZEDMissing, invalid, or revoked key.
429RATE_LIMITPer-minute burst cap, free-tier daily cap, or paid monthly cap reached. See Rate limits.
500INTERNALServer-side failure. Quote request_id in support requests.
POST/v1/keys/free

Mint a free, rate-limited API key. No authentication. No request body.

Response body

FieldTypeDescription
keystringThe API key. Prefix tsk_. Shown once.
api_keystringThe same value as key.
key_idstringIdentifier for the key. Safe to log.
daily_quotaintegerCalls allowed per day on this key.
notestringA reminder that the key is not recoverable.

Errors

StatuscodeCause
429RATE_LIMITMore than 20 mints from one IP address in a day.
400BAD_REQUESTThe free tier is disabled.
GET/v1/keys/{transaction_id}

Retrieve the key minted for a completed checkout. No authentication. Polled by the checkout page after payment.

Response body

FieldTypeDescription
api_keystringThe paid key. Returned once.
notestringA 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.

GET/v1/usage

List recent calls made with your key, newest first. Requires authentication.

Query parameters

NameTypeDefaultDescription
limitinteger50Maximum rows to return.
offsetinteger0Rows to skip.

Response body

FieldTypeDescription
key_idstringThe key the results belong to.
callsarrayObjects with request_id, ts, tokens_in, tokens_out, dollars_saved, verdict, mode.
GET/v1/savings

Return the running cost total for your key. Requires authentication.

Response body

FieldTypeDescription
key_idstringThe key the total belongs to.
tenant_idstringThe account the key belongs to.
lifetime_dollars_savednumberSum of dollars_saved across every call, in USD.
GET/v1/health

Return service status. No authentication. Response is {"status": "ok"}.

POST/v1/support

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

FieldTypeDescription
messagestringrequired1–4000 characters.
request_idstringoptionalA request_id from a prior error response — attach it so the report includes exactly what failed.
contactstringoptionalSet 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.
shell
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

FieldTypeDescription
statusstring"received".

Errors

StatuscodeCause
429RATE_LIMITMore than 10 requests from one IP address in an hour.
400BAD_REQUESTmessage missing or over 4000 characters.

Errors

Every error response has the same shape:

json
{
  "request_id": "b3f1a09c-4e77-4b2a-9c1d-2f6e8a0d5b41",
  "error": true,
  "code": "BAD_REQUEST",
  "message": "exactly one of text or messages must be set"
}
StatuscodeMeaning
400BAD_REQUESTThe request body is malformed or violates a field constraint.
401UNAUTHORIZEDThe API key is missing, invalid, or revoked.
429RATE_LIMITA rate or quota limit was reached.
500INTERNALA server-side failure. Retry, then file it with request_id.

Rate limits

LimitApplies to
300 requests / minute / keyEvery key. A burst guard, not a plan limit.
100 requests / day / keyFree-tier keys.
5,000 requests / month / keyPro keys. Rolling 30-day window. Hit the cap? Email contact@savetoken.org and we'll bump you.
20 mints / day / IP addressPOST /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.