Live translation for your own product
One API for live voice translation, text translation, speech and video in 32 languages. Prepaid credit, pay only for what you use, no monthly fee.
curl https://api.sponkly.com/v1/translate \
-H "Authorization: Bearer $SPONKLY_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Merhaba dünya", "target": "en"}'
{
"translation": "Hello world",
"target": "en",
"characters": 13,
"cost_usd": "0.000130"
}What it costs
Every call is paid from your prepaid balance. A call that fails on our side is not charged.
Authentication
Every request carries your API key in the Authorization header. Keys are made and revoked in your dashboard; each one spends the same balance.
- Sign up free on sponkly.com and open Dashboard → Developer API.
- Add credit (from $10) and create an API key.
- Send requests to https://api.sponkly.com/v1 with Authorization: Bearer <your key>.
Base URL: https://api.sponkly.com/v1
Authorization: Bearer spk_…Signed requests
Every API key comes with a signing secret. When you sign your requests with it, a stolen key alone is useless, a request cannot be changed on the way, and the same request cannot be sent twice. Turn on "Require signed requests" for the key in your dashboard and every unsigned request is refused.
- Take the current time in Unix seconds and the SHA-256 of the exact body you send (for a file upload: the file's bytes; for GET and the live socket: an empty body).
- Join them as: timestamp, method, path with query string and the body hash, separated by dots.
- Make an HMAC-SHA256 of that text with your signing secret, in lowercase hex.
- Send it as X-Sponkly-Signature: v1=<signature>, with the time as X-Sponkly-Timestamp. A signature is good for 5 minutes and for one request.
- Sending the same request twice within one second (a quick retry or a poll)? Give the time in milliseconds instead; it is accepted too and keeps every signature unique.
X-Sponkly-Timestamp: 1792693200
X-Sponkly-Signature: v1=hex( HMAC-SHA256( signing_secret,
"1792693200" + "." + "POST" + "." + "/v1/translate" + "." + hex(SHA-256(body)) ) )// Node.js 18+
import crypto from "node:crypto";
async function sponkly(method, path, body) {
const raw = body === undefined ? "" : JSON.stringify(body);
const ts = Math.floor(Date.now() / 1000).toString();
const hash = crypto.createHash("sha256").update(raw).digest("hex");
const sig = crypto.createHmac("sha256", process.env.SPONKLY_SIGNING_SECRET)
.update(`${ts}.${method}.${path}.${hash}`).digest("hex");
const res = await fetch("https://api.sponkly.com" + path, {
method,
headers: {
Authorization: `Bearer ${process.env.SPONKLY_KEY}`,
"Content-Type": "application/json",
"X-Sponkly-Timestamp": ts,
"X-Sponkly-Signature": `v1=${sig}`,
},
body: raw || undefined,
});
return res.json();
}
console.log(await sponkly("POST", "/v1/translate", { text: "Merhaba dünya", target: "en" }));Keep your keys safe
- Call the API from your server. Never put a key or a signing secret in a web page, a mobile app or a public repository.
- Require signed requests for every key in production.
- List the addresses of your servers in the key's allowed addresses, so the key works from nowhere else.
- Set a daily spending limit per key, so a mistake or a leak can only cost so much.
- Use one key per app or environment, and revoke a key the moment you think it was exposed. You can also make a new signing secret at any time.
- Repeated wrong keys or signatures from one address lock that address out for 15 minutes.
/v1/translateText translation
Translates one text or a list of up to 50 into any of the 32 languages. Leave out the source language to have it detected.
# One text, or up to 50: "text": ["Good morning", "Thank you"]
curl https://api.sponkly.com/v1/translate \
-H "Authorization: Bearer $SPONKLY_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Bugün hava çok güzel.", "target": "en", "source": "tr"}'// JavaScript (Node 18+, Deno, Bun)
const res = await fetch("https://api.sponkly.com/v1/translate", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.SPONKLY_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ text: ["Good morning", "Thank you"], target: "de" }),
});
const { translations } = await res.json(); // [{ text: "Guten Morgen" }, { text: "Danke" }]# Python
import os, requests
r = requests.post("https://api.sponkly.com/v1/translate",
headers={"Authorization": f"Bearer {os.environ['SPONKLY_KEY']}"},
json={"text": "How are you?", "target": "ja", "context": "A message to a hotel guest"})
print(r.json()["translation"])Fields: text (string or array), target (language code), source (optional), context (optional, up to 1,000 characters that tell what the text is about). You pay per character sent.
wss://api.sponkly.com/v1/live?source=en&target=trLive voice translation
A WebSocket for live speech: send the audio as it is spoken and receive translated captions and, if you ask for it, a translated voice. Thirteen major languages are translated by a realtime speech model with about 1–2 seconds of delay; the others by our own streaming pipeline.
// Node.js (npm i ws). Send 16 kHz mono 16-bit PCM in ~100 ms frames, in real time.
import WebSocket from "ws";
const ws = new WebSocket("wss://api.sponkly.com/v1/live?source=en&target=tr&voice=1", {
headers: { Authorization: `Bearer ${process.env.SPONKLY_KEY}` },
});
ws.on("message", (raw) => {
const e = JSON.parse(raw);
if (e.t === "cap" && e.done) console.log("caption:", e.text); // a translated line
if (e.t === "pcm") play(Buffer.from(e.d, "base64"), e.r); // translated voice, 24 kHz PCM
});
microphone.on("data", (frame) => ws.send(frame)); // binary frames
// Browsers cannot set headers: new WebSocket(url, ["sponkly-key", key]). Never put your key in a public page —
// open the socket from your server, or from an app only your own team uses.| Message from the server | What it means |
|---|---|
{"t":"ready","engine":"openai|own"} | The session is open; engine tells which translation path serves this language. |
{"t":"partial","text":"…"} | The speech being recognised right now (it may still change). |
{"t":"cap","id","text","done"} | A translated line: shown as it grows, final when done is true. |
{"t":"pcm","d":"<base64>","r":24000} | A piece of the translated voice: 16-bit mono PCM at the given rate, base64. Play the pieces back to back. |
{"t":"problem","text":"noMinutes"} | Your balance ran out; the session closes. |
Parameters: source and target (language codes), voice=0 for captions only (half price). Send audio in real time (faster is refused). Up to 10 sessions at once per account, 2 hours each. You pay per second of translated speech; silence and music are free.
/v1/speechText to speech
Turns text into natural speech and returns the audio file itself.
curl https://api.sponkly.com/v1/speech \
-H "Authorization: Bearer $SPONKLY_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Merhaba, hoş geldiniz.", "language": "tr", "voice": "female", "format": "mp3"}' \
--output welcome.mp3Fields: text (up to 4,096 characters), language, voice (female, male, neutral, or alloy, ash, ballad, coral, echo, fable, onyx, nova, sage, shimmer, verse), format (mp3, wav or opus), speed (0.5–2). The cost is in the X-Sponkly-Cost-Usd header.
/v1/transcribeSpeech to text
Upload an audio or video file (up to 25 MB and 60 minutes); get the text with timestamps, and its translation if you ask for one.
curl https://api.sponkly.com/v1/transcribe \
-H "Authorization: Bearer $SPONKLY_KEY" \
-F [email protected] \
-F language=tr \
-F translate_to=en
{ "text": "…", "language": "tr", "duration_seconds": 182.4,
"segments": [{ "start": 0.0, "end": 4.2, "text": "…" }],
"translation": { "text": "…", "target": "en" }, "cost_usd": "0.030430" }/v1/videosVideo subtitles and dubbing
Send a video of up to 5 minutes and get it back translated: subtitles burned in, a translated voice, or both, plus subtitle files in both languages.
# 1. Ask for a video: dubbed into Turkish, with Turkish subtitles
curl https://api.sponkly.com/v1/videos -H "Authorization: Bearer $SPONKLY_KEY" -H "Content-Type: application/json" \
-d '{"target": "tr", "dub": true, "subtitles": "translated", "content_type": "video/mp4", "size_bytes": 48211022}'
# → { "id": "…", "upload": { "method": "PUT", "url": "https://…", "headers": { "Content-Type": "video/mp4" } } }
# 2. Upload the file to that link
curl -X PUT -H "Content-Type: video/mp4" --data-binary @talk.mp4 "<upload.url>"
# 3. Start it, then poll until "status": "ready"
curl -X POST https://api.sponkly.com/v1/videos/<id>/start -H "Authorization: Bearer $SPONKLY_KEY"
curl https://api.sponkly.com/v1/videos/<id> -H "Authorization: Bearer $SPONKLY_KEY"
# → { "status": "ready", "cost_usd": "0.500000",
# "result": { "video_url": "https://…", "subtitles": { "tr": "https://…srt", "en": "https://…srt" } } }Fields: target, source (optional), dub (true for a translated voice), subtitles (translated, original, both or none), content_type (video/mp4 or video/quicktime), size_bytes, title (optional). You pay per second of video once its length is known. Results stay downloadable for 7 days.
/v1/balance · /v1/usage · /v1/languagesBalance, usage and languages
Check your balance and when your oldest credit expires, list your billed calls, and get the languages and current prices.
curl https://api.sponkly.com/v1/balance -H "Authorization: Bearer $SPONKLY_KEY"
# → { "balance_usd": "48.213400", "next_expiry": "2027-10-22T09:12:00.000Z" }
curl "https://api.sponkly.com/v1/usage?limit=20" -H "Authorization: Bearer $SPONKLY_KEY"
# → { "data": [{ "product": "live", "units": 312.5, "unit": "second", "cost_usd": "0.520834", … }], "has_more": false }Errors
Errors come back as JSON with a stable code and a readable message.
{ "error": { "code": "insufficient_balance", "message": "…" } }| 400 | invalid_text · invalid_target · text_too_long … | Something in the request is missing or not allowed; the message says what. |
| 401 | missing_key · invalid_key | No API key, or a key that is wrong or revoked. |
| 402 | insufficient_balance | Your balance does not cover this call. Add credit in the dashboard. |
| 429 | — | Too many requests at once. Wait a moment and retry. |
| 502 | translation_failed · speech_failed … | It failed on our side or at a provider. You were not charged; retry. |
Limits
- Text translation: up to 50 texts and 20,000 characters per request, 5,000 per text.
- Text to speech: up to 4,096 characters per request.
- Speech to text: files up to 25 MB and 60 minutes.
- Live translation: up to 10 sessions at once per account, 2 hours each, audio in real time.
- Videos: up to 5 minutes and 4 GB, 3 being made at once; results kept 7 days.
- Rates per key: 300 translations, 120 speech, 60 transcriptions and 20 new videos a minute.
- 32 languages: English, Turkish, German, French, Spanish, Italian, Portuguese, Russian, Japanese, Korean, Chinese, Arabic, Hindi, Dutch, Polish, Swedish, Norwegian, Danish, Finnish, Greek, Czech, Romanian, Hungarian, Ukrainian, Hebrew, Thai, Vietnamese, Indonesian, Malay, Estonian, Persian and Catalan.
Billing
- Prepaid: add credit by card from $10; every call is taken from your balance.
- The card processing fee is shown and added at checkout, so your whole credit is yours to use.
- Credit can be used for 12 months after you buy it. It is not refundable.
- A call that fails on our side is not charged, and a video that fails is refunded.
- When your balance runs low we email you; at zero, calls are refused with 402 until you add credit.