
Base URL: https://api.inwista.ai/v1Create 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…"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": "…" } }| Status | Code | When |
|---|---|---|
| 401 | invalid_api_key | Authentication failed (any reason) |
| 400 | invalid_source_url | Not https, credentials in URL, private host, or malformed |
| 400 | invalid_language | Missing or not an ISO 639-1 code |
| 400 | invalid_num_speakers | Not an integer between 1 and 32 |
| 400 | invalid_store_media | Not a boolean |
| 400 | invalid_retention | Not "standard" or "none", or combined with store_media true |
| 400 | invalid_metadata | Not an object, or over 1 KB |
| 400 | invalid_settings | Not an object, or over 2 KB |
| 400 | unreadable_source | Media duration could not be determined |
| 400 | invalid_cursor | starting_after is not a known id |
| 400 | invalid_format | Caption format not supported |
| 402 | insufficient_credits | Wallet does not cover the cost |
| 404 | not_found | Unknown resource |
| 404 | translation_not_found | The requested language has no completed translation for the served version |
| 409 | not_ready | Requires a completed transcription |
| 409 | operation_in_progress | An enhancement is still processing |
| 409 | translation_exists | Language already exists for this version |
| 400 | invalid_idempotency_key | Idempotency-Key header empty or over 255 characters |
| 400 | idempotency_key_reused | Idempotency-Key already used for a different request |
| 409 | idempotency_conflict | A request with this key is still being processed |
| 429 | rate_limited | Too many requests — retry after the Retry-After header |
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" }'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": "…" } }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.
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 }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.
/v1/transcriptionsTurn 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
| Field | Type | Description |
|---|---|---|
source_urlrequired | string | Public https URL of the media file, or a YouTube/TikTok/Vimeo URL |
languagerequired | string | ISO 639-1 code, e.g. "en" or "nb-NO" |
diarization | boolean | Speaker labels (default false) |
num_speakers | integer | 1–32, hint for diarization |
store_media | boolean | false = transcript-only: no playback assets are prepared (default true) |
retention | string | "none" deletes the source media after transcription — the transcript is kept (default "standard") |
metadata | object | Your 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
}/v1/transcriptionsReconcile 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
| Field | Type | Description |
|---|---|---|
limit | integer | Page size, default 25, max 100 |
starting_after | string | Cursor: last id of the previous page |
curl "https://api.inwista.ai/v1/transcriptions?limit=10" \
-H "Authorization: Bearer inw_live_…"/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
}/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
}/v1/transcriptions/{id}/captionsPull 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
| Field | Type | Description |
|---|---|---|
formatrequired | string | srt, vtt, json or txt |
diarization | "true" | Prefix speaker labels |
language | string | Serve a completed translation instead of the source; 404 translation_not_found if none exists for the served version |
revision | string | Serve 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.srtThe 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.
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.
/v1/transcriptions/{id}/enhanceBroadcast-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
| Field | Type | Description |
|---|---|---|
settings | object | Studio enhancement settings, up to 2 KB; omit for defaults |
Enhancement settings
| Field | Type | Description |
|---|---|---|
maxLinesPerBlock | "1" | "2" | Lines shown at once; broadcast standard and default is 2 |
maxCharactersPerLine | 1–100 | Characters per line; broadcast standard and default is 42 |
blockLineBalancing | bottom_heavy | top_heavy | equal | unconstrained | Visual shape of two-line blocks; default unconstrained |
textCondensation | none | smart | aggressive | Lets the AI condense dialogue that reads too fast; default none (verbatim) |
speakerDialogueFormat | none | hyphens | speaker_name | brackets | How multiple speakers in one block are separated; default none |
continuationMarkers | none | end | start | both | Marker placement when a sentence spans several blocks; default none |
continuationMarkerStyle | dash | ellipsis | Dash or ellipsis for split sentences; default dash |
gapBetweenBlocks | none | broadcasting | streaming | sdh | Forced empty gap between consecutive blocks; default broadcasting (~99 ms) |
minBlockDuration | 0.1–60 s | Shortest time a block stays on screen, in seconds; default 1.0 |
maxBlockDuration | > min | Longest 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
}/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_…"Subtitle translation with timing inherited from the source — one translation per language per subtitle version. Requires a completed transcription.
/v1/transcriptions/{id}/translateOne 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
| Field | Type | Description |
|---|---|---|
target_languagerequired | string | ISO 639-1 code, e.g. "es" |
target_label | string | Display 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
}/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.vttConfigure 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 use cookies to understand how Inwista is used and to measure our advertising. Privacy policy · Cookie policy