audio cap
For developers

Transcription API

Send a voice message, a recording or a video file from your own code and get the transcript back as JSON, with the language detected and, if you ask, translated. The same engine as the website, for your apps, bots and scripts.

Quick start

  1. 1 Buy any credit package once.
  2. 2 Create an API key in Settings.
  3. 3 Send your first request with one of the examples below.

What it does

  • Speech to text for audio and video files, with the spoken language detected for you.
  • Translations into up to 5 languages in the same call.
  • One synchronous request: you get the answer as soon as the transcript is ready, for a voice message usually within seconds.
  • The same file sent again is answered from the stored transcript, without waiting for a second transcription.

Authentication

Every request carries a personal API key as a Bearer token in the Authorization header. Create one in Settings under API keys. It is shown once, so store it where your code can read it, and never in a browser or an app you ship. A key only works for the endpoints on this page.

API keys are for paying accounts: an account that has bought credits at least once, or a premium account.

HTTP Copied
Authorization: Bearer YOUR_API_KEY
Create a key in Settings

Pricing

Paid from your credit balance, per started minute of audio, after the transcript is ready. A call that fails costs nothing.

Transcription 10 credits per minute
Translation 5 credits per minute and language
500 credits about 50 minutes of transcription
2,000 credits about 200 minutes of transcription
5,000 credits about 500 minutes of transcription

Example: a 77 second voice message counts as 2 minutes, so 20 credits, or 30 credits with one translation.

Asking for the language the recording is already in is not a translation and costs nothing.

Sending the same file twice is charged twice, because you receive the transcript twice. To retry safely after a timeout, send an Idempotency-Key header: a key you already used returns the first answer and charges nothing.

Endpoints

POST /api/v1/transcriptions Create a transcription

A multipart upload. The response comes with status 201 once the transcript is ready.

Parameters
file file body required
The audio or video file.
translate string body optional
Comma-separated target languages, such as de,en. At most 5.
Idempotency-Key string header optional
Any string up to 100 characters. Repeating it returns the first answer and charges nothing.
cURL Copied
curl -X POST https://audiocap.app/api/v1/transcriptions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Idempotency-Key: 6f1c2b7e-voice-0001" \
  -F "file=@voice-message.ogg" \
  -F "translate=de"
JavaScript Copied
import { openAsBlob } from 'node:fs';

const form = new FormData();
form.append('file', await openAsBlob('voice-message.ogg'), 'voice-message.ogg');
form.append('translate', 'de');

const res = await fetch('https://audiocap.app/api/v1/transcriptions', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer YOUR_API_KEY',
    'Idempotency-Key': '6f1c2b7e-voice-0001',
  },
  body: form,
});

console.log(await res.json());
Python Copied
import requests

res = requests.post(
    "https://audiocap.app/api/v1/transcriptions",
    headers={"Authorization": "Bearer YOUR_API_KEY", "Idempotency-Key": "6f1c2b7e-voice-0001"},
    files={"file": open("voice-message.ogg", "rb")},
    data={"translate": "de"},
)

print(res.json())
PHP Copied
<?php

$client = new GuzzleHttp\Client();

$res = $client->request('POST', 'https://audiocap.app/api/v1/transcriptions', [
    'headers' => [
        'Authorization' => 'Bearer YOUR_API_KEY',
        'Idempotency-Key' => '6f1c2b7e-voice-0001',
    ],
    'multipart' => [
        ['name' => 'file', 'contents' => fopen('voice-message.ogg', 'r')],
        ['name' => 'translate', 'contents' => 'de'],
    ],
]);

echo $res->getBody();
Response 201 Created JSON Copied
{
    "data": {
        "id": 1234,
        "slug": "k3v9q2m7x1ab",
        "file": "voice-message.ogg",
        "status": "completed",
        "language": "en",
        "seconds": 6.4000000000000003552713678800500929355621337890625,
        "text": "Hi, it's me. I'm running ten minutes late, see you at the station.",
        "error": null,
        "translations": {
            "de": "Hallo, ich bin es. Ich komme zehn Minuten später, wir sehen uns am Bahnhof."
        }
    },
    "billing": {
        "credits_charged": 15,
        "minutes": 1,
        "transcription": 10,
        "translation": 5,
        "translated_languages": [
            "de"
        ],
        "balance": 485,
        "receipt_id": 42,
        "replayed": false
    }
}
Show less

GET /api/v1/transcriptions/{id} Fetch a transcription

Returns a transcription this account created or paid for, with every translation that already exists. Free, and it never starts a new translation.

Parameters
id integer path required
The id from the create response.
cURL Copied
curl https://audiocap.app/api/v1/transcriptions/1234 \
  -H "Authorization: Bearer YOUR_API_KEY"
JavaScript Copied
const res = await fetch('https://audiocap.app/api/v1/transcriptions/1234', {
  method: 'GET',
  headers: {
    Authorization: 'Bearer YOUR_API_KEY',
  },
});

console.log(await res.json());
Python Copied
import requests

res = requests.get(
    "https://audiocap.app/api/v1/transcriptions/1234",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
)

print(res.json())
PHP Copied
<?php

$client = new GuzzleHttp\Client();

$res = $client->request('GET', 'https://audiocap.app/api/v1/transcriptions/1234', [
    'headers' => [
        'Authorization' => 'Bearer YOUR_API_KEY',
    ],
]);

echo $res->getBody();
Response 200 OK JSON Copied
{
    "data": {
        "id": 1234,
        "slug": "k3v9q2m7x1ab",
        "file": "voice-message.ogg",
        "status": "completed",
        "language": "en",
        "seconds": 6.4000000000000003552713678800500929355621337890625,
        "text": "Hi, it's me. I'm running ten minutes late, see you at the station.",
        "error": null,
        "translations": {
            "de": "Hallo, ich bin es. Ich komme zehn Minuten später, wir sehen uns am Bahnhof."
        }
    }
}
Show less

Errors

Every error answers with JSON that has a stable error code and a readable message.

401
unauthenticated
The API key is missing, wrong or revoked.
402
payment_required
Not a paying account, or not enough credits for this file. The body has reason (not_paying or insufficient_credits), balance, cost and buy_url. A transcript that was already made stays stored, so the retry after a top-up is instant.
403
forbidden
This key may not use this endpoint.
404
not_found
Nothing with this id for this account.
413
payload_too_large
The upload is larger than the server accepts.
422
validation_failed
A field is missing or invalid; fields lists which.
422
file_too_long
The recording is longer than this account may send.
422
transcription_failed
The file could not be transcribed, for example because it is not audio.
429
rate_limited
Too many requests. Wait a minute and try again.
Response 402 Payment Required JSON Copied
{
    "error": "payment_required",
    "message": "Not enough credits for this file. Top up and send it again; it will not be transcribed twice.",
    "reason": "insufficient_credits",
    "balance": 5,
    "cost": 20,
    "credits_per_minute": 10,
    "translation_per_minute": 5,
    "buy_url": "https://audiocap.app/developers?buy=credits"
}

Limits

  • Up to 100 MB per file
  • 20 requests per minute and account
  • Up to 5 translation languages per request
  • Up to 120 minutes per file, 240 minutes on a premium account