curl --request GET \
--url https://k8mfogcvz4.execute-api.us-east-1.amazonaws.com/prod/v1/reads/{read_id} \
--header 'Authorization: Bearer <token>'import requests
url = "https://k8mfogcvz4.execute-api.us-east-1.amazonaws.com/prod/v1/reads/{read_id}"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://k8mfogcvz4.execute-api.us-east-1.amazonaws.com/prod/v1/reads/{read_id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://k8mfogcvz4.execute-api.us-east-1.amazonaws.com/prod/v1/reads/{read_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://k8mfogcvz4.execute-api.us-east-1.amazonaws.com/prod/v1/reads/{read_id}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://k8mfogcvz4.execute-api.us-east-1.amazonaws.com/prod/v1/reads/{read_id}")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://k8mfogcvz4.execute-api.us-east-1.amazonaws.com/prod/v1/reads/{read_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"format": "json",
"is_cached": true,
"credits_charged": 4503599627370495,
"transcript": {
"episode_id": "<string>",
"show_id": "<string>",
"language": "<string>",
"duration_sec": 64800,
"source": "qwen3-asr",
"source_revision": "<string>",
"model_version": "<string>",
"pipeline_version": "<string>",
"timing_precision": "word",
"diarized": false,
"warnings": [
{
"segment": 500000,
"type": "<string>",
"detail": "<string>"
}
],
"segments": [
{
"id": 500000,
"start": 64800,
"end": 64800,
"speaker": null,
"text": "<string>",
"words": [
{
"w": "<string>",
"s": 64800,
"e": 64800
}
]
}
],
"created_at": "2023-11-07T05:31:56Z"
},
"transcript_url": "<string>",
"expires_at": "2023-11-07T05:31:56Z"
}{
"error": {
"code": "invalid_request",
"type": "invalid_request",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"retryable": true
}
}{
"error": {
"code": "invalid_request",
"type": "invalid_request",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"retryable": true
}
}{
"error": {
"code": "invalid_request",
"type": "invalid_request",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"retryable": true
}
}{
"error": {
"code": "invalid_request",
"type": "invalid_request",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"retryable": true
}
}{
"error": {
"code": "invalid_request",
"type": "invalid_request",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"retryable": true
}
}Re-read a transcript this account has already paid for
Serves the transcript behind a settled read, and charges nothing. read_id is the id a read was charged under: the read_id of a cached_read group member (JobGroupCachedReadMember), which a confirm creates and bills when the episode was already transcribed, or the job_id of a job that reached completed, which earns the same receipt when it settles. Either way the account has paid once; this operation is how it takes delivery, as many times as it needs to, without paying again.
Without it a cache hit inside a confirmed group would be money spent on content with no retrieval path: a cache read creates no job, so GET /v1/transcripts/{job_id} cannot serve it, and GET /v1/episodes/{episode_id}/transcript charges a new read on every call.
Delivery format. Identical to the episode read: format: json (or omitted) returns application/json; format: text|srt|vtt|md returns the raw artifact under its own media type (text/plain, application/x-subrip, text/vtt, text/markdown) with provenance in X-Transcript-Episode-Id, X-Transcript-Source, and X-Transcript-Timing-Precision; and a payload over the ~5 MB inline limit returns 200 application/json with { transcript_url, expires_at } in every format — the single content-type switch. format defaults to json here rather than having no default as it does on the job poll, because a receipt has no state to poll: there is nothing this operation could usefully answer except the transcript.
credits_charged on the TranscriptRead envelope, and the X-Credits-Charged header on every format, are 0 on this operation — always, not merely usually. What the read cost is the credits_charged of its group member and the amount on its ledger event, both recorded when it settled.
A read_id this account holds no receipt for answers 404 job_not_found, whether it never existed, belongs to another account, or names a job that has not completed — one answer, as on the job poll, so the miss discloses nothing.
curl --request GET \
--url https://k8mfogcvz4.execute-api.us-east-1.amazonaws.com/prod/v1/reads/{read_id} \
--header 'Authorization: Bearer <token>'import requests
url = "https://k8mfogcvz4.execute-api.us-east-1.amazonaws.com/prod/v1/reads/{read_id}"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://k8mfogcvz4.execute-api.us-east-1.amazonaws.com/prod/v1/reads/{read_id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://k8mfogcvz4.execute-api.us-east-1.amazonaws.com/prod/v1/reads/{read_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://k8mfogcvz4.execute-api.us-east-1.amazonaws.com/prod/v1/reads/{read_id}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://k8mfogcvz4.execute-api.us-east-1.amazonaws.com/prod/v1/reads/{read_id}")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://k8mfogcvz4.execute-api.us-east-1.amazonaws.com/prod/v1/reads/{read_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"format": "json",
"is_cached": true,
"credits_charged": 4503599627370495,
"transcript": {
"episode_id": "<string>",
"show_id": "<string>",
"language": "<string>",
"duration_sec": 64800,
"source": "qwen3-asr",
"source_revision": "<string>",
"model_version": "<string>",
"pipeline_version": "<string>",
"timing_precision": "word",
"diarized": false,
"warnings": [
{
"segment": 500000,
"type": "<string>",
"detail": "<string>"
}
],
"segments": [
{
"id": 500000,
"start": 64800,
"end": 64800,
"speaker": null,
"text": "<string>",
"words": [
{
"w": "<string>",
"s": 64800,
"e": 64800
}
]
}
],
"created_at": "2023-11-07T05:31:56Z"
},
"transcript_url": "<string>",
"expires_at": "2023-11-07T05:31:56Z"
}{
"error": {
"code": "invalid_request",
"type": "invalid_request",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"retryable": true
}
}{
"error": {
"code": "invalid_request",
"type": "invalid_request",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"retryable": true
}
}{
"error": {
"code": "invalid_request",
"type": "invalid_request",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"retryable": true
}
}{
"error": {
"code": "invalid_request",
"type": "invalid_request",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"retryable": true
}
}{
"error": {
"code": "invalid_request",
"type": "invalid_request",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"retryable": true
}
}Authorizations
Authorization: Bearer hk_live_... for live keys or Authorization: Bearer hk_test_... for test-mode keys. hk_test_ keys resolve real public catalog metadata but return deterministic committed fixtures, never call inference, and never mutate live credits. This is the only transport for the credential: the x-api-key alias once documented was removed in 0.2.0, because the edge authorizer reads Authorization as its single identity source and a request on any other header is refused before it is authenticated.
Never accepted on a dashboardJwt operation, and there are no exceptions. GET /v1/usage and GET /v1/limits briefly declared both schemes (0.8.0); that was withdrawn in 0.8.1 because no deployed route could honor it — both operations are served by the control-plane API, whose authorizer verifies a Cognito token and refuses an hk_live_ credential on shape, and the customer API does not route either path. An API-key holder reads its balance and reservation from QuoteResponse, which carries balance_credits and reserved_credits on every quote. Every operation in this document takes one scheme or the other and refuses the wrong one as unauthenticated.
Path Parameters
The id a settled transcript read was charged under — a cached_read group member's read_id, or a completed job's job_id. Job-shaped: the two are minted from one generator, so one read is one id whichever way the transcript was obtained.
Opaque, server-generated job identifier.
40^job_[A-Za-z0-9]{16,32}$Query Parameters
The derived output to return once the transcript is available, and with it the response media type: json → application/json, text → text/plain, srt → application/x-subrip, vtt → text/vtt, md → text/markdown. An oversized payload is application/json in every format — the one documented content-type switch, described on each operation. Delivering content is the whole point of the operations that use this parameter, so omitting it defaults to json rather than withholding the transcript (contrast PollFormatQueryParam, which has no default).
Delivery-only: never affects the cache key or the produced transcript content.
Delivery-only: never affects the cache key or the produced transcript content. Every format is derived at read time from the one canonical transcript. Response media type per format: json → application/json, text → text/plain, srt → application/x-subrip, vtt → text/vtt, md → text/markdown. The one exception is an oversized payload, which is application/json in every format (see TranscriptUrlRef).
json, text, srt, vtt, md Response
The paid transcript, inline for format=json under the inline limit, as a raw body for text|srt|vtt|md, or as a transcript_url reference for an oversized payload in any format. Never a charge.
- Option 1
- Option 2
- Option 3
A synchronous JSON transcript read: a cache hit or publisher passthrough (POST /v1/transcripts), a direct cached fetch (GET /v1/episodes/{episode_id}/transcript), or the re-delivery of a read that was already paid for (GET /v1/reads/{read_id}). credits_charged is what this request charged, which is why it is 0 on the last of the three and the cached-read price for this account on the other two. format is always json here — a text|srt|vtt|md read of the same transcript is delivered as a raw body with X-Credits-Charged carrying this envelope's credits_charged. See the comment above for why this shape is self-contained rather than composed via allOf.
A non-negative JavaScript-safe integer credit amount. The upper bound matches Number.MAX_SAFE_INTEGER, which is enforced by the domain ledger so arithmetic and JSON round-trips cannot silently lose cents worth of credit precision.
0 <= x <= 9007199254740991Show child attributes
Show child attributes
2048
