Translation and text tools
Which languages are available: GET https://ai.ollalink.com/v1/languages lists them, with what each supports (all hosts).
Base: https://ai.ollalink.com · Auth: X-NH-GPU-Key: <your GPU API key>
Translate text
Section titled “Translate text”POST /v1/translate — send the text and the target language; you get the translation back. The service writes its own
translation instructions, so the text is always treated as content to translate — it is a translation endpoint, not a general
chat model, and instructions inside the text are translated, not followed. It is always on: a sentence typically comes back in
about 1.5–3 s.
curl https://ai.ollalink.com/v1/translate \ -H "X-NH-GPU-Key: $YOUR_GPU_KEY" -H "Content-Type: application/json" \ -d '{"text": "Do not open port 443 before Friday.", "target": "es"}'{"translation": "No abras el puerto 443 antes del viernes.", "source": null, "target": "es", "numbers_verified": true, "characters": 35}Request
Section titled “Request”| Field | Required | Values |
|---|---|---|
text |
yes | the text to translate, up to 5,000 characters per request |
target |
yes | language code: en, hi, es, fr, zh, de, ar, pt (Brazilian Portuguese), ru, ja, ko, it, nl, vi, ta (Tamil — text translation only); any other code (including tr) returns 400 unsupported_language |
source |
no | the source language code (same list as target), when you know it; omit it to let the translator work it out (Tamil text is recognised by its script) |
stream |
no | true to receive the translation piece by piece while it is written (see Streaming); default false |
No other fields are accepted (400). Use your GPU API key from the dashboard — the same key as the Speech GPU
services — in X-NH-GPU-Key.
Response
Section titled “Response”| Field | Meaning |
|---|---|
translation |
the translated text |
numbers_verified |
true when every number and placeholder in the source also appears in the translation; false means review them |
source, target |
echo of the request (source is null when you omitted it) |
characters |
length of the source text; usage is counted in characters (also in the X-Translate-Chars header) |
What is kept
Section titled “What is kept”- Meaning: negations, commands, questions and conditions are kept as they are (“Do not open port 443” stays a prohibition).
- Numbers: spoken numbers in the source (“port four forty three”) are turned into digits before translation, and values are kept exactly, written with the target language’s decimal separator.
- Technical tokens: names, product names, protocol names such as BGP, commands, IP addresses and placeholders
(
{name},%d,{{x}}) are kept as written; placeholders are protected during translation. - Check: after translating, the service checks that every number and placeholder survived and retries once if one is
missing;
numbers_verifiedreports the result.
A successful response and numbers_verified: true do not certify meaning or grammar; review critical text.
Streaming ("stream": true)
Section titled “Streaming ("stream": true)”For live captions, or anywhere the first words matter more than the whole text: add "stream": true and the translation
arrives as Server-Sent Events while it is being written. The first words usually arrive within a second; without streaming
you wait for the whole translation.
curl -N https://ai.ollalink.com/v1/translate \ -H "X-NH-GPU-Key: $YOUR_GPU_KEY" -H "Content-Type: application/json" \ -d '{"text": "Open port 443 on VLAN 10 and save the configuration.", "target": "hi", "stream": true}'data: {"delta": "V"}
data: {"delta": "LAN 10 पर पोर्ट 443"}
data: {"delta": " खोलें और कॉन्फ़िगरेशन सहेजें।"}
data: {"done": true, "translation": "VLAN 10 पर पोर्ट 443 खोलें और कॉन्फ़िगरेशन सहेजें।", "source": null, "target": "hi", "numbers_verified": true, "characters": 52}The response is Content-Type: text/event-stream. Each event is one data: line holding JSON, followed by a blank line:
| Event | Meaning |
|---|---|
{"delta": "…"} |
the next piece of the translation; append the pieces in order to show it as it grows |
{"done": true, "translation", "numbers_verified", "source", "target", "characters"} |
the last event: the complete translation (use it as the final text) and the number check |
{"error": {"code": "translation_unavailable", "message"}} |
the translation stopped part-way; discard the pieces and send the request again |
import json, requests
with requests.post("https://ai.ollalink.com/v1/translate", headers={"X-NH-GPU-Key": YOUR_GPU_KEY}, json={"text": "Open port 443 on VLAN 10 and save the configuration.", "target": "hi", "stream": True}, stream=True, timeout=60) as r: r.raise_for_status() for line in r.iter_lines(decode_unicode=True): if not line.startswith("data:"): continue event = json.loads(line[5:]) if "delta" in event: print(event["delta"], end="", flush=True) # show the translation as it grows elif event.get("done"): final, verified = event["translation"], event["numbers_verified"] elif "error" in event: raise RuntimeError(event["error"]["message"]) # send the request againHow streaming differs from the normal call:
- No automatic retry. Without streaming, a translation that lost a number or placeholder is retried once before it is
returned. With streaming the text is already on its way, so
numbers_verifiedin thedoneevent only reports the check. Onfalse, review the numbers, or send the text again withoutstreamto get the retry. - Errors before the stream starts (missing key, bad request, unsupported language, text too long) come back as ordinary
JSON responses with the usual status codes. Only failures during translation arrive as an
errorevent. - Usage is counted the same way, in source characters (header
X-Translate-Chars). - Captions: send each caption
finalas soon as it arrives, and do not wait for the previous translation; requests run in parallel. Show thedeltapieces as they come, then replace them withtranslationfrom thedoneevent.
Hindi with English numbers and technical terms
Section titled “Hindi with English numbers and technical terms”Hindi and Hindi–English mixed (Hinglish) text can be translated directly with "target": "en" — for example, a Hinglish
instruction comes back as “Do not open port 179 until the configuration is verified.” For live translation of Hindi speech,
translate each caption’s final text here. See Hindi with English numbers.
Captions and recordings in another language
Section titled “Captions and recordings in another language”Transcribe the audio (speech-to-text or live captions — captions arrive in the speaker’s language only), translate the text here, then optionally synthesize it (text-to-speech). Keep the original transcript next to the translation.
Text tools
Section titled “Text tools”Three more routes on https://ai.ollalink.com, same key, usage counted in characters (header X-Translate-Chars), rate limit
600 requests / 60 s per key.
Spoken numbers to digits — POST /v1/normalize {"text", "language": "en" (default) | "hi"} →
{"text", "normalized", "changed", "language", "characters"}. For recogniser output: “open port four forty three” → “open port 443”,
“two point five seconds” → “2.5 seconds”, “one ninety two dot zero dot two dot one” → “192.0.2.1”, “at two thirty” → “2:30”, “one hundred and
five” → “105”; a lone “one” or “two” stays a word (“one of the reasons”). Hindi converts cardinal numbers
(“पोर्ट चार सौ तैंतालीस खोलो” → “पोर्ट 443 खोलो”, “उन्नीस सौ तिरसठ” → 1963) and English numbers spelled in Devanagari (“वन सेवेंटी नाइन” → 179); a lone एक or दो stays a word. Up to 5,000 characters. Realtime speech-to-text, live captions and file transcription already apply this
to their English text.
curl https://ai.ollalink.com/v1/normalize \ -H "X-NH-GPU-Key: $YOUR_GPU_KEY" -H "Content-Type: application/json" \ -d '{"text": "open port four forty three", "language": "en"}'Text embeddings — POST /v1/embeddings {"input": "text" | ["text", …] (1–256), "dimensions": 1024 (default and only size; shorter vectors lose accuracy), "mode": "document" (default; what you store and search) | "query" (the search text)} → {"object": "list", "data": [{"index", "embedding": [...]}], "dimensions", "mode", "characters"}. Vectors are unit length, so a dot product is the cosine
similarity. 1024-dimension vectors since 17 October 2026 (previously 768 by default): vectors made before that date are not compatible —
re-embed stored documents. At most 5,000 characters per text and 50,000 per request.
Classification — POST /v1/classify {"text", "questions": {<id>: …}} with up to 20 questions of three kinds:
| Kind | Question shape | Answer |
|---|---|---|
choice (pick one — the reliable kind) |
{"type": "choice", "question": "…", "options": {"name": "short description", … (2–10)}} |
{"type": "choice", "answer": name, "confidence", "probabilities"} — confidence is the probability of the chosen option |
yes_no (experimental) |
{"type": "yes_no", "question": "…"} |
{"type": "yes_no", "answer": true/false, "probability_yes", "experimental": true} |
score (experimental) |
{"type": "score", "question": "…", "levels": ["lowest", …, "highest"] (2–10)} |
{"type": "score", "answer": level index, "level", "confidence", "probabilities", "experimental": true} — the most likely level, never an average |
The response is {"answers": {<id>: …}, "characters"}. Pick-one is the reliable kind; yes_no and score are experimental.
For a yes/no decision, ask a choice with two named options and one-line criteria (“question: it asks something” /
“statement: it tells or instructs”). score is best read with its confidence. Up to 5,000 characters. Classification is
always on; a busy period answers 503, retry shortly.
Example request:
{"text": "My invoice for October charged me twice. Please refund one payment.", "questions": { "team": {"type": "choice", "question": "Which team should handle this?", "options": {"billing": "money, invoices, refunds", "support": "technical problems", "sales": "buying or upgrading"}}, "refund": {"type": "choice", "question": "Is the customer asking for a refund?", "options": {"yes": "asks for money back", "no": "does not ask for money back"}}, "urgency": {"type": "score", "question": "How urgent is it?", "levels": ["not urgent", "low", "medium", "high", "critical"]}}}Answer: {"answers": {"team": {"type": "choice", "answer": "billing", "confidence": 0.89, "probabilities": {…}}, "refund": {…}, "urgency": {"type": "score", "answer": 2, "level": "medium", …, "experimental": true}}, "characters": 66}.
Errors
Section titled “Errors”| Status | error.code |
Meaning |
|---|---|---|
400 |
bad_request / unsupported_language |
missing text, unknown field, body not JSON, a language code not in the list on /v1/translate, or a language other than en / hi on /v1/normalize |
401 / 403 |
unauthorized / not_approved / forbidden |
unknown key, key not yet approved, or a key without GPU access; a missing key is blocked at the edge with 403 |
410 |
gone |
a route switched off on this host |
413 |
too_long |
text over the limit (5,000 characters) — split it at sentence ends |
503 |
translation_unavailable / embeddings_unavailable / classification_unavailable |
translation, embeddings or classification briefly unavailable (or loading); retry shortly |
Dashboard
Section titled “Dashboard”Use Playground → Translate for text, or Live speech → Translate finalized captions for live caption text. The latter composes two documented APIs: caption finals followed by text translation. Original captions remain visible.
