Skip to main content

spoken-md

A podcast transcript API returns a podcast episode as text in one request, instead of as audio. There are two kinds. A retrieval API hands back the transcript a show has already published, in seconds and for cents. A transcription API generates one from the audio, in minutes and for dollars, with speakers labelled "Speaker 0".

Spoken is the first kind. This package is its Python client: any published podcast episode as Markdown with real speaker names and timestamps, one call, no dependencies outside the standard library.

pip install spoken-md
from spoken_md import Spoken

spoken = Spoken()                                  # SPOKEN_API_KEY from the environment, else the demo key
episode = spoken.search("huberman sleep")[0]       # search by text, or paste a Spotify or YouTube link
transcript = spoken.transcript(episode.id)         # Markdown, 1 credit the first time, free after that
print(transcript)
**Andrew Huberman** (0:00)
Welcome to the Huberman Lab podcast, where we discuss science and
science-based tools for everyday life. Today my guest is Dr. Matt Walker...

**Matt Walker** (0:45)
Thank you for having me, Andrew.

The demo key pt_demo needs no signup. It searches and lists any show, and fetches the demo episode. A key of your own comes from spoken.md: 100 transcripts for $15, credits never expire, errors are never charged.

A whole show

Listing a show's episodes is free, and every id it returns has a transcript. archive walks the list, skips what you already hold, and yields the episode with transcript=None for the rare episode that has no transcript today.

from pathlib import Path
from spoken_md import Spoken

spoken = Spoken("pt_your_key")
show = spoken.episodes(episode.podcast_id)          # podcast_id comes with every search result
out = Path(show.podcast)
out.mkdir(exist_ok=True)

done = {p.stem for p in out.glob("*.md")}            # re-running only fetches what is missing
for item in spoken.archive(show.podcast_id, skip=done):
    if item.transcript:
        (out / f"{item.episode.id}.md").write_text(item.transcript.markdown)

A 300-episode show is 300 credits. Re-running it later costs nothing for the episodes already on disk, and a scheduled run picks up new episodes as they publish.

From the terminal

The package installs a spoken-md command with the same verbs.

export SPOKEN_API_KEY=pt_your_key            # or --key, or leave it unset for the demo key

spoken-md search "acquired costco"           # id, date, show, title per line; --json for JSON
spoken-md episodes 1050462261                # every fetchable episode of a show, free
spoken-md transcript 1000625088063 > costco.md
spoken-md archive 1050462261 -o acquired/    # one .md per episode, resumable
spoken-md balance

Errors

Every non-2xx response raises a typed error, so a loop can decide without reading a body.

Error Status Meaning Charged Retry
AuthError 401 Missing or invalid key. Carries purchase_url and demo_key. No No, fix the key
PaymentRequired 402 No credits left. Carries top_up_url; POST to it. No After topping up
NotFound 404 No such episode, or no published transcript for it. No No
Throttled 429 Throttled under load. No Yes
UpstreamError 502 Upstream failure. No Yes

Throttled and UpstreamError are retried twice with backoff before they reach you; set Spoken(retries=0) to see every one. All five subclass SpokenError.

from spoken_md import NotFound, PaymentRequired, Spoken

spoken = Spoken("pt_your_key")
try:
    transcript = spoken.transcript("1000625088063")
except NotFound:
    ...                                              # this episode has no transcript
except PaymentRequired as e:
    print("top up:", e.top_up_url)

What comes back

transcript() returns a Transcript: .markdown, .credits_remaining, .credits_charged (1 on a first fetch, 0 on a repeat), and .top_up_url when the balance is low. str() on it is the Markdown. search() returns Episode objects with id, title, podcast, podcast_id and date. episodes() returns a Show you can iterate. balance() returns credits, the attached email and recent usage.

When Spoken is the wrong tool

Spoken returns the published transcript of a podcast episode and nothing else. For your own audio, a meeting or an unpublished recording, use a speech-to-text API and add diarization. For YouTube videos that are not distributed as podcasts, use a YouTube transcript tool. Word-level timestamps and caption files are out of scope. The full list, with what to use instead, is in agents.md.

More

MIT licensed. Issues and pull requests: github.com/spokenmd/spoken.

Release files for spoken-md 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for spoken-md 0.1.0
File Size Uploaded
spoken_md-0.1.0.tar.gz 13.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for spoken-md 0.1.0
File Interpreter ABI Platform
spoken_md-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 25.4 kB

Release files / spoken_md-0.1.0.tar.gz

Download URL spoken_md-0.1.0.tar.gz
Size 13.3 kB
Tags Source
SHA-256 checksum
How to use checksums
353da694c692969aa7fe3ddd8c2bc70eff341d70eb570aa44a1f13c0fd4766f4
BLAKE2b-256 checksum
How to use checksums
dc7e22a367fe7e4898d078857e2aafeca4ffdfd7a66d667211bcb5bdd437c9d9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.14 {"installer":{"name":"uv","version":"0.12.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / spoken_md-0.1.0-py3-none-any.whl

Download URL spoken_md-0.1.0-py3-none-any.whl
Size 12.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7870a391286f45daa4f5f6ee33170c8f1c32384f073c9991c020a79a8ed07602
BLAKE2b-256 checksum
How to use checksums
e4fa7d7e3178f4c144601d4c154ad9851738a11691459e969a9851c72a43b0b7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.14 {"installer":{"name":"uv","version":"0.12.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

0.2.0

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page