Skip to main content
POST

Authorizations

Authorization
string
header
required

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.

Headers

Idempotency-Key
string

Account-scoped for 24 hours; stores a canonical request-body hash. Reuse with a different body returns 409 idempotency_conflict; a repeat while the original request is still running returns 409 request_in_progress (retry with the same key). This is separate from the cache lease used to coalesce inference.

Required string length: 1 - 255
Pattern: ^[!-~]{1,255}$

Body

application/json

Exactly one of url, feed_url+guid, episode_id (with the feed_url and itunes_id it was listed under, when the catalog does not hold it yet), or upload_id. format is delivery-only and never affects the cache key or the produced transcript. Raw-audio URLs, engine selection, diarization, vocabulary, and callback URLs are rejected rather than ignored.

url
string<uri>
required

An Apple Podcasts episode URL.

Required string length: 1 - 2048
language

A BCP-47 language tag, or null/omitted to auto-detect. Automatic detection never selects the ASR engine: it always routes to the configured default engine, and detected language is recorded only as a reporting field. Reaching a language outside the default engine's coverage requires passing this field explicitly.

Maximum string length: 35
Pattern: ^[A-Za-z]{2,3}(-[A-Za-z0-9]{1,8})*$
format
enum<string>

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).

Available options:
json,
text,
srt,
vtt,
md
dry_run
boolean
default:false
max_credits
integer

The most this call may take; refused 422 max_credits_exceeded before anything is reserved when it could take more. Omit for no cap beyond the balance.

Required range: 0 <= x <= 9007199254740991

Response

A cache hit or publisher-transcript passthrough (charges credits_charged), or a dry-run quote (dry_run: true; creates no job or reservation). The media type follows format; see the operation description for the one documented switch back to application/json.

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.

format
any
required
is_cached
boolean
required
read_id
string
required

The receipt this read was charged under (0.15.0). GET /v1/reads/{read_id} delivers the same transcript again, in any format, and charges nothing further.

Maximum string length: 40
Pattern: ^job_[A-Za-z0-9]{16,32}$
credits_charged
integer
required

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.

Required range: 0 <= x <= 9007199254740991
show_title
string | null
required

The show's title as it was when this job or read was created (ADR-0036), or null when it was created without one: before 0.14.0, or from a feed that names no show. For an upload it is Uploads. Copied once, never updated.

Required string length: 1 - 300
episode_title
string | null
required

The episode's title as it was when this job or read was created, or null when it was created without one. For an upload it is the title given when the file was announced. Where a feed gave no title, a catalog read (GET /v1/episodes/{episode_id}/transcript) answers the identifier the catalog stores in its place.

Required string length: 1 - 300
transcript
object
required
transcript_url
string<uri>
Maximum string length: 2048
expires_at
string<date-time>