Developer API

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.

Live voice translation
Stream speech in, get translated captions and a translated voice back in seconds.
$0.10 per minute
Live captions
The same live stream, translated captions only.
$0.05 per minute
Text translation
Natural, context-aware translation of one text or up to 50.
$10.00 per 1M characters
Text to speech
Text read aloud in a natural voice, as MP3, WAV or Opus.
$30.00 per 1M characters
Speech to text
Audio or video to text with timestamps, optionally translated.
$0.01 per minute
Video subtitles
A video back with translated subtitles burned in, plus SRT files.
$0.20 per minute
Video dubbing
A video back spoken in another language, with subtitles if you like.
$0.50 per minute

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.

  1. Sign up free on sponkly.com and open Dashboard → Developer API.
  2. Add credit (from $10) and create an API key.
  3. 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.

  1. 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).
  2. Join them as: timestamp, method, path with query string and the body hash, separated by dots.
  3. Make an HMAC-SHA256 of that text with your signing secret, in lowercase hex.
  4. 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.
  5. 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.
POST/v1/translate

Text 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.

WSwss://api.sponkly.com/v1/live?source=en&target=tr

Live 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 serverWhat 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.

POST/v1/speech

Text 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.mp3

Fields: 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.

POST/v1/transcribe

Speech 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" }
POST/v1/videos

Video 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.

GET/v1/balance · /v1/usage · /v1/languages

Balance, 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": "…" } }
400invalid_text · invalid_target · text_too_long …Something in the request is missing or not allowed; the message says what.
401missing_key · invalid_keyNo API key, or a key that is wrong or revoked.
402insufficient_balanceYour balance does not cover this call. Add credit in the dashboard.
429—Too many requests at once. Wait a moment and retry.
502translation_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.