Specala AIDocs
API Reference

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

ParameterTypeDescription
idUUIDTranscription 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

FieldTypeDescription
textstring | nullThe whole transcript as one string, with timestamps and speaker labels. null until status is completed.
segments[]arrayThe same speech, one entry per turn. Empty until status is completed.
segments[].idintegerPosition of the turn, starting at 1. Stable for a given transcript.
segments[].start / .endfloat | nullSeconds from the start of the recording.
segments[].speaker_idstring | nullDiarization identifier, matching speakers[].id. Survives renames — group by this. null when the recording was not diarized.
segments[].speaker_namestring | nullThe 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[].textstringWhat 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:

FieldTypeDescription
statusstringqueued, processing, completed, failed or insufficient_balance — see status lifecycle.
error_codestring | nullMachine-readable failure reason when status is failed.
required_minutesfloat | nullHow many minutes this file needs. Set only when status is insufficient_balance.
available_minutesfloat | nullHow 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[]arrayEveryone detected in the recording, as { id, name }.
external_idstring | nullThe identifier you sent as X-External-Id at upload. null if the transcription did not come through the API. See Your own identifiers.
metadataobject | nullThe object you sent as X-Metadata at upload, returned as given.
authorobjectThe 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[]arrayAI 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[].idUUIDReport identifier.
prompt_results[].slugstring | nullSlug of the report template that produced it. null for legacy reports without a template.
prompt_results[].namestring | nullReport name, e.g. Follow-up plan, Summary.
prompt_results[].statusstringqueued → processing → completed / failed.
prompt_results[].error_codestring | nullSet only on failed.
prompt_results[].textstring | nullReport content, as prose. Present only when status is completed.
prompt_results[].created_atdatetimeWhen the report was requested.
prompt_results[].completed_atdatetime | nullWhen the report was generated.

Need the whole thing as a file instead? Export transcription with format=json returns exactly this body.

Errors

StatusWhen
401Missing/invalid key, or key passed in the URL.
403Plan without API access.
403transcription_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.
404No transcription with that id in this workspace.
429Rate limit exceeded.

A 404 is also returned when the transcription exists but belongs to another workspace — existence is never revealed across workspaces. See Errors.

On this page