Get transcription
GET /transcriptions/{id} — one transcription: the flat text, the structured segments, speakers, topics and AI reports.
GET /transcriptions/{id}
Returns one transcription in full: the transcript both as a flat text and as structured
segments, plus any AI reports (prompt_results). No query parameters — everything comes
back every time. Find the id via List transcriptions.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | UUID | Transcription identifier. |
Request
curl https://app.specala.ai/api/v1/developer/transcriptions/3fa85f64-5717-4562-b3fc-2c963f66afa6 \
-H "Authorization: Bearer sk_live_your_key_here"import httpx
tid = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
resp = httpx.get(
f"https://app.specala.ai/api/v1/developer/transcriptions/{tid}",
headers={"Authorization": "Bearer sk_live_your_key_here"},
)
resp.raise_for_status()
t = resp.json()const tid = "3fa85f64-5717-4562-b3fc-2c963f66afa6";
const res = await fetch(
`https://app.specala.ai/api/v1/developer/transcriptions/${tid}`,
{ headers: { Authorization: "Bearer sk_live_your_key_here" } },
);
const t = await res.json();Open the Acme discovery call and show me what was decided.
The assistant calls specala_get_transcription with the UUID.
Response
200 OK. The transcript comes back two ways in the same payload: text is the
flat string to show a person, segments is the same speech as structured data to
load into a CRM, a database or your own model. You do not have to ask for either.
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"title": "Acme — discovery call",
"status": "completed",
"summary": "Walked through current workflow and pain points...",
"language": "en",
"duration_seconds": 1840.5,
"media_type": "audio",
"project": { "id": "8b1f3e22-1c4a-4f7e-9a2b-6d5e8c1a2b3c", "name": "Sales" },
"author": { "id": "9c2d4e6f-1a2b-4c3d-8e9f-0a1b2c3d4e5f", "email": "alex@company.com", "name": "Alex Morgan" },
"speakers": [{ "id": "1", "name": "Alex" }, { "id": "2", "name": "Jordan" }],
"topics": [{ "title": "Pricing" }, { "title": "Onboarding" }],
"text": "[00:00] Alex: Thanks for hopping on...\n[00:42] Jordan: Of course...",
"segments": [
{
"id": 1,
"start": 0.0,
"end": 4.82,
"speaker_id": "1",
"speaker_name": "Alex",
"text": "Thanks for hopping on..."
},
{
"id": 2,
"start": 42.1,
"end": 45.6,
"speaker_id": "2",
"speaker_name": "Jordan",
"text": "Of course..."
}
],
"prompt_results": [
{
"id": "8d2f6a1e-0c4b-4f0e-9d3a-6b1c2e7f5a90",
"slug": "followup-email",
"name": "Follow-up plan",
"status": "completed",
"error_code": null,
"text": "- Send pricing deck by Friday\n- Schedule technical follow-up",
"created_at": "2026-05-20T09:31:00Z",
"completed_at": "2026-05-20T09:31:40Z"
}
],
"created_at": "2026-05-20T09:14:00Z",
"updated_at": "2026-05-20T09:31:00Z"
}The transcript
| Field | Type | Description |
|---|---|---|
text | string | null | The whole transcript as one string, with timestamps and speaker labels. null until status is completed. |
segments[] | array | The same speech, one entry per turn. Empty until status is completed. |
segments[].id | integer | Position of the turn, starting at 1. Stable for a given transcript. |
segments[].start / .end | float | null | Seconds from the start of the recording. |
segments[].speaker_id | string | null | Diarization identifier, matching speakers[].id. Survives renames — group by this. null when the recording was not diarized. |
segments[].speaker_name | string | null | The name shown today, with manual renames applied over the AI-assigned one. null when nobody has named this speaker — label them yourself from speaker_id, in your own language. |
segments[].text | string | What was said, without the timestamp or speaker prefix. |
Build on segments. text is the same speech assembled for a person to read: its exact
shape is not something we promise to keep, and the speaker labels inside it change the
moment someone renames a speaker in the app.
Everything else
Same as a list item, plus:
| Field | Type | Description |
|---|---|---|
status | string | queued, processing, completed, failed or insufficient_balance — see status lifecycle. |
error_code | string | null | Machine-readable failure reason when status is failed. |
required_minutes | float | null | How many minutes this file needs. Set only when status is insufficient_balance. |
available_minutes | float | null | How many minutes the workspace has left. Set only when status is insufficient_balance — the difference from required_minutes is exactly what needs topping up. |
speakers[] | array | Everyone detected in the recording, as { id, name }. |
external_id | string | null | The identifier you sent as X-External-Id at upload. null if the transcription did not come through the API. See Your own identifiers. |
metadata | object | null | The object you sent as X-Metadata at upload, returned as given. |
author | object | The workspace member who owns the transcription, as { id, email, name }. For API uploads this is the X-Author you sent, or the key's creator. id is a UUID and can be used as X-Author: user:<id>. See Assigning an author. |
prompt_results[] | array | AI reports for this transcription — generated in the app, with ai_metadata=true, or requested through the API. Reports requested with ?reports= appear here immediately with their own status; the transcription itself is completed as soon as the text is ready, so keep polling until every report you need is terminal. See AI reports. |
prompt_results[].id | UUID | Report identifier. |
prompt_results[].slug | string | null | Slug of the report template that produced it. null for legacy reports without a template. |
prompt_results[].name | string | null | Report name, e.g. Follow-up plan, Summary. |
prompt_results[].status | string | queued → processing → completed / failed. |
prompt_results[].error_code | string | null | Set only on failed. |
prompt_results[].text | string | null | Report content, as prose. Present only when status is completed. |
prompt_results[].created_at | datetime | When the report was requested. |
prompt_results[].completed_at | datetime | null | When the report was generated. |
Need the whole thing as a file instead? Export transcription
with format=json returns exactly this body.
Errors
| Status | When |
|---|---|
401 | Missing/invalid key, or key passed in the URL. |
403 | Plan without API access. |
403 | transcription_locked — the transcription is a locked preview. Should not happen on a plan with API access; if it does, the recording is not readable through the API. |
404 | No transcription with that id in this workspace. |
429 | Rate limit exceeded. |
A 404 is also returned when the transcription exists but belongs to another workspace — existence
is never revealed across workspaces. See Errors.