Overview
Transcripts are premium, diarized, entity-annotated, and community-correctable.- Base URL:
https://api.arcmira.com, paths under/v1. - Auth:
Authorization: Bearer arc_sk_...orx-api-key: arc_sk_.... Transcript access requires a paid plan. - Billing: 75 rows per 15-minute block of video, rounded up, minimum one block, debited from the shared monthly pool. Unlocks are permanent per account; re-reads free; a job that fails permanently auto-refunds (status
refunded) and revokes the unlock it charged for. Corrections cost 0 rows.
Endpoints
Minimal working example
Request parameters
GET /v1/transcripts/{video_id}:
POST /v1/transcriptions body:
Constraints: public YouTube videos only, up to 12 hours, paid tiers only. Rows debited up front; unlock granted at submit time. Resubmitting an in-flight video returns the existing request with
existing: true. If a premium transcript already exists, the request short-circuits to complete (unified pricing; quote.rows shows what was actually debited, 0 if you were already unlocked).
Response schema
Transcript read:
Transcription request object. Three envelopes, one shape: submit wraps it as
{ "request": {...}, "existing": true? }, poll returns the object flat (no wrapper), and list returns { "requests": [...] } with an added nullable title per row.
Polling loop: sleep on
Retry-After, re-poll, stop on any terminal status (complete / refunded; treat failed as terminal too for forward compatibility). On complete, GET /v1/transcripts/{video_id} works immediately: you were unlocked at submission. Never busy-poll.
Corrections
All six kinds go throughPOST /v1/videos/{video_id}/corrections with a discriminated kind, optional seq, and (for anchored kinds) revision + anchor. Free, Idempotency-Key supported, pending review for everyone but you.
Envelope:
The six kinds, full payloads
line_edit (anchored):
speaker_reassign (anchored; sub-line splits supported; target.kind is existing, new, or role with roles like clip / other / unidentified):
speaker_identify (not anchored; creates a community-flagged appearance on the person’s page immediately; requires a numeric entityId. To propose a person by name, use add_person instead):
add_person (not anchored; proposes a person not in the index and links the speaker in one action):
entity_tag (anchored; existing entity via entityId, or propose one with proposedName + proposedType):
segment_rewrite (anchored; the structural primitive: replaces segments startIndex..endIndex inclusive with the replacements):
- Any N-to-M shape: merge mangled ASR lines into one clean segment, split one into several, or delete the range outright with
"replacements": []. - Timestamps are repaired, optionally pinned:
start/end(seconds) are optional pins per replacement and must be non-decreasing across the list. Every unpinned time is interpolated char-proportionally between the surrounding pins; with no pins at all, the outer bounds are the source time range. To fix a wrong timestamp, rewrite the segment with the same text and an explicitstart. speakeris optional per replacement; omitted, it inherits from the source segment the replacement’s repaired start time falls in.startIndexmust equalanchor.segmentIndex, and thecontentHashcoversstartIndex..endIndex(joined with\n). Limits: 50 source segments, 50 replacements, 2000 chars per replacement.
Anchors and the content hash
anchor.contentHash is a djb2 hash of the covered segment text (segments joined with \n for multi-segment selections), base-36 encoded:
seq and the queue-client contract
seq is an optional per-user, per-video monotonic counter for clients submitting streams of dependent corrections (sub-line splits re-index later segments, so order matters). One-off submissions can omit it. The error semantics are designed for at-least-once queue clients:
Use the correction’s client-generated UUID as the
Idempotency-Key: replays of final outcomes return the stored response with Idempotency-Replayed: true; non-final outcomes (412) are never cached.
Withdrawal
Every pending correction is deletable by its author. ThePOST response’s row id maps to:
Video-level entity merges
For misattributed name mentions within one video (“mentions of Imad in this video are Emad Mostaque”):replaceWith optionally respells the transcript text. Mentions of the same name in other videos are untouched.
Timestamps
Segment times are pipeline-derived and repaired on every structural correction: sub-line splits interpolate at the split point,segment_rewrite interpolates char-proportionally between pins. To set a time explicitly, pin it: rewrite the segment with the same text and an explicit start (or end). Dependent edits should share a seq stream so reviewers see them together.
Errors
Rate limits & idempotency
Standard per-key limits (RateLimit-* headers). Idempotency-Key on every POST; for corrections, only final outcomes are cached, and a 412 never replays.
Community Review
Transcript corrections are the Community Review surface for transcript content: pending-review rows, attributed to your key, 0 rows, withdrawable, reputation-building. Anything that is not transcript content (wrong entity in search, a bad mention row that led you to this video) goes throughPOST /v1/feedback with the matching type. Catalog: Community Review.
Common mistakes
Patterns and gotchas
- Always read before you write: corrections anchor to the
meta.revisionyou were served. - Batch dependent edits (splits then edits) under one
seqstream, strict FIFO per video. speaker_identifyhas visible side effects immediately (a community-flagged appearance on the person page); withdrawal removes it.- Refunds revoke unlocks: treat
refundedas “you have neither the transcript nor the charge.” - Quote before you buy:
meta.quoteon a free read tells you the exact row cost; check/v1/mefor budget. existing: trueon submit means an in-flight request already covers the video; poll that one.
Complete examples
Read-unlock-correct, end to end:TypeScript