# Transcribe

> Upload pre-recorded Arabic audio as multipart/form-data and receive a high-quality transcript with total duration and word-level timestamps. Optimized for asynchronous processing of interviews, meetings, media clips and customer calls.

## Endpoint

`POST /api/v1/audio/transcribe`

Authenticate with your API key in the `x-api-key` header. See [Authentication](/authentication).

| Header | Value |
| --- | --- |
| `x-api-key` | YOUR\_MUNSIT\_API\_KEY |

## Request

Send the body as `multipart/form-data`.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `file` | file | **Yes** | Audio file in a supported format — see [what you can send](/speech-to-text/get-started#send). |
| `model` | string | No | ASR model to use: `munsit` (default) or `munsit-en-ar` (mixed Arabic-English with code-switching). |
| `hotwords` | string | No | Comma-separated custom vocabulary (multi-word phrases allowed). Biases recognition toward names, brands and domain terms. **Not supported with `munsit-en-ar`.** |
| `return_confidence` | boolean | No | When `true`, each `timestamps` entry includes a `confidence` score (0–1). The response is otherwise unchanged. |
| `return_timestamps` | boolean | No | Defaults to `true` on `munsit`; set `false` to return an empty `timestamps` array. On `munsit-en-ar` timestamps are off unless you set this to `true`. |
| `return_turns` | boolean | No | When `true`, adds the `turns` array and tags each turn with smart-turn `is_complete` and `turn_probability`. These two fields appear **only** with this flag. |
| `return_gender` | boolean | No | When `true`, adds the `turns` array with a `gender` object (`label`, `score`) per turn, plus a whole-file rollup in `analysis`. |
| `return_sentiment` | boolean | No | When `true`, adds the `turns` array with a `sentiment` object (`label`, `score`) per turn, plus a whole-file rollup in `analysis`. |

## Custom vocabulary

Pass rare terms the recognizer is unlikely to know — customer names, product codes, brand words. In our benchmarks, biasing recovered rare terms from 0% to 77% recall with overall accuracy unchanged or slightly better. Short lists of 5–30 genuinely rare terms work best; very long lists dilute the effect.

```bash
curl -X POST "https://api.munsit.com/api/v1/audio/transcribe" \
  -H "x-api-key: $MUNSIT_API_KEY" \
  -F "file=@call.wav" \
  -F "hotwords=عبد القادر,أديب" \
  -F "return_confidence=true"
```

Response — timestamps entry

```
{ "word": "الأشياء", "start": 0.24, "end": 0.31, "confidence": 0.994 }
```

> hotwords is ignored when model=munsit-en-ar. The mixed Arabic-English model does not support custom vocabulary.
> Transcribe only. hotwords and return_confidence apply to POST /audio/transcribe — not to diarization or minutes of meetings. For live audio, streaming takes hotwords as a query parameter and always returns confidence.

## Per-turn analysis

Set `return_turns`, `return_gender` and/or `return_sentiment` to break the transcript into turns and annotate each one. Any of the three adds the `turns` array — the gender and sentiment flags imply it — and each flag contributes only its own fields. `analysis` holds the whole-file rollup and appears only with `return_gender` or `return_sentiment`; `return_turns` on its own does not produce it. Short single-speaker recordings typically come back as one turn.

```bash
curl -X POST "https://api.munsit.com/api/v1/audio/transcribe" \
  -H "x-api-key: $MUNSIT_API_KEY" \
  -F "file=@call.wav" \
  -F "return_turns=true" \
  -F "return_gender=true" \
  -F "return_sentiment=true"
```

Response — 200

```
{
  "statusCode": 200,
  "data": {
    "transcription": "أهلا كيف حالك... بخير شكرا",
    "duration": 6.2,
    "turns": [
      {
        "turn_id": 0,
        "start": 0.0,
        "end": 2.8,
        "text": "أهلا كيف حالك",
        "is_complete": true,
        "turn_probability": 0.94,
        "gender": { "label": "male", "score": 0.88 },
        "sentiment": { "label": "neutral", "score": 0.81 }
      },
      {
        "turn_id": 1,
        "start": 3.1,
        "end": 6.2,
        "text": "بخير شكرا",
        "is_complete": true,
        "turn_probability": 0.97,
        "gender": { "label": "female", "score": 0.91 },
        "sentiment": { "label": "positive", "score": 0.86 }
      }
    ],
    "analysis": {
      "turns": 2,
      "gender": { "dominant": "female", "by_duration_s": { "male": 2.8, "female": 3.1 } },
      "sentiment": { "dominant": "positive", "counts": { "neutral": 1, "positive": 1 } }
    }
  },
  "message": "Success"
}
```

> This sentiment is a quick per-utterance signal returned alongside the transcript. For deeper, LLM-based analysis of an existing transcription — emotions, trends, critical moments — use Sentiment analysis instead. For live audio, streaming emits Sentiment and Gender events per turn.
> GET /history/speech-to-text/:id also returns turns and analysis for transcriptions created with one of these flags set.

## Example request

How it works: upload audio, Munsit analyzes the recording and converts the Arabic speech into text, and you get back a transcript with duration and word-level timestamps.

```bash
curl -X POST "https://api.munsit.com/api/v1/audio/transcribe" \
  -H "x-api-key: $MUNSIT_API_KEY" \
  -F "file=@meeting.mp3" \
  -F "model=munsit"
```

```python
import requests, os

r = requests.post(
    "https://api.munsit.com/api/v1/audio/transcribe",
    headers={"x-api-key": os.environ["MUNSIT_API_KEY"]},
    files={"file": open("meeting.mp3", "rb")},
    data={"model": "munsit"},
)
out = r.json()["data"]
print(out["transcription"], out["duration"])
```

```javascript
import fs from "node:fs";

const form = new FormData();
form.append("file", new Blob([fs.readFileSync("meeting.mp3")]), "meeting.mp3");
form.append("model", "munsit");

const res = await fetch(
  "https://api.munsit.com/api/v1/audio/transcribe",
  { method: "POST",
    headers: { "x-api-key": process.env.MUNSIT_API_KEY },
    body: form }
);
const { data } = await res.json();
console.log(data.transcription, data.duration);
```

```go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
)

func main() {
	var buf bytes.Buffer
	w := multipart.NewWriter(&buf)
	f, _ := os.Open("meeting.mp3")
	fw, _ := w.CreateFormFile("file", "meeting.mp3")
	io.Copy(fw, f)
	w.WriteField("model", "munsit")
	w.Close()

	req, _ := http.NewRequest("POST",
		"https://api.munsit.com/api/v1/audio/transcribe", &buf)
	req.Header.Set("x-api-key", os.Getenv("MUNSIT_API_KEY"))
	req.Header.Set("Content-Type", w.FormDataContentType())

	resp, _ := http.DefaultClient.Do(req)
	defer resp.Body.Close()

	var out map[string]any
	json.NewDecoder(resp.Body).Decode(&out)
	data := out["data"].(map[string]any)
	fmt.Println(data["transcription"], data["duration"])
}
```

Response — 200

```
{
  "statusCode": 200,
  "data": {
    "transcriptionId": "805059bf-7c3f-4a1e-9d2b-1f0c6ae83b47",
    "transcription": "لك كلما عمقت الآخرين أصبحت قزما...",
    "duration": 53.661375,
    "timestamps": [
      { "word": "الأشياء", "start": 0.24, "end": 0.31 }
    ],
    "summary": "",
    "audioUrl": "<stored-audio-url>",
    "stats": {
      "fileName": "meeting.mp3",
      "fileSize": "1.42 MB",
      "mimeType": "audio/mpeg",
      "creditsConsumed": 7
    }
  },
  "message": "Success"
}
```

> Files must be under 60 minutes. For longer recordings, split the audio into shorter segments for best performance — or use Streaming, which has no duration limit.

## Response fields

The payload arrives under `data`, alongside `statusCode` and `message`.

| Field | Type | Description |
| --- | --- | --- |
| `transcriptionId` | string (UUID) | Transcription identifier. Pass it as the path parameter to [sentiment analysis](/understanding/sentiment-analysis) and [keyword extraction](/understanding/keyword-extraction). |
| `transcription` | string | Full transcript text. |
| `duration` | number | Audio duration in seconds. |
| `timestamps` | array of objects (`word`, `start`, `end`, plus `confidence` when `return_confidence=true`) | Word-level timestamps. |
| `turns` | array of objects (`turn_id`, `start`, `end`, `text`; plus `is_complete` / `turn_probability` with `return_turns`, `gender` with `return_gender`, `sentiment` with `return_sentiment`) | Present when any of the three flags is `true` — `return_gender` and `return_sentiment` imply it. Short single-speaker clips typically return one turn. |
| `analysis` | object (`turns` count; plus `gender` and `sentiment` when those flags are set) | Whole-file rollup. Present only with `return_gender` or `return_sentiment` — `return_turns` alone does not produce it. |
| `attributes` | object | Internal metadata blob persisted with the transcription; mirrors fields above under different names (and repeats the full `timestamps` array). Prefer the named fields — treat this as unstable. |
| `summary` | string | Always present. Empty string on plain transcription; carries the generated summary on [minutes of meetings](/speech-to-text/minutes-of-meetings). |
| `audioUrl` | string | Stored copy of the uploaded audio. See [Audio retention](#retention). |
| `stats` | object (`fileName`, `fileSize`, `mimeType`, `creditsConsumed`) | Upload metadata and the credits billed for this request. |

## Audio retention

Audio submitted to the hosted API is stored server-side. Every successful response carries an `audioUrl` pointing at that stored copy, and the transcript text is persisted alongside it so it can be retrieved later through the history endpoints.

| What | Detail |
| --- | --- |
| **What is stored** | The uploaded audio file and the resulting transcript, keyed to your account. |
| **Access** | The response returns an `audioUrl` for the stored object. Do not treat it as a secret or hard-code it — the exact form is not part of the API contract and may change. |
| **Removal** | Delete a transcription and its stored audio through the history endpoints, or contact [support](/support) for bulk removal. |
| **Avoiding retention** | If your deployment cannot retain audio at all, use [self-hosting](/deployment/self-hosting), where storage is under your control. |

## Go further

Do more with your transcript.

- [Streaming →](/speech-to-text/streaming) — Live transcripts over a WebSocket instead of file upload. — `WSS /api/v1/listen`

- [Diarization →](/speech-to-text/diarization) — Speaker-labeled segments merged with the transcript. — `POST /audio/diarization/transcribe`

- [Minutes of meetings →](/speech-to-text/minutes-of-meetings) — Structured transcripts for meeting records. — `POST /minutes-of-meeting/transcribe`

- [Sentiment analysis →](/understanding/sentiment-analysis) — Analyze the tone of what was transcribed. — `POST /sentiment-analysis`
