Same free trial as the web UI: 60 minutes of audio on signup. Create a key in Settings, then call the endpoints below.

Podcast transcript API

Podskrift’s podcast transcription API lets agents and scripts get a transcript over HTTP — the same path as the web UI. Resolve an episode by publisher/show and date (or URL), start Whisper, poll until ready, then fetch the plain-text transcript. New accounts get 60 free trial minutes on our OpenAI key; after that, add your own. Create a psk_… key in Settings.

Authentication

Generate a key in Settings → API key (psk_…). One key per account.

Send it on every request as either:

Authorization: Bearer psk_…

or

X-Api-Key: psk_…

Revoke or Regenerate in Settings — old keys stop working immediately. Never commit a real key.

export PODSKRIFT_API_KEY='psk_…'   # from Settings — never commit

POST /api/v1/resolve

Also available as GET with the same fields as query parameters.

Look up a public catalog episode (RSS / iTunes / Spotify / Apple / direct audio) without creating a transcription job.

Field In Meaning
publisher / show body or query Show title (case-insensitive; exact match preferred)
date body or query YYYY-MM-DD, Europe/Oslo calendar date
url body or query Optional Spotify / Apple / .mp3 / RSS instead of (or with) publisher
curl -sS -H "Authorization: Bearer $PODSKRIFT_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"publisher":"Spårtsklubben","date":"2026-09-10"}' \
  https://podskrift.com/api/v1/resolve

Example response:

{
  "episode": {
    "title": "Ukas iddiot …",
    "publisher": "Spårtsklubben",
    "published_at": "2026-09-10",
    "audio_url": "https://…/episode.mp3",
    "rss_url": "https://…/feed.xml",
    "duration_min": 62.0,
    "artwork_url": "https://…/art.jpg",
    "transcript_status": "none"
  }
}

Catalog hits have no id yet — that appears after you start a transcription. Not found / no public RSS → 404 with error and "episode": null.

POST /api/v1/transcriptions

Resolve (if needed) and enqueue Whisper for your account. Same trial path and OpenAI key rules as the web UI.

Request fields match resolve (publisher+date and/or url). You may also pass a resolved audio_url with title (and optional publisher, published_at, duration_min, rss_url, artwork_url), plus optional language.

curl -sS -H "Authorization: Bearer $PODSKRIFT_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"publisher":"Spårtsklubben","date":"2026-09-10"}' \
  https://podskrift.com/api/v1/transcriptions

Example response (201 when a new job is created):

{
  "id": "a1b2c3d4e5f6",
  "title": "Ukas iddiot …",
  "publisher": "Spårtsklubben",
  "published_at": "2026-09-10",
  "transcript_status": "pending",
  "language": null,
  "audio_duration_seconds": null,
  "artwork_url": "https://…/art.jpg",
  "rss_url": "https://…/feed.xml",
  "task_status": "downloading",
  "started_at": "2026-09-10T12:00:00",
  "completed_at": null,
  "error_message": null,
  "reused": false
}

Starting the same show+date again while a job is still pending or ready returns that task with "reused": true and HTTP 200 (no double charge).

GET /api/v1/episodes

Search your already-transcribed jobs (not the public catalog — use resolve for that). At least one of publisher/show or date is required.

Param Meaning
publisher / show Case-insensitive substring on show name
date YYYY-MM-DD, Europe/Oslo against episode publish time
limit Max rows (default 50, hard cap 100)
curl -sS -H "Authorization: Bearer $PODSKRIFT_API_KEY" \
  "https://podskrift.com/api/v1/episodes?publisher=Forklaring&date=2026-09-12"

Example response:

{
  "episodes": [
    {
      "id": "a1b2c3d4e5f6",
      "title": "Episode title",
      "publisher": "Forklaring",
      "published_at": "2026-09-12",
      "transcript_status": "ready",
      "language": "no",
      "audio_duration_seconds": 1800.0,
      "artwork_url": null,
      "rss_url": null,
      "task_status": "completed",
      "started_at": "2026-09-12T08:00:00",
      "completed_at": "2026-09-12T08:05:00",
      "error_message": null
    }
  ],
  "count": 1,
  "filters": {
    "publisher": "Forklaring",
    "date": "2026-09-12",
    "timezone": "Europe/Oslo"
  }
}

Empty match → {"episodes":[],"count":0,…} (not an error). Other accounts’ jobs are invisible.

GET /api/v1/episodes/{id}

Metadata for one of your transcription jobs, including transcript_status: none | pending | ready | failed. Poll this after start.

curl -sS -H "Authorization: Bearer $PODSKRIFT_API_KEY" \
  "https://podskrift.com/api/v1/episodes/$ID"

Example response:

{
  "id": "a1b2c3d4e5f6",
  "title": "Ukas iddiot …",
  "publisher": "Spårtsklubben",
  "published_at": "2026-09-10",
  "transcript_status": "ready",
  "language": "no",
  "audio_duration_seconds": 3720.0,
  "artwork_url": "https://…/art.jpg",
  "rss_url": "https://…/feed.xml",
  "task_status": "completed",
  "started_at": "2026-09-10T12:00:00",
  "completed_at": "2026-09-10T12:08:00",
  "error_message": null
}

Unknown id, or another account’s job → 404.

GET /api/v1/episodes/{id}/transcript

When transcript_status is ready, JSON includes text (full plain transcript). When not ready, the same shape with transcript_status set and no text — HTTP 200, never a server error for a pending job.

Param Meaning
format txt (default) or srt when segment timestamps were stored
raw 1 / true / yestext/plain body when ready (txt only)
curl -sS -H "Authorization: Bearer $PODSKRIFT_API_KEY" \
  "https://podskrift.com/api/v1/episodes/$ID/transcript"

Example response (ready):

{
  "id": "a1b2c3d4e5f6",
  "title": "Ukas iddiot …",
  "publisher": "Spårtsklubben",
  "transcript_status": "ready",
  "format": "txt",
  "text": "Full transcript text…"
}

Errors

Status Meaning
400 Bad input (missing fields, invalid date, unsupported format)
401 Missing / wrong / revoked key
402 Trial exhausted (or episode past the trial per-episode cap)
403 No OpenAI key available for the account (trial off and no key in Settings)
404 Not found (catalog miss, or another account’s job)
429 Too many transcription starts, or too many jobs already in flight