Skip to content
Listener.comDevelopers

Episodes

Episodes per show, search, transcripts, tags, goals and Substack download figures.

List a show's episodes

GET/v1/shows/{show_id}/episodes

A show's episodes as the Episodes page lists them: every episode the app knows (stored feed, Spotify chart, Apple and cluster-derived, newest first) with its cluster's reach by category and form, clips and posts, engagement, tags, brands, freshness and the cluster's public URL. meta.episode_count is the full count.

time_range (all, 30d, 90d, quarter, year; or start_date + end_date) filters on the publish date; undated episodes never fall inside an active window. series, guests and format match episode tags (any-of). has_cluster=true keeps episodes whose cluster exists or has reach. view=goals adds per-episode target deltas from the show's episode goals; view=benchmark adds deltas against same-format peer medians (minimum 3 peers) and the ≥ 3× breakout badge.

SDKlistener.shows.episodes.list()client.shows.episodes.list()

Path parameters

  • show_idstringrequired
    A show id in this workspace (see GET /v1/shows).

Query parameters

  • time_rangestring
    7d | 14d | 30d | 90d | 180d | 365d | all, or a quarter like 2026-Q2. Windows are UTC days anchored to today.
    One ofall30d90dquarteryear

    Default "all"

  • start_datestringnullable
    First day, YYYY-MM-DD (inclusive). With end_date, overrides time_range.
  • end_datestringnullable
    Last day, YYYY-MM-DD (inclusive). With start_date, overrides time_range.
  • seriesarray of stringsnullable
    Series tags. Repeat the parameter or comma-separate values.
  • guestsarray of stringsnullable
    Guest tags. Repeat the parameter or comma-separate values.
  • formatarray of stringsnullable
    Format tags. Repeat the parameter or comma-separate values.
  • brandstringnullable
    Brand name or slug filter.
  • searchstringnullable
    Case-insensitive text filter.
  • has_clusterbooleannullable
  • sort_bystring
    Sort field.
    One ofdatetotal_reachyoutube_reachaudio_reachsocial_reachlong_form_reachshort_form_reachclipspoststitle

    Default "date"

  • sort_orderstring
    asc or desc.
    One ofascdesc

    Default "desc"

  • viewstring
    One ofsimplegoalsbenchmark

    Default "simple"

  • limitinteger1 to 100
    Page size, 1–100 (default 25).

    Default 25

  • cursorstring
    Opaque cursor from the previous page's next_cursor.

Returns

200

OK

  • objectstringrequired
  • urlstringrequired
  • dataarray of objectsrequired
    Show child attributesHide child attributes
  • has_morebooleanrequired
  • next_cursorstringrequirednullable
    Pass as cursor to get the next page; null on the last page.
  • metaobjectrequired
    The result's non-list fields (window, coverage, freshness, totals, note …).
    Show child attributesHide child attributes

Errors

400parameter_invalid401credential_invalid403scope_missing404resource_not_found429rate_limited500internal_error

Retrieve an episode

GET/v1/shows/{show_id}/episodes/{episode_id}

One episode: its episodes-table row (reach by category and form, clips and posts, engagement, tags, brands), stored Spotify / Apple / YouTube stats when present, a summary of its cluster (type, node counts, sponsor brands, linked campaigns, freshness), goal deltas and benchmark bucket. episode_id is any id GET /v1/shows/{show_id}/episodes or GET /v1/episodes/search returns (Spotify, manual-<ts>, Apple or synthetic ids).

SDKlistener.shows.episodes.retrieve()client.shows.episodes.retrieve()

Path parameters

  • show_idstringrequired
    A show id in this workspace (see GET /v1/shows).
  • episode_idstringrequired
    Episode id (see GET /v1/shows/{show_id}/episodes).

Returns

200

OK

  • showobjectrequired
    The minimal show reference embedded in cross-show results.
    Show child attributesHide child attributes
  • episodeobjectrequired
    One episodes-table row: the episode plus its source cluster's reach.
    Show child attributesHide child attributes
  • computed_atstringrequired
  • cache_age_secondsnumberrequired
  • objectstringrequired
  • sourcesarray of strings
    Index sources that produced the row (stored feed, chart, apple, cluster, provider).
  • spotifyobjectnullable
    Spotify chart / stored stats for the episode when present.
  • appleobjectnullable
  • youtubeobjectnullable
  • clusterobjectnullable
    Show child attributesHide child attributes
  • goalsobjectnullable
    Show child attributesHide child attributes
  • benchmarkobjectnullable
    Show child attributesHide child attributes
  • freshnessmap of object
    Show child attributesHide child attributes
  • stale_platformsarray of strings
  • notestringnullable

Errors

400parameter_invalid401credential_invalid403scope_missing404resource_not_found429rate_limited500internal_error

Retrieve an episode transcript

GET/v1/shows/{show_id}/episodes/{episode_id}/transcript

The best text available for an episode. Listener stores no transcripts, so status is description-fallback (the episode description, up to 9,000 characters) or missing; transcript text saved on a linked campaign's air check is returned in aircheck_excerpts. Do not present the description as a transcript.

SDKlistener.shows.episodes.transcript()client.shows.episodes.transcript()

Path parameters

  • show_idstringrequired
    A show id in this workspace (see GET /v1/shows).
  • episode_idstringrequired
    Episode id (see GET /v1/shows/{show_id}/episodes).

Returns

200

OK

  • showobjectrequired
    The minimal show reference embedded in cross-show results.
    Show child attributesHide child attributes
  • episode_idstringrequired
  • titlestringrequired
  • statusstringrequired
    One ofdescription-fallbackmissing
  • computed_atstringrequired
  • cache_age_secondsnumberrequired
  • objectstringrequired
  • published_atstringnullable
  • textstringnullable
    Description text (there is no stored transcript); capped at 9,000 characters.
  • truncatedboolean

    Default false

  • character_countinteger

    Default 0

  • aircheck_excerptsarray of objects
    Transcript text found on linked campaigns' air-check analyses, when any.
    Show child attributesHide child attributes
  • share_urlstringnullable
  • notestringnullable

Errors

400parameter_invalid401credential_invalid403scope_missing404resource_not_found429rate_limited500internal_error

Search episodes

GET/v1/episodes/search

Search episodes by title, description, episode number or date across the workspace's shows; each hit carries its show, reach fields and cluster id. Real feed episodes rank above manual and synthetic rows. Pass show_ids to keep it fast on large networks. Results are not paginated; limit caps them.

SDKlistener.episodes.search()client.episodes.search()

Query parameters

  • querystringrequired
    Search text.
  • show_idsarray of stringsnullable
    Limit to these show ids (default: every show in the workspace). Repeat the parameter or comma-separate values.
  • limitinteger1 to 100
    Maximum number of items to return.

    Default 20

Returns

200

OK

  • objectstringrequired
  • urlstringrequired
  • dataarray of objectsrequired
    Show child attributesHide child attributes
  • has_morebooleanrequired
  • next_cursorstringrequirednullable
    Pass as cursor to get the next page; null on the last page.
  • metaobjectrequired
    The result's non-list fields (window, coverage, freshness, totals, note …).
    Show child attributesHide child attributes

Errors

400parameter_invalid401credential_invalid403scope_missing404resource_not_found429rate_limited500internal_error

Set episode tags

POST/v1/shows/{show_id}/episodes/tags

Set episode tags (series, format, guests) on one or more episodes of a show. mode=replace overwrites every dimension you send (omitted dimensions become empty); add unions and remove subtracts only the dimensions you send. Values are trimmed and de-duplicated case-insensitively.

Any id the episodes list returns is accepted; episodes known only from provider data or clusters are added to the stored list (data.appended), unknown ids come back in data.not_found. Cluster node tags are a separate system (POST /v1/shows/{show_id}/clusters/{cluster_id}/nodes/tags).

  • Send "dry_run": true to get {"object": "dry_run", "would_write": …} without writing anything.

SDKlistener.shows.episodes.setTags()client.shows.episodes.set_tags()

Path parameters

  • show_idstringrequired
    A show id in this workspace (see GET /v1/shows).

Headers

  • Idempotency-Keystringmax 255 chars
    Any unique string (≤ 255 chars). Replays within 24 h return the stored response with Idempotent-Replayed: true.

Request body

application/json
  • episode_idsarray of stringsrequired
    Episode ids on the show.
  • seriesarray of stringsnullable
    Series tags.
  • formatarray of stringsnullable
    Format tags.
  • guestsarray of stringsnullable
    Guest tags.
  • modestring
    How the change applies (see the operation description).
    One ofreplaceaddremove

    Default "replace"

  • dry_runboolean
    true returns exactly what would be written without writing anything (the API's test mode).

    Default false

Returns

200

OK

One of:

WriteResult
DryRun

Errors

400parameter_invalid401credential_invalid403scope_missing404resource_not_found409confirmation_required413body_too_large429rate_limited500internal_error

Set episode goals

PUT/v1/shows/{show_id}/episode-goals

Set the show's episode reach goals: a default target plus per-series overrides in by_series (keyed by series tag). Targets accept integers or shorthand ("250k", "1.2M"); only positive values are kept. clear=true, or no positive target at all, removes the goals.

  • Send "dry_run": true to get {"object": "dry_run", "would_write": …} without writing anything.

SDKlistener.shows.setEpisodeGoals()client.shows.set_episode_goals()

Path parameters

  • show_idstringrequired
    A show id in this workspace (see GET /v1/shows).

Headers

  • Idempotency-Keystringmax 255 chars
    Any unique string (≤ 255 chars). Replays within 24 h return the stored response with Idempotent-Replayed: true.

Request body

application/json
  • defaultobjectnullable
    Reach targets; accept ints or shorthand strings like "250k", "1.2M", "1,200,000".
    Show child attributesHide child attributes
  • by_seriesmap of objectnullable
    Show child attributesHide child attributes
  • clearboolean

    Default false

  • dry_runboolean
    true returns exactly what would be written without writing anything (the API's test mode).

    Default false

Returns

200

OK

One of:

WriteResult
DryRun

Errors

400parameter_invalid401credential_invalid403scope_missing404resource_not_found409confirmation_required413body_too_large429rate_limited500internal_error

Set Substack podcast downloads for an episode node

PUT/v1/shows/{show_id}/substack/downloads

Set the podcast downloads on one Substack card by hand (the app's "Edit metrics"), or remove that override with clear=true. The figure is stored beside Substack's API count, counts in the cluster's audio reach and wins over an imported figure until cleared. node_id is a Substack post node from GET /v1/shows/{show_id}/clusters/{cluster_id} (its podcast_downloads_source is manual, export or api). For many episodes at once use POST /v1/shows/{show_id}/substack/imports.

  • Send "dry_run": true to get {"object": "dry_run", "would_write": …} without writing anything.

SDKlistener.shows.substack.setDownloads()client.shows.substack.set_downloads()

Path parameters

  • show_idstringrequired
    A show id in this workspace (see GET /v1/shows).

Headers

  • Idempotency-Keystringmax 255 chars
    Any unique string (≤ 255 chars). Replays within 24 h return the stored response with Idempotent-Replayed: true.

Request body

application/json
  • cluster_idstringrequired
    Cluster id (see GET /v1/clusters).
  • node_idstringrequired
    Cluster node id.
  • downloadsintegernullable
  • clearboolean

    Default false

  • dry_runboolean
    true returns exactly what would be written without writing anything (the API's test mode).

    Default false

Returns

200

OK

One of:

WriteResult
DryRun

Errors

400parameter_invalid401credential_invalid403scope_missing404resource_not_found409confirmation_required413body_too_large429rate_limited500internal_error

Import Substack episode stats (CSV)

POST/v1/shows/{show_id}/substack/imports

Import Substack's podcast "episode stats" CSV for a show: each row's Total becomes the downloads on that episode's Substack cards (source export) and in cluster audio reach. Substack's API only reports the downloads Substack tracks itself (often ~70 % of the export), so this brings in the publisher's full figure.

csv_text is the whole exported file (columns Title, Date, Downloads (first 30d), Total, …); rows match posts on publish time to the second, then title. Re-importing is safe. Hand-set figures still win. Send it with "dry_run": true first and review each cluster's before → after.

  • Send "dry_run": true to get {"object": "dry_run", "would_write": …} without writing anything.

SDKlistener.shows.substack.importStats()client.shows.substack.import_stats()

Path parameters

  • show_idstringrequired
    A show id in this workspace (see GET /v1/shows).

Headers

  • Idempotency-Keystringmax 255 chars
    Any unique string (≤ 255 chars). Replays within 24 h return the stored response with Idempotent-Replayed: true.

Request body

application/json
  • csv_textstringrequired
  • file_namestringnullable
  • dry_runboolean
    true returns exactly what would be written without writing anything (the API's test mode).

    Default false

Returns

200

OK

One of:

WriteResult
DryRun

Errors

400parameter_invalid401credential_invalid403scope_missing404resource_not_found409confirmation_required413body_too_large429rate_limited500internal_error