Skip to main content

Overview

Transcripts are premium, diarized, entity-annotated, and community-correctable.
  • Base URL: https://api.arcmira.com, paths under /v1.
  • Auth: Authorization: Bearer arc_sk_... or x-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

Prefer the unified corrections route; the wrappers are conveniences for single-kind clients.

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 through POST /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 explicit start.
  • speaker is optional per replacement; omitted, it inherits from the source segment the replacement’s repaired start time falls in.
  • startIndex must equal anchor.segmentIndex, and the contentHash covers startIndex..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. The POST 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 through POST /v1/feedback with the matching type. Catalog: Community Review.

Common mistakes

Patterns and gotchas

  • Always read before you write: corrections anchor to the meta.revision you were served.
  • Batch dependent edits (splits then edits) under one seq stream, strict FIFO per video.
  • speaker_identify has visible side effects immediately (a community-flagged appearance on the person page); withdrawal removes it.
  • Refunds revoke unlocks: treat refunded as “you have neither the transcript nor the charge.”
  • Quote before you buy: meta.quote on a free read tells you the exact row cost; check /v1/me for budget.
  • existing: true on submit means an in-flight request already covers the video; poll that one.

Complete examples

Read-unlock-correct, end to end:
TypeScript