Speech-to-Text
API Documentation
Transcribe audio files into accurate text using Whisper or Google Speech Recognition backends — synchronously or through async jobs — over a simple REST API. Built for developers.
Overview
The Speech-to-Text API converts audio files (WAV, MP3, M4A, FLAC, OGG, and more) into text. The whisper backend offers selectable model sizes for different accuracy/speed trade-offs, while the google backend uses Google Speech Recognition. Synchronous transcription returns the transcript directly; async jobs are available for longer audio files.
Base URL & Authentication
Base URL
Base URL
BASE_URL = "https://odivora.com/api/v1"
Set BASE_URL to <your STT server url>/api/v1 — the /api/v1 prefix is part of the base URL and must be included.
Authentication
Include your API credentials in every request header.
Headers
X-API-Key: your_api_key
X-API-Secret: your_secret
Content-Type: application/json
Get API Keys
Follow these steps to obtain your API credentials:
Access Your Profile
Once you log in, click on your profile picture in the top-right corner, then click on "Your profile" from the dropdown menu.
Create API Key
From your profile page, proceed to the "API Keys" section and create a key by filling in a name you prefer.
"Transcription bot" or "My Podcast Pipeline"
Save Your Credentials
Click on "Create API Key" to generate your credentials, then immediately copy and save both the API Key and Secret.
Your secret key will only be displayed once! Copy it immediately and store it securely. You won't be able to see it again.
Never share your API keys publicly or commit them to version control. Treat them like passwords — regenerate immediately if compromised.
8 requests per minute per API key. Check your dashboard for current usage and limits.
Endpoints
GET /stt/healthPOST /stt/transcribePOST /stt/transcribe/asyncGET /stt/jobs/{job_id}GET /stt/jobs/{job_id}/downloadHealth Check
Health check endpoint to verify the Speech-to-Text service status, available backends, and supported formats.
Response
Success Response
{
"success": true,
"status": "healthy",
"stt_backends": {
"whisper": true,
"google": true
},
"supported_formats": [".wav", ".mp3", ".mpga", ".m4a", ".flac", ".ogg"],
"max_file_size_mb": 50,
"async_stt": true,
"timestamp": "2026-08-07T12:00:00"
}
Code Examples
Python
import requests
url = "https://odivora.com/api/v1/stt/health"
headers = {
"X-API-Key": "your_api_key",
"X-API-Secret": "your_secret"
}
res = requests.get(url, headers=headers)
print(res.json())
cURL
curl -X GET https://odivora.com/api/v1/stt/health \
-H "X-API-Key: your_api_key" \
-H "X-API-Secret: your_secret"
JavaScript
fetch('https://odivora.com/api/v1/stt/health', {
method: 'GET',
headers: {
'X-API-Key': 'your_api_key',
'X-API-Secret': 'your_secret'
}
})
.then(res => res.json())
.then(console.log);
Transcribe Audio
Transcribe an audio file to text synchronously. Upload the file as multipart/form-data.
Form Parameters
| Field | Type | Default | Description |
|---|---|---|---|
file | File | — | Audio file (required) — WAV, MP3, MPGA, M4A, FLAC, or OGG, up to 50MB |
backend | string | whisper | Transcription backend — whisper or google |
model | string | base | Whisper model size (whisper only) — base only (larger sizes are disabled) |
On success the endpoint returns JSON with the transcript, the backend/model used, and audio file info.
Response
Success Response
{
"success": true,
"transcript": "Hello world, this is a test recording.",
"backend": "whisper",
"model": "base",
"file_info": {
"size_mb": 2.4,
"duration_seconds": 8.5
}
}
cURL Example
cURL
curl -X POST "https://odivora.com/api/v1/stt/transcribe" \
-H "X-API-Key: your_api_key" \
-H "X-API-Secret: your_secret" \
-F "file=@recording.mp3" \
-F "backend=whisper" \
-F "model=base"
Code Examples
Python
import requests
url = "https://odivora.com/api/v1/stt/transcribe"
headers = {
"X-API-Key": "your_api_key",
"X-API-Secret": "your_secret"
}
with open("recording.mp3", "rb") as f:
files = {"file": f}
data = {"backend": "whisper", "model": "base"}
response = requests.post(url, headers=headers, files=files, data=data)
if response.status_code == 200:
result = response.json()
print("Transcript:", result.get("transcript"))
else:
print(response.json())
JavaScript
const form = new FormData();
form.append('file', fileInput.files[0]);
form.append('backend', 'whisper');
form.append('model', 'base');
fetch('https://odivora.com/api/v1/stt/transcribe', {
method: 'POST',
headers: {
'X-API-Key': 'your_api_key',
'X-API-Secret': 'your_secret'
},
body: form
})
.then(res => res.json())
.then(data => console.log(data.transcript));
Transcribe Audio (Async)
Submit a transcription job and receive a job_id immediately. Use the job endpoints to poll for progress and download the result. Ideal for long audio files.
Form Parameters
Same parameters as the synchronous endpoint.
| Field | Type | Default | Description |
|---|---|---|---|
file | File | — | Audio file (required) — WAV, MP3, MPGA, M4A, FLAC, or OGG, up to 50MB |
backend | string | whisper | Transcription backend — whisper or google |
model | string | base | Whisper model size (whisper only) — base only (larger sizes are disabled) |
Response
Success Response
{
"success": true,
"job_id": "9c14d5f7a2b34e8c",
"status": "queued"
}
cURL Example
cURL
curl -X POST "https://odivora.com/api/v1/stt/transcribe/async" \
-H "X-API-Key: your_api_key" \
-H "X-API-Secret: your_secret" \
-F "file=@recording.mp3" \
-F "backend=whisper" \
-F "model=base"
Get Job Status
Poll the status of an asynchronous transcription job.
Path Parameters
| Field | Type | Description |
|---|---|---|
job_id | string | Job ID returned by the async submit endpoint (required) |
Response
Success Response
{
"success": true,
"job_id": "9c14d5f7a2b34e8c",
"status": "done",
"filename": "recording.mp3",
"created_at": "2026-08-07 12:00:01",
"updated_at": "2026-08-07 12:00:08",
"result_url": "/api/v1/stt/jobs/9c14d5f7a2b34e8c/download"
}
Jobs are bound to the API key that created them. Accessing another key's job returns 403 Access denied.
cURL Example
cURL
curl -X GET "https://odivora.com/api/v1/stt/jobs/9c14d5f7a2b34e8c" \
-H "X-API-Key: your_api_key" \
-H "X-API-Secret: your_secret"
Download Transcription Result
Download the transcript produced by a completed async job.
Path Parameters
| Field | Type | Description |
|---|---|---|
job_id | string | Job ID of a completed (done) job (required) |
On success the endpoint returns the transcript text file as an attachment.
cURL Example
cURL
curl -X GET "https://odivora.com/api/v1/stt/jobs/9c14d5f7a2b34e8c/download" \
-H "X-API-Key: your_api_key" \
-H "X-API-Secret: your_secret" \
--output transcript.txt
Python SDK
Complete Python SDK covering health check, synchronous transcription, async job submission, polling, and download.
Complete SDK Class
import requests
import time
import os
BASE_URL = "https://odivora.com/api/v1"
API_KEY = "YOUR_API_KEY"
API_SECRET = "YOUR_API_SECRET"
headers = {
"X-API-Key": API_KEY,
"X-API-Secret": API_SECRET
}
# --- Health Check ---
def check_health():
return requests.get(f"{BASE_URL}/stt/health", headers=headers).json()
# --- Transcribe audio synchronously ---
def transcribe(audio_path, backend="whisper", model="base"):
with open(audio_path, "rb") as audio_file:
files = {"file": audio_file}
data = {"backend": backend, "model": model}
response = requests.post(
f"{BASE_URL}/stt/transcribe",
headers=headers,
files=files,
data=data
)
if response.status_code == 200:
result = response.json()
print(f"Transcript: {result.get('transcript')}")
return result.get("transcript")
else:
print(f"Error: {response.status_code} - {response.text}")
return None
# --- Submit async transcription job ---
def transcribe_async(audio_path, backend="whisper", model="base"):
with open(audio_path, "rb") as audio_file:
files = {"file": audio_file}
data = {"backend": backend, "model": model}
response = requests.post(
f"{BASE_URL}/stt/transcribe/async",
headers=headers,
files=files,
data=data
)
if response.status_code == 200:
job_id = response.json().get("job_id")
print(f"Job submitted: {job_id}")
return job_id
else:
print(f"Error: {response.status_code} - {response.text}")
return None
# --- Poll job status ---
def get_job_status(job_id):
return requests.get(f"{BASE_URL}/stt/jobs/{job_id}", headers=headers).json()
# --- Download transcription result ---
def download_transcript(job_id, output_path="transcript.txt"):
response = requests.get(f"{BASE_URL}/stt/jobs/{job_id}/download", headers=headers)
if response.status_code == 200:
with open(output_path, "wb") as f:
f.write(response.content)
print(f"Transcript saved to: {output_path}")
return output_path
else:
print(f"Error: {response.status_code} - {response.text}")
return None
# --- Wait for a job to complete and download ---
def transcribe_and_wait(audio_path, output_path="transcript.txt", backend="whisper", model="base"):
job_id = transcribe_async(audio_path, backend=backend, model=model)
if not job_id:
return None
while True:
status = get_job_status(job_id).get("status")
print(f"Job status: {status}")
if status == "done":
return download_transcript(job_id, output_path)
if status == "failed":
print("Job failed")
return None
time.sleep(2)
# --- Test all endpoints ---
if __name__ == "__main__":
audio_path = "recording.mp3"
print("=== STT API TEST ===")
health = check_health()
print(f"Health: {health.get('status', 'unknown')}")
print(f"Backends: {health.get('stt_backends', {})}")
print("\n1. Synchronous transcription:")
transcribe(audio_path, backend="whisper", model="base")
print("\n2. Async transcription with download:")
transcribe_and_wait(audio_path, output_path="transcript.txt")
Errors & Status Codes
Error Response Format
Error Response
{
"success": false,
"error": "No audio file provided",
"message": "A 'file' field is required"
}
Status Codes
| Code | Meaning |
|---|---|
200 | Success |
400 | Bad request / invalid parameters / unsupported file format / job not finished |
401 | Invalid or missing API key/secret |
403 | Access denied / job owned by another API key |
404 | Job not found |
413 | File too large (max 50MB) |
429 | Rate limit exceeded |
500 | Internal server error / transcription failed |