---
title: "Quickstart Guide | Scriptivox API"
description: "Get started with the Scriptivox API in under 5 minutes. Upload audio, transcribe, and retrieve results with code examples in Python, JavaScript, and curl."
canonical: "https://platform.scriptivox.com/docs/quickstart"
last-updated: "2026-08-25"
---

# Quickstart

Get your first transcription running in under 5 minutes.

---

### Step 1: Create an API key
Go to the [API Keys page](/platform/keys) in your dashboard and create a new key. Copy it — you'll only see it once.

Your key looks like: `sk_live_12ab34cd...`

### Step 2: Add balance
Transcription costs `$0.20/hour` of audio, billed per second. Add funds on the [Billing page](/platform/billing). Minimum deposit is $5.00 (~25 hours of audio).

### Step 3: Transcribe
Send a URL and we'll download, validate, and transcribe it. Supports direct file links, Google Drive, Dropbox, and OneDrive sharing links.

```python
resp = requests.post(f"{BASE}/transcribe",
    headers={"Authorization": API_KEY},
    json={
        "url": "https://example.com/podcast.mp3",
        "diarize": True
    })
job = resp.json()
print(f"Transcription ID: {job['id']}")
```

```javascript
const jobResp = await fetch(`${BASE}/transcribe`, {
  method: "POST",
  headers: {
    Authorization: API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://example.com/podcast.mp3",
    diarize: true,
  }),
});
const job = await jobResp.json();
console.log("Transcription ID:", job.id);
```

```bash
curl -X POST https://api.scriptivox.com/v1/transcribe \
  -H "Authorization: sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/podcast.mp3",
    "diarize": true
  }'
```

You'll get back a transcription ID immediately. The file downloads and processes in the background.

Optionally enable speaker diarization with `diarize: true`, and wire up automatic [webhooks](/docs/webhooks) via `webhook_url`. If you know how many speakers are on the recording, pass `speaker_count` along with `diarize` — providing it noticeably improves accuracy versus letting the model auto-detect. Word-level timestamps (`align`) are on by default; pass `align: false` to opt out. When `diarize: true`, alignment is always enabled (it's required for speaker assignment) regardless of what you pass.

**Pass `language` when you know it.** If you omit it, the model auto-detects, which usually works but can misclassify short clips, code-switched audio, or files that start with music. Passing the ISO code (e.g. `"language": "en"`) is both faster and more accurate. See the [language parameter notes](/docs/api-reference#important-notes) for details.

### Step 4: Get the result
Poll until the status is `completed` or `failed`. Typical transcriptions complete in under a minute.

```python
import time

while True:
    resp = requests.get(f"{BASE}/transcribe/{job['id']}",
        headers={"Authorization": API_KEY})
    result = resp.json()

    if result["status"] == "completed":
        print(result["result"]["full_transcript"])
        break
    elif result["status"] == "failed":
        print("Error:", result["error"])
        break

    time.sleep(5)
```

```javascript
// Poll until complete
let result;
while (true) {
  const res = await fetch(
    `${BASE}/transcribe/${job.id}`,
    { headers: { Authorization: API_KEY } }
  );
  result = await res.json();

  if (result.status === "completed") break;
  if (result.status === "failed") throw new Error(result.error.message);

  await new Promise((r) => setTimeout(r, 5000));
}

console.log(result.result.full_transcript);
```

```bash
curl "https://api.scriptivox.com/v1/transcribe/txn-456" \
  -H "Authorization: sk_live_YOUR_KEY"
```

The response includes the full transcript, timestamped utterances, word-level timestamps (alignment is on by default), and speaker labels when diarization is enabled:

```json
{
  "id": "txn-456",
  "status": "completed",
  "audio_duration_seconds": 120,
  "cost_cents": 0.6667,
  "result": {
    "full_transcript": "Hello, thanks for joining the call today...",
    "language": "en",
    "duration_seconds": 120,
    "speakers": ["SPEAKER 1", "SPEAKER 2"],
    "utterances": [
      {
        "start": 0.5,
        "end": 3.2,
        "text": "Hello, thanks for joining the call today.",
        "speaker": "SPEAKER 1",
        "confidence": 0.95,
        "words": [
          { "word": "Hello,", "start": 0.5, "end": 0.9, "confidence": 0.98, "speaker": "SPEAKER 1" },
          { "word": "thanks", "start": 1.0, "end": 1.3, "confidence": 0.97, "speaker": "SPEAKER 1" }
        ]
      }
    ]
  }
}
```

---

> **Need to upload your own files?:** 
> If you don't have a public URL, you can upload files directly using the [file upload flow](/docs/api-reference#upload) in the API Reference.

## When something goes wrong

Failures arrive on two different paths, and a client that only handles one of them will silently lose jobs.

**Synchronous** errors come back on the call you just made, as JSON with a stable `code`:

```json
{
  "error": {
    "code": "INSUFFICIENT_BALANCE",
    "message": "Add funds to continue transcribing.",
    "docs_url": "https://platform.scriptivox.com/docs/api-reference#error-codes"
  }
}
```

The ones worth handling by name on submit are `INVALID_API_KEY` (401 — the key is missing, malformed or revoked), `INSUFFICIENT_BALANCE` and `ZERO_BALANCE` (402 — top up on the [Billing page](/platform/billing)), `VALIDATION_ERROR` (400 — the body is wrong; the message says how), and `RATE_LIMIT_EXCEEDED` (429 — wait for the seconds in `Retry-After`, then retry).

**Asynchronous** errors do not appear on submit at all. `POST /v1/transcribe` returns `status: "created"` before the file has been fetched, so anything about the file itself — an unreachable URL, a permission-gated Drive link, a corrupt container, a codec we cannot decode, a file over 5 GB or longer than 10 hours, or a recording with no audible speech — surfaces later on the poll:

```json
{
  "id": "txn-456",
  "status": "failed",
  "error": {
    "code": "DOWNLOAD_FAILED",
    "message": "The URL could not be fetched (HTTP 403)."
  }
}
```

Treat `status: "failed"` as a first-class outcome next to `"completed"`. Both are terminal; neither will change if you keep polling. Failed and cancelled jobs cost nothing — the reserved balance is released — so a retry after fixing the input is free.

## Retrying safely

Pass `Idempotency-Key` on `POST /v1/transcribe` and `POST /v1/upload`. If the same key arrives again within 24 hours you get the original job back instead of a second one, which makes a network timeout safe to retry. Without it, a retried submit is a second billable job.

## Polling without hammering

Poll no faster than once every few seconds — the rate limit is 100 requests per minute per endpoint, and every response tells you where you stand:

```http
RateLimit-Policy: "endpoint";q=100;w=60, "ip";q=300;w=60
RateLimit: "endpoint";r=94;t=41
```

If you would rather not poll at all, register a `webhook_url` on the submit call and we will POST the finished transcription to you, HMAC-signed. See [Webhooks](/docs/webhooks).

## Supported input

25 container formats are accepted — MP3, WAV, FLAC, M4A, AAC, OGG, Opus, MP4, MOV, MKV, WebM among them — from 1 second to 10 hours long and at most 5 GB. Public URLs work, including Google Drive, Dropbox and OneDrive share links, as long as the link does not require a sign-in. 119 languages are supported; pass `language` whenever you know it.

## Next steps

- [API Reference](/docs/api-reference) — Full endpoint documentation
- [Authentication](/docs/authentication) — Keys, headers, rotation and what each 401 means
- [Webhooks](/docs/webhooks) — Real-time completion notifications
- [CLI](/docs/cli) — npx @scriptivox-api/cli for shells and scripts
