> ## Documentation Index
> Fetch the complete documentation index at: https://docs.maybloc.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Transcribe a recording with the Maybloc API

> Create an upload, send the file, start a transcript and read the speakers and word times, in four API requests. Examples in curl, Node.js and Python.

You transcribe a recording in four requests: create an upload, send the file, start the transcript, then read the result. Recordings run 20 seconds to 3 hours and up to 2 GB, in these formats: MP3, M4A, AAC, WAV, FLAC, OGG, Opus or WebM audio, or MP4, MOV, MKV or WebM video (only the sound is kept).

> [!NOTE]
>
> Create an API key first. See [authentication](https://docs.maybloc.com/authentication.md). The examples read it from the `API_KEY` environment variable.

## How do I transcribe a file?

**1. Create an upload**

Send the file's name, content type and size in bytes. The response has the upload `id`, an `upload_url` and the `headers` to send with the file.

**2. Send the file**

PUT the file bytes to `upload_url` with every returned header. The URL accepts one PUT for 15 minutes. Then call `POST /uploads/{id}/complete` so the size and type are checked.

**3. Start the transcript**

Send the `upload_id` and `duration_ms`, the recording's length in milliseconds (20,000 and 10,800,000). Add a fresh `Idempotency-Key` header. The price is held from your balance and charged when the transcript completes.

**4. Read the result**

Poll `GET /transcripts/{id}` every few seconds until `status` is `completed`, `failed` or `canceled`, or use a [webhook](https://docs.maybloc.com/guides/webhooks.md). Then call `GET /transcripts/{id}/result`.

```bash curl theme={null}
export BASE=https://app.maybloc.com/api/v1
export API_KEY=mbk_live_...

# 1. Create an upload. The response has id, upload_url and headers.
curl -s $BASE/uploads \
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{"file_name":"call.m4a","content_type":"audio/mp4","byte_size":4812001}'

# 2. Send the file to upload_url (valid 15 minutes), then mark it complete.
curl -X PUT "$UPLOAD_URL" -H "Content-Type: audio/mp4" --data-binary @call.m4a
curl -s -X POST $BASE/uploads/$UPLOAD_ID/complete -H "Authorization: Bearer $API_KEY"

# 3. Start the transcript. duration_ms is the recording's length.
curl -s $BASE/transcripts \
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"upload_id":"'$UPLOAD_ID'","duration_ms":155000}'

# 4. When status is "completed", read the result.
curl -s $BASE/transcripts/$TRANSCRIPT_ID/result -H "Authorization: Bearer $API_KEY"
```

```javascript Node.js theme={null}
import { readFile } from "node:fs/promises";
import { randomUUID } from "node:crypto";

const BASE = "https://app.maybloc.com/api/v1";
const json = { Authorization: "Bearer " + process.env.API_KEY, "Content-Type": "application/json" };
const api = async (path, init = {}) => {
  const response = await fetch(BASE + path, { ...init, headers: { ...json, ...init.headers } });
  if (!response.ok) throw new Error(path + " failed: " + (await response.text()));
  return response.status === 204 ? null : response.json();
};

const bytes = await readFile("call.m4a");
const durationMs = 155000; // the recording's length in milliseconds

// 1 and 2. Create the upload, send the file, mark it complete.
const upload = await api("/uploads", {
  method: "POST",
  body: JSON.stringify({ file_name: "call.m4a", content_type: "audio/mp4", byte_size: bytes.length }),
});
await fetch(upload.upload_url, { method: "PUT", headers: upload.headers, body: bytes });
await api("/uploads/" + upload.id + "/complete", { method: "POST" });

// 3. Start the transcript.
let transcript = await api("/transcripts", {
  method: "POST",
  headers: { "Idempotency-Key": randomUUID() },
  body: JSON.stringify({ upload_id: upload.id, duration_ms: durationMs }),
});

// 4. Poll until it finishes, then read the result.
while (["queued", "processing"].includes(transcript.status)) {
  await new Promise((resolve) => setTimeout(resolve, 3000));
  transcript = await api("/transcripts/" + transcript.id);
}
if (transcript.status !== "completed") throw new Error("Transcript " + transcript.status);
const result = await api("/transcripts/" + transcript.id + "/result");
for (const turn of result.turns) console.log(turn.speaker_id, turn.start_ms, turn.text);
```

```python Python theme={null}
import os, time, uuid, requests

BASE = "https://app.maybloc.com/api/v1"
session = requests.Session()
session.headers["Authorization"] = "Bearer " + os.environ["API_KEY"]

def api(method, path, **kwargs):
    response = session.request(method, BASE + path, **kwargs)
    response.raise_for_status()
    return response.json() if response.content else None

with open("call.m4a", "rb") as handle:
    data = handle.read()
duration_ms = 155000  # the recording's length in milliseconds

# 1 and 2. Create the upload, send the file, mark it complete.
upload = api("POST", "/uploads", json={"file_name": "call.m4a", "content_type": "audio/mp4", "byte_size": len(data)})
requests.put(upload["upload_url"], headers=upload["headers"], data=data).raise_for_status()
api("POST", "/uploads/" + upload["id"] + "/complete")

# 3. Start the transcript.
transcript = api("POST", "/transcripts", headers={"Idempotency-Key": str(uuid.uuid4())},
                 json={"upload_id": upload["id"], "duration_ms": duration_ms})

# 4. Poll until it finishes, then read the result.
while transcript["status"] in ("queued", "processing"):
    time.sleep(3)
    transcript = api("GET", "/transcripts/" + transcript["id"])
if transcript["status"] != "completed":
    raise RuntimeError("Transcript " + transcript["status"])
result = api("GET", "/transcripts/" + transcript["id"] + "/result")
for turn in result["turns"]:
    print(turn["speaker_id"], turn["start_ms"], turn["text"])
```

## What happens if a request is repeated?

Send a new `Idempotency-Key` for each transcript you want. Repeat a request with the same key to retry it safely: you get the same transcript and are charged once. Reusing a key for a different request returns `409`.

## What next?

Read [what a transcript result contains](https://docs.maybloc.com/guides/results.md), switch on [add-ons](https://docs.maybloc.com/guides/add-ons.md), or set up [webhooks](https://docs.maybloc.com/guides/webhooks.md).


## Related topics

- [Maybloc API documentation](https://docs.maybloc.com/index.md)
- [Languages supported by the Maybloc API](https://docs.maybloc.com/guides/languages.md)
- [Maybloc API errors, rate limits and idempotency](https://docs.maybloc.com/guides/errors-and-limits.md)
- [Maybloc API pricing: per-minute rates and add-on prices](https://docs.maybloc.com/guides/pricing.md)
- [Maybloc add-ons: redaction, summaries, sentiment and translation](https://docs.maybloc.com/guides/add-ons.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.