Log in Sign up
Docs/AI/AI Translation

AI Translation v1.0 15 credits / call

GET|POST /api/v1/translate

Translate text into another language — natural, context-aware output from a large language model, priced per token.

At a glance
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
  • text is 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 text per 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's language parameter when you want a translated summary instead of a translated original.

Endpoints

POST /api/v1/translate 15 credits

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.

Parameters
ParameterTypeDescription
text required string 1–20000 chars The text to translate. 1 to 20,000 characters.
target_language required string ≤ 40 chars Language to translate into, e.g. "French" or "es".
source_language optional string ≤ 40 chars Language to translate from. Omit to auto-detect.
Request
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()
Response 200 OK
{
    "translated_text": "おはようございます",
    "romanization": "Ohayō gozaimasu",
    "detected_source_language": "English",
    "target_language": "Japanese",
    "usage": {
        "input_tokens": 38,
        "output_tokens": 24
    }
}
Response fields
FieldTypeDescription
translated_text string The translation.
romanization string The translation sounded out in Latin letters. Empty when the translation is already Latin script, or when text was longer than 500 characters.
detected_source_language string The language the model actually translated from — check this against what you expected when you set source_language.
target_language string Echoes the target_language you sent.
usage.input_tokens integer Tokens read, including the instructions. Multiply by the input rate to check the charge.
usage.output_tokens integer Tokens produced. This is what you are billed for on the output side — not the ceiling that was held.

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.

CodeHTTPCause & what to do
invalid_request this API 400 No "text" or "target_language" was sent, or "text" was empty. Send the text to translate as text and the language to translate into as target_language (a name like "French" or a code like "es" both work).
text_too_large this API 413 The text exceeded 20,000 characters. Split the text and translate the parts — a split translation does not lose accuracy the way a split summary would.
content_refused this API 422 The model declined to process the content. Nothing is charged for a refused call. Retrying the identical text will refuse again — this is a judgment about the content, not a transient failure.
provider_busy this API 503 The upstream model was rate-limited, overloaded, or did not respond in time. Retry after a short backoff. Nothing is charged, so a retry costs no credits.
provider_unavailable this API 503 This deployment has no working model credentials configured. An operator problem rather than a request problem — nothing you change in the request will fix it. Nothing is charged; contact support.
provider_error this API 502 The model answered in a form this API could not read. Nothing is charged. This is logged for us to investigate; a retry is worth one attempt.
output_truncated this API 502 The translation hit its output ceiling before it was complete. Split the text into smaller pieces and translate them separately. Nothing is charged for a truncated answer.
missing_key platform 401 No API key was sent. Send your key in the X-Api-Key header (or an api_key parameter).
invalid_key platform 401 The key is not recognised — mistyped, truncated or never issued. Copy the key again from the API keys page. Repeated bad keys from one address are throttled, so fix it rather than retrying.
insufficient_credits platform 402 The balance can't cover the call. Top up on the Billing page. Nothing is charged and the call never reaches the API.
key_suspended platform 403 The key is suspended (for example after an abuse review). The reason is shown on the API keys page; contact support to have it restored.
key_revoked platform 403 The key was revoked, or it belongs to a team you have left. Create or use another key.
key_expired platform 403 The key is past the expiry date set on it. Create a new key, or one without an expiry.
out_of_scope platform 403 The key is valid but not scoped to this API. Add this API to the key's scopes, or use a key scoped to all APIs.
sandbox_disabled platform 403 A test (ksty_test_) key was used on an API with no sandbox. Use a live key for this API.
ip_blocked platform 403 The key has an IP allowlist and the calling address isn't on it. Call from an allowed address, or edit the key's allowlist.
geo_blocked platform 403 Calls from this country aren't permitted for this API. Contact support if you believe this is wrong.
spend_cap_reached platform 403 The account has the credits, but this API's monthly spend cap is used up. Raise or clear the cap on the API's page in your dashboard — topping up won't help.
unknown_api platform 404 No public API has that slug. Check the slug against the catalog.
unknown_endpoint platform 404 The API has no endpoint with that name. Check the endpoint list on the API's reference page.
method_not_allowed platform 405 The endpoint accepts only GET or only POST. Use the method shown for the endpoint.
rate_limited platform 429 A rate limit was exceeded — per key, per account across all your keys, or the tighter per-account limit on AI APIs. Wait for Retry-After seconds. A second key shares the account limit, so it won't help.
api_unavailable platform 502 The API is temporarily unavailable. Nothing was charged. Retry later; check the status page.
upstream_error platform 502 The API failed while processing the call. Safe to retry — any credits taken are 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.

HeaderMeaning
X-Request-Id Identifier for this call — on every response, in every error body and in your usage log. Quote it in support requests.
X-Credits-Charged Credits taken for this call. Zero on test-mode calls and on errors raised before the work started.
X-Credits-Remaining Balance left afterwards, across per-API grants, promotional and paid credits.
X-RateLimit-Limit Requests allowed per minute by the limit you are closest to (key, account or AI tier).
X-RateLimit-Remaining Requests left in the current window of that limit.
X-RateLimit-Reset Unix time when that window resets.
Retry-After On a 429 only: seconds to wait before retrying.