audio transcription

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.

Try it now Back to API Hub

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:

1

Log In to Your Account

Existing Users: Go to /login to access your dashboard

New Users: Visit /register to create your free account first

2

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.

3

Create API Key

From your profile page, proceed to the "API Keys" section and create a key by filling in a name you prefer.

Example name

"Transcription bot" or "My Podcast Pipeline"

4

Save Your Credentials

Click on "Create API Key" to generate your credentials, then immediately copy and save both the API Key and Secret.

Critical Warning

Your secret key will only be displayed once! Copy it immediately and store it securely. You won't be able to see it again.

Security Notice

Never share your API keys publicly or commit them to version control. Treat them like passwords — regenerate immediately if compromised.

Rate Limits

8 requests per minute per API key. Check your dashboard for current usage and limits.

Endpoints

💚 Health CheckGET /stt/health
🎙️ Transcribe AudioPOST /stt/transcribe
⏳ Transcribe (Async)POST /stt/transcribe/async
📋 Job StatusGET /stt/jobs/{job_id}
⬇️ Download ResultGET /stt/jobs/{job_id}/download

Health Check

GET/stt/health

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

POST/stt/transcribe

Transcribe an audio file to text synchronously. Upload the file as multipart/form-data.

Form Parameters

FieldTypeDefaultDescription
fileFileAudio file (required) — WAV, MP3, MPGA, M4A, FLAC, or OGG, up to 50MB
backendstringwhisperTranscription backend — whisper or google
modelstringbaseWhisper model size (whisper only) — base only (larger sizes are disabled)
Response

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)

POST/stt/transcribe/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.

FieldTypeDefaultDescription
fileFileAudio file (required) — WAV, MP3, MPGA, M4A, FLAC, or OGG, up to 50MB
backendstringwhisperTranscription backend — whisper or google
modelstringbaseWhisper 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

GET/stt/jobs/{job_id}

Poll the status of an asynchronous transcription job.

Path Parameters

FieldTypeDescription
job_idstringJob 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"
}
Ownership

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

GET/stt/jobs/{job_id}/download

Download the transcript produced by a completed async job.

Path Parameters

FieldTypeDescription
job_idstringJob ID of a completed (done) job (required)
Response

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

CodeMeaning
200Success
400Bad request / invalid parameters / unsupported file format / job not finished
401Invalid or missing API key/secret
403Access denied / job owned by another API key
404Job not found
413File too large (max 50MB)
429Rate limit exceeded
500Internal server error / transcription failed

Limitations

Maximum file size: 50MB
Rate limit: 8 requests per minute
Supported formats: WAV, MP3, MPGA, M4A, FLAC, OGG
Backends: whisper (default, model base) and google
Processing time: Longer files should use the async endpoint