The Inwista API is live
Overlay

API Reference

Transcribe, enhance and translate subtitles programmatically. Base URL, authentication, every endpoint and every error code — all on one page.
Base URL: https://api.inwista.ai/v1

Authentication

Create an API key in Dashboard → API Keys (workspace administrators only). The key is shown only once — store it like a password.

Send the key as a bearer token on every request. Keys are scoped to one workspace: everything the API returns belongs to it. Resources created via the API are visible in the dashboard, but dashboard projects are not exposed through the API.

Every authentication failure — missing header, unknown key, revoked key — returns the same 401 response.

curl https://api.inwista.ai/v1/transcriptions \
  -H "Authorization: Bearer inw_live_4f6a…"

Errors

Every error uses one envelope: an object with a machine-readable code and a human-readable message. Codes are additive-only within v1 — build on the code, not the message.

{ "error": { "code": "insufficient_credits", "message": "…" } }
StatusCodeWhen
401invalid_api_keyAuthentication failed (any reason)
400invalid_source_urlNot https, credentials in URL, private host, or malformed
400invalid_languageMissing or not an ISO 639-1 code
400invalid_num_speakersNot an integer between 1 and 32
400invalid_store_mediaNot a boolean
400invalid_retentionNot "standard" or "none", or combined with store_media true
400invalid_metadataNot an object, or over 1 KB
400invalid_settingsNot an object, or over 2 KB
400unreadable_sourceMedia duration could not be determined
400invalid_cursorstarting_after is not a known id
400invalid_formatCaption format not supported
402insufficient_creditsWallet does not cover the cost
404not_foundUnknown resource
404translation_not_foundThe requested language has no completed translation for the served version
409not_readyRequires a completed transcription
409operation_in_progressAn enhancement is still processing
409translation_existsLanguage already exists for this version
400invalid_idempotency_keyIdempotency-Key header empty or over 255 characters
400idempotency_key_reusedIdempotency-Key already used for a different request
409idempotency_conflictA request with this key is still being processed
429rate_limitedToo many requests — retry after the Retry-After header

Idempotency

Every POST charges credits when it is accepted, so a timed-out request that you blindly retry would create a second job and a second charge. Send an Idempotency-Key header (any unique string, up to 255 characters) and retries become safe: the first request's response is stored for 24 hours and replayed verbatim for every retry with the same key.

Reusing a key with a different request returns 400 idempotency_key_reused; retrying while the original is still running returns 409 idempotency_conflict. Error responses are never stored — a failed request never keeps its charge, so the key is released for a clean retry.

curl -X POST https://api.inwista.ai/v1/transcriptions \
  -H "Authorization: Bearer inw_live_…" \
  -H "Idempotency-Key: order-42-transcribe" \
  -H "Content-Type: application/json" \
  -d '{ "source_url": "…", "language": "en" }'

Rate limits

Each API key may make 300 read requests (GET) and 60 write requests (POST) per minute. The allowance refills continuously and can be spent all at once. Above the limit the API responds 429 rate_limited with a Retry-After header in seconds.

Treat the numbers as approximate: back off whenever you see a 429, and prefer webhooks over tight polling loops.

HTTP/1.1 429 Too Many Requests
Retry-After: 12

{ "error": { "code": "rate_limited", "message": "…" } }

Credits

Operations are metered in credits from your workspace's prepaid wallet (top up in Dashboard → Billing). Transcription costs 4 credits per started minute of media. Enhancement and translation are priced from content size — the same rates the studio charges. Retrieval, listing and polling are free.

Charges happen when a request is accepted. If an operation fails, the charge is refunded automatically and the resource reports credits_charged: 0. A 402 rejection never charges anything.

Pagination

List endpoints take limit (default 25, max 100) and starting_after — the last id of the previous page. Responses wrap results in a list object with has_more.

{ "object": "list", "data": [ … ], "has_more": true }

Transcriptions

Submit media by URL, poll until completed (or use webhooks), then retrieve captions. Statuses are processing, completed and failed. Timestamps are unix seconds.

Polling and results share one endpoint: GET /v1/transcriptions/{id} is where you watch status AND where the finished resource lives — there is no separate result endpoint. The only exception is the subtitle files themselves, which always come from the captions endpoint because they are raw file bodies, not JSON.

Create a transcription

POST/v1/transcriptions

Turn any hosted media file into accurate, timestamped subtitles without anyone opening the studio — feed your CMS, archive or publishing pipeline directly.

The media duration is probed before the request is accepted; the wallet is charged and the job queued in one atomic step. Returns 201 with the transcription resource.

Credits: 4 credits per started minute of media, charged when the job is accepted. Failed jobs are refunded automatically.

Body

FieldTypeDescription
source_urlrequiredstringPublic https URL of the media file, or a YouTube/TikTok/Vimeo URL
languagerequiredstringISO 639-1 code, e.g. "en" or "nb-NO"
diarizationbooleanSpeaker labels (default false)
num_speakersinteger1–32, hint for diarization
store_mediabooleanfalse = transcript-only: no playback assets are prepared (default true)
retentionstring"none" deletes the source media after transcription — the transcript is kept (default "standard")
metadataobjectYour own tags, up to 1 KB, echoed back verbatim
curl -X POST https://api.inwista.ai/v1/transcriptions \
  -H "Authorization: Bearer inw_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "source_url": "https://cdn.example.com/interview.mp4",
    "language": "en",
    "diarization": true
  }'

Response

{
  "id": "aB3dE9f2…",
  "object": "transcription",
  "status": "processing",
  "progress": 50,
  "language": "en",
  "duration_seconds": 1834,
  "diarization": true,
  "store_media": true,
  "retention": "standard",
  "source_url": "https://cdn.example.com/interview.mp4",
  "metadata": { "internal_ref": "case-42" },
  "credits_charged": 124,
  "error": null,
  "created": 1754558000
}

List transcriptions

GET/v1/transcriptions

Reconcile your catalogue against processed jobs or build a dashboard over everything you have transcribed.

API-created transcriptions, newest first. Standard pagination.

Credits: Free.

Query parameters

FieldTypeDescription
limitintegerPage size, default 25, max 100
starting_afterstringCursor: last id of the previous page
curl "https://api.inwista.ai/v1/transcriptions?limit=10" \
  -H "Authorization: Bearer inw_live_…"

Retrieve a transcription

GET/v1/transcriptions/{id}

Both the progress poll and the final result: watch status flip to completed, then read duration, language and charge from the same response.

Poll until status is completed (or register a webhook, below). Reading a failed transcription also triggers its automatic refund.

Credits: Free.

curl https://api.inwista.ai/v1/transcriptions/aB3dE9f2… \
  -H "Authorization: Bearer inw_live_…"

Response

{
  "id": "aB3dE9f2…",
  "object": "transcription",
  "status": "processing",
  "progress": 50,
  "language": "en",
  "duration_seconds": 1834,
  "diarization": true,
  "store_media": true,
  "retention": "standard",
  "source_url": "https://cdn.example.com/interview.mp4",
  "metadata": { "internal_ref": "case-42" },
  "credits_charged": 124,
  "error": null,
  "created": 1754558000
}

Delete a transcription

DELETE/v1/transcriptions/{id}

Erase a job on demand — your data-retention controls extended to a single call.

Permanently deletes the transcription and everything stored for it: media, transcript content, revisions, translations and comments. Aggregate billing counters are kept — they contain no content.

Only completed or failed jobs can be deleted; a job still processing returns 409, as does one with an enhancement or translation in flight. Deletion is immediate and irreversible.

Credits: endpoints.delete-transcription.pricing

curl -X DELETE https://api.inwista.ai/v1/transcriptions/aB3dE9f2… \
  -H "Authorization: Bearer inw_live_…"

Response

{
  "id": "aB3dE9f2…",
  "object": "transcription",
  "deleted": true
}

Retrieve captions

GET/v1/transcriptions/{id}/captions

Pull broadcast-ready subtitle files straight into your player, MAM or delivery pipeline — no manual exports, no format conversion on your side. In a video production pipeline, finished SRT or VTT drops straight into your NLE, review tool or packaging step the moment a cut is transcribed.

Returns the raw caption file body with the matching Content-Type — not JSON-wrapped. Responds 409 not_ready until the transcription completes.

Credits: Free.

Query parameters

FieldTypeDescription
formatrequiredstringsrt, vtt, json or txt
diarization"true"Prefix speaker labels
languagestringServe a completed translation instead of the source; 404 translation_not_found if none exists for the served version
revisionstringServe one exact version (an enhancement id is its revision id); omitted = the latest
curl "https://api.inwista.ai/v1/transcriptions/aB3dE9f2…/captions?format=srt" \
  -H "Authorization: Bearer inw_live_…" -o interview.srt

The json format is a versioned contract: { version: 1, language, segments: [{ index, start, end, text, speaker? }] } — seconds with millisecond precision, fields only ever added within a version.

Translations belong to the version they were created against — the translation resource's revision_id names it, and a later enhancement's version does not inherit them. Pass that revision_id as revision to pin the translated version.

Enhancements

AI subtitle enhancement — line length, balancing and timing rules — producing a new version of the subtitles. Requires a completed transcription. Once an enhancement completes, caption retrieval serves the enhanced version automatically.

Start an enhancement

POST/v1/transcriptions/{id}/enhance

Broadcast-grade timing and line balancing on autopilot — ship subtitles that pass QC without an editor touching them. For production houses, this automates the subtitle-conform step of the delivery pipeline: every episode leaves with consistent, spec-compliant subtitles.

Returns 202 with the enhancement resource.

Credits: Computed from the subtitle content size at the same rate the studio charges, deducted when the job is accepted. Refunded automatically on failure.

Body

FieldTypeDescription
settingsobjectStudio enhancement settings, up to 2 KB; omit for defaults

Enhancement settings

FieldTypeDescription
maxLinesPerBlock"1" | "2"Lines shown at once; broadcast standard and default is 2
maxCharactersPerLine1–100Characters per line; broadcast standard and default is 42
blockLineBalancingbottom_heavy | top_heavy | equal | unconstrainedVisual shape of two-line blocks; default unconstrained
textCondensationnone | smart | aggressiveLets the AI condense dialogue that reads too fast; default none (verbatim)
speakerDialogueFormatnone | hyphens | speaker_name | bracketsHow multiple speakers in one block are separated; default none
continuationMarkersnone | end | start | bothMarker placement when a sentence spans several blocks; default none
continuationMarkerStyledash | ellipsisDash or ellipsis for split sentences; default dash
gapBetweenBlocksnone | broadcasting | streaming | sdhForced empty gap between consecutive blocks; default broadcasting (~99 ms)
minBlockDuration0.1–60 sShortest time a block stays on screen, in seconds; default 1.0
maxBlockDuration> minLongest time a block stays on screen, in seconds; default 7.0
curl -X POST https://api.inwista.ai/v1/transcriptions/aB3dE9f2…/enhance \
  -H "Authorization: Bearer inw_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "settings": { "maxCharactersPerLine": 37, "textCondensation": "smart" } }'

Response

{
  "id": "rev8Xk3…",
  "object": "enhancement",
  "transcription_id": "aB3dE9f2…",
  "status": "processing",
  "progress": 0,
  "credits_charged": 12,
  "error": null,
  "created": 1754559000
}

Retrieve an enhancement

GET/v1/transcriptions/{id}/enhancements/{enhancementId}

Poll and result in one: when status reads completed, the captions endpoint already serves the enhanced version.

Poll until completed. Reading a failed enhancement triggers its automatic refund.

Credits: Free.

curl https://api.inwista.ai/v1/transcriptions/aB3dE9f2…/enhancements/rev8Xk3… \
  -H "Authorization: Bearer inw_live_…"

Translations

Subtitle translation with timing inherited from the source — one translation per language per subtitle version. Requires a completed transcription.

Start a translation

POST/v1/transcriptions/{id}/translate

One call per language turns a finished subtitle track into a localized version with identical timing — multiply the reach of every video you already have.

Returns 202. Responds 409 translation_exists if that language already exists for the current version.

Credits: Computed from the subtitle content size at the same rate the studio charges, per target language, deducted at accept. Refunded automatically on failure.

Body

FieldTypeDescription
target_languagerequiredstringISO 639-1 code, e.g. "es"
target_labelstringDisplay name, up to 60 characters
curl -X POST https://api.inwista.ai/v1/transcriptions/aB3dE9f2…/translate \
  -H "Authorization: Bearer inw_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "target_language": "es" }'

Response

{
  "id": "es",
  "object": "translation",
  "transcription_id": "aB3dE9f2…",
  "target_language": "es",
  "revision_id": "TRkf2nY7…",
  "status": "processing",
  "progress": 0,
  "credits_charged": 6,
  "error": null,
  "created": 1754559600
}

Retrieve a translation

GET/v1/transcriptions/{id}/translations/{language}

Poll and result in one: once completed, fetch the translated subtitle file from the captions endpoint with the language parameter.

Poll until completed, then fetch the translated captions from the captions endpoint with the language query parameter.

Credits: Free.

# Poll the translation resource
curl https://api.inwista.ai/v1/transcriptions/aB3dE9f2…/translations/es \
  -H "Authorization: Bearer inw_live_…"

# Once completed, fetch the translated subtitle file
curl "https://api.inwista.ai/v1/transcriptions/aB3dE9f2…/captions?format=vtt&language=es" \
  -H "Authorization: Bearer inw_live_…" -o interview-es.vtt

Webhooks

Configure a webhook in Dashboard → Integrations → Webhook to receive transcript.completed events instead of polling. Payloads are HMAC-SHA256 signed (X-Inwista-Signature: sha256=<hex> over the raw body) with the signing secret shown once at setup. File URLs are pre-signed and expire after 24 hours.

{
  "event": "transcript.completed",
  "id": "evt_9f2c…",
  "timestamp": 1754560000,
  "workspaceId": "ws_…",
  "project": {
    "id": "aB3dE9f2…",
    "name": "interview.mp4",
    "language": "en",
    "durationSeconds": 1834
  },
  "files": [
    {
      "format": "srt",
      "name": "interview.srt",
      "mimeType": "application/x-subrip; charset=utf-8",
      "url": "https://…",
      "expiresAt": 1754646400
    }
  ]
}
We value your privacy

We use cookies to understand how Inwista is used and to measure our advertising. Privacy policy · Cookie policy