Transcription for apps and SaaS products

Add voice notes, meeting transcripts, captions or searchable audio to your product with an asynchronous REST API: upload, start a job, take the webhook, store the segments.

Get an API key Quickstart Supported languages

Last updated: · Markdown

The production flow

  1. Your backend calls POST /upload for the user's file and returns the signed form to the client, or uploads it itself (up to 100 MiB).
  2. Start the job with the returned fileUrl as audio_url and your webhook_url.
  3. Verify the webhook signature, deduplicate on X-KolWrite-Delivery-Id, then fetch GET /transcribe/{jobId} if result_omitted is true.
  4. Store segments (text, times, speakers) and render them; export srt or txt on demand.

Job request:

{
  "audio_url": "<fileUrl from POST /upload>",
  "model": "oracle",
  "language": "auto",
  "include_diarization": true,
  "output_format": "json",
  "webhook_url": "https://api.example.com/hooks/kolwrite",
  "webhook_secret": "<random secret>"
}

Verify webhooks

import crypto from "node:crypto";

// Express-style handler; rawBody is the exact request body as received.
export function verifyKolWrite(req, rawBody, secret) {
  const timestamp = req.headers["x-kolwrite-timestamp"];
  const expected = "sha256=" + crypto.createHmac("sha256", secret)
    .update(timestamp + "." + rawBody).digest("hex");
  const given = req.headers["x-kolwrite-signature"] || "";
  return given.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
}

Deliveries are at least once and retried on failure. Keep GET /transcribe/{jobId} as the source of truth.

Pick the model per job

Product needSettingWhy
Cheap, clean audio at volumemodel: "base"0.40 credits (US$0.48) per hour
Default for user uploadsmodel: "oracle"Production quality, translation available
Transcript in another languageOracle model + translate_toOne job, translated text
Who said whatinclude_diarization: trueIncluded in the price
Captionsoutput_format: "srt"Ready for video players

Limits to design around

  • Inline Base64 upload: 4 MiB of decoded media. Signed upload: 100 MiB.
  • The signed upload form expires after 60 minutes; the read URL after 24 hours.
  • Jobs are asynchronous: show progress from PENDING and IN_PROGRESS.
  • channel_speaker_separation cannot be combined with diarization.

Questions and answers

Is there an SDK?

The API is plain HTTPS and JSON; use any HTTP client. The OpenAPI 3.1 file at /openapi.json works with code generators.

How do I avoid double-processing webhooks?

Deduplicate on X-KolWrite-Delivery-Id; retries reuse it.

How is usage billed?

Per second of audio at the model's credit rate, with no minimum. 1 credit is US$1.20.