Speech-to-Text API

Turn audio and video into text from your own code. One key gives you the whole pipeline: any input format, automatic language detection (~99 languages), long-file chunking, and LLM post-processing — clean text, summaries, action items, translation.

Quick start

1. Sign up and confirm your email. 2. In your account, open “API keys” and create a key (shown once). 3. Send your first request:

Upload a file and wait for the result:

curl -X POST https://oratext.com/api/v1/transcriptions \
  -H "Authorization: Bearer ora_sk_YOUR_KEY" \
  -F "file=@meeting.mp3" \
  -F "wait=true"

With wait=true the server holds the connection for up to ~90 seconds and returns the finished text in one request — enough for most files. For long recordings use the asynchronous flow below.

Authentication

Every request needs the header Authorization: Bearer ora_sk_… . Keys are created in your account and can be revoked at any time. Keep the key secret: anyone who has it spends your minutes.

Authorization: Bearer ora_sk_YOUR_KEY

Sign up →

Create a transcription

POST /api/v1/transcriptions

POST /api/v1/transcriptions accepts either multipart/form-data with a file, or JSON/form with a url — we download the file ourselves (public hosts, ports 80/443, up to 3 redirects). Exactly one source per request.

ParameterDescription
fileAudio or video file (multipart/form-data). MP3, WAV, M4A, OGG, MP4, MOV and most other formats.
urlDirect http(s) link to the file instead of uploading it.
levelProcessing level: standard, premium or ultra. Defaults to the best level of your plan. Higher levels use stronger models.
modeOptional post-processing applied right after transcription: clean (remove filler words), summary, tasks or translate. The result is returned in mode_text next to the raw text.
target_langTarget language for mode=translate, e.g. “English” or “Español”.
waittrue — hold the request until the result is ready (up to ~90 s). If time runs out you get 202 with the id — poll it as usual.

Or pass a link — with a summary ordered right away:

curl -X POST https://oratext.com/api/v1/transcriptions \
  -H "Authorization: Bearer ora_sk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/podcast.mp3", "mode": "summary", "wait": "true"}'

Success codes: 201 — task created (no wait), 200 — wait finished with the result, 202 — wait timed out, keep polling by id.

Get the result

GET /api/v1/transcriptions/{id}

Poll GET /api/v1/transcriptions/{id} every 1–3 seconds. Status values: queued → processing → done; error means processing failed (minutes are not lost — the job did not complete), rejected means a business rule stopped it (quota, duration).

Check status / fetch the result:

curl -H "Authorization: Bearer ora_sk_YOUR_KEY" \
  https://oratext.com/api/v1/transcriptions/JOB_ID

Response when done:

{
  "id": "3f2b8c1e-5a70-4a3e-9c0f-1d2e3f4a5b6c",
  "status": "done",
  "level": "standard",
  "duration_sec": 184.2,
  "language": "ru",
  "mode": "summary",
  "text": "…full transcript…",
  "mode_text": "…summary…"
}

Post-process the text

POST /api/v1/transcriptions/{id}/process

Any finished transcription can be re-processed without new minutes: POST /api/v1/transcriptions/{id}/process with mode and, for translation, target_lang. Up to 20 processing calls per transcription.

curl -X POST https://oratext.com/api/v1/transcriptions/JOB_ID/process \
  -H "Authorization: Bearer ora_sk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"mode": "translate", "target_lang": "English"}'

Check your quota

GET /api/v1/usage

GET /api/v1/usage returns your plan, the limit, used and remaining minutes, and when the quota resets. Call it before sending big files.

{
  "plan": "free",
  "period": "day",
  "limit_minutes": 5.0,
  "used_minutes": 1.2,
  "remaining_minutes": 3.8,
  "resets_at": "2026-08-06T00:00:00+00:00"
}

Python example:

import requests, time

API = "https://oratext.com/api/v1"
HEADERS = {"Authorization": "Bearer ora_sk_YOUR_KEY"}

with open("meeting.mp3", "rb") as f:
    r = requests.post(f"{API}/transcriptions", headers=HEADERS,
                      files={"file": f}, data={"mode": "summary", "wait": "true"})
job = r.json()
if not r.ok:
    raise SystemExit(job["error"]["message"])

while job["status"] not in ("done", "error", "rejected"):
    time.sleep(2)
    job = requests.get(f"{API}/transcriptions/{job['id']}", headers=HEADERS).json()

if job["status"] == "done":
    print(job["text"])
    print(job.get("mode_text"))
else:
    print("failed:", job["error"]["message"])

JavaScript (Node 18+) example:

const API = "https://oratext.com/api/v1";
const headers = { Authorization: "Bearer ora_sk_YOUR_KEY" };

const res = await fetch(`${API}/transcriptions`, {
  method: "POST",
  headers: { ...headers, "Content-Type": "application/json" },
  body: JSON.stringify({ url: "https://example.com/podcast.mp3", wait: "true" }),
});
let job = await res.json();
if (!res.ok) throw new Error(job.error.message);

while (!["done", "error", "rejected"].includes(job.status)) {
  await new Promise(r => setTimeout(r, 2000));
  job = await (await fetch(`${API}/transcriptions/${job.id}`, { headers })).json();
}
console.log(job.status === "done" ? job.text : job.error.message);

Limits

Errors

Errors come as JSON: {"error": {"code": "…", "message": "…"}}. The main codes:

HTTPDescription
400bad_request / invalid_level / invalid_mode / url_invalid / url_blocked / url_failed — a parameter or the url is wrong; details in message.
401Missing, invalid or revoked API key.
402quota_exceeded — not enough minutes left on the plan; the response includes remaining_minutes.
403email_unverified, level_not_allowed or llm_limit — confirm your email; the requested level is above your plan; or the 20-processing cap for that transcription is used up.
404not_found — no transcription with this id on this API key.
409not_ready — the transcription is not finished yet; wait for status done.
413file_too_large / too_long — the file exceeds the size or duration limit.
415unsupported_media — we could not decode audio from the file.
429rate_limited — too many requests per minute; slow down and retry.
502llm_failed — text processing failed; retry later.

Questions?

Write to us in Telegram: @oratextbot — we answer quickly and are happy to help with integration.