AI Translation v1.0 15 credits / call
Translate text into another language — natural, context-aware output from a large language model, priced per token.
- Base path
- /api/v1/translate
- Methods
- GET or POST — parameters are read identically from the query string or a JSON body
- Auth
- X-Api-Key header (never Authorization: Bearer)
- Price
- 15 credits per call · about $0.0150 at 1,000 credits/USD
- Free tier
- No per-API allowance; calls draw on your account balance
- Test mode
- Not supported; this API requires a live key
- Rate limit
- 60 calls/minute per key by default, raisable per key
- Pricing mode
- Dynamic — the price depends on the request, so it is estimated up front and the difference refunded once the work is done
AI Translation
Send text and a target language, get a translation back — structured JSON, not prose you have to strip a preamble off.
POST /api/v1/translate
X-Api-Key: ksty_live_...
Content-Type: application/json
{"text": "Good morning", "target_language": "Japanese"}
{
"translated_text": "おはようございます",
"romanization": "Ohayō gozaimasu",
"detected_source_language": "English",
"target_language": "Japanese",
"usage": { "input_tokens": 38, "output_tokens": 24 }
}
Source language is optional
Leave source_language out and the model detects it from the text. Send it when you already know
it — a short or ambiguous phrase is easier to translate correctly with the source language pinned
down than guessed at. Either way, detected_source_language in the response tells you what the
text was actually treated as, so a caller who guessed wrong can see that rather than getting a
translation from the wrong starting point silently.
Romanization
A translation into a non-Latin script comes back with romanization alongside it — the
translation sounded out in Latin letters, using the standard scheme for the language (Hepburn for
Japanese, Pinyin with tone marks for Mandarin, Revised Romanization for Korean). It is the
difference between a string you can display and a string you can say.
romanization is always present and is an empty string when there is nothing to sound out:
- the translation is already in Latin script (
French,Spanish,Vietnamese), or textis longer than 500 characters.
That second limit is a billing decision rather than a technical one. A romanization roughly doubles the output tokens, and output is the expensive half of this API — worth it for a phrase someone is about to say, wasteful on a document nobody will read aloud. Send long text in short pieces if you do want it romanized throughout.
What "faithful" means here
The model is instructed to translate what is there — including phrasing you might not expect a translation service to touch — rather than to edit, soften, or summarise. Formatting (line breaks, lists, punctuation) is preserved as closely as the target language allows. This is a translator, not a rewriter: if you want the meaning adapted rather than the words translated, that is a different task from what this endpoint does.
Pricing — why this is metered per token
Like text-ai and ocr, this calls a paid language model, so it is priced the same way:
| Component | Credits |
|---|---|
| Base fee, per call | 1 |
| Input, per 1,000 tokens | 10 |
| Output, per 1,000 tokens | 50 |
Unlike text-ai, where a short summary is cheap regardless of document length, translation's output
is close to its input in size — sometimes longer, since some language pairs expand text by 15-30%,
and roughly double on a short call that also returns a romanization. The gateway's pre-charge
accounts for that with a generous per-call ceiling; X-Credits-Charged is what the call actually
cost once the difference is refunded.
Live keys only, and no per-API free allowance
translate has no test sandbox and no monthly free grant, for the same reason as text-ai and
ocr: every call spends real money on a metered model upstream. A ksty_test_ key gets
403 sandbox_disabled. Calls draw on your normal account balance, which includes the free credits
released with your first API key.
Limits
- 20,000 characters of
textper call, about 8 pages. Split longer documents and translate the parts — unlike a summary, a split translation loses no accuracy from being chunked. - One target language per call.
When to use it
- Translating support messages, product listings, or UI strings on demand rather than maintaining a static translation table.
- Localising user-generated content where a fixed phrase list will not cover what people actually write.
- Pairing with
text-ai'slanguageparameter when you want a translated summary instead of a translated original.
Endpoints
Translate text into another language
Leave source_language out to have it auto-detected — detected_source_language in the response tells you what the model actually translated from.
Translating into a non-Latin script also returns a romanization of the result, for text up to 500 characters. It is an empty string otherwise.
curl -X POST "https://ksty.ch/api/v1/translate" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"Where is the nearest train station?","target_language":"French","source_language":"example"}'
<?php
$ch = curl_init('https://ksty.ch/api/v1/translate');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'X-Api-Key: YOUR_API_KEY',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => '{ "text": "Where is the nearest train station?", "target_language": "French", "source_language": "example"}',
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
const response = await fetch('https://ksty.ch/api/v1/translate', {
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
"text": "Where is the nearest train station?",
"target_language": "French",
"source_language": "example"
})
});
const data = await response.json();
import requests
response = requests.post(
'https://ksty.ch/api/v1/translate',
headers={'X-Api-Key': 'YOUR_API_KEY'},
json={
"text": "Where is the nearest train station?",
"target_language": "French",
"source_language": "example"
},
)
data = response.json()
{
"translated_text": "おはようございます",
"romanization": "Ohayō gozaimasu",
"detected_source_language": "English",
"target_language": "Japanese",
"usage": {
"input_tokens": 38,
"output_tokens": 24
}
}
Errors
Errors are JSON with a stable error.code — branch on the code, never on
the message. Everything below carries the X-Request-Id of the failed call.
Requests rejected by this API are not charged, and a 502 after a
charge is refunded automatically.
Response headers
Every successful call reports its own cost, so you never have to guess what a request spent or reconcile it later.