Skip to content
Listener.comDevelopers

Clusters

Episode clusters (an episode plus its clips and posts), cluster reports and timelines, and the AI cluster builder.

List clusters

GET/v1/clusters

Episode clusters across shows as the Clusters page lists them: reach by category and form, node, clip and post counts, type (episode, merged, manual), emptiness, freshness, sponsor brands, linked campaigns and public URL, plus meta.totals over reach-contributing clusters. Rows come from stored summaries (summary_stale marks rows that could not be recomputed).

time_range (default all) buckets by the cluster's episode publish date. series, guests and format match the episode's tags; custom_tags matches node tags (applied to at most 300 rows). state is all, active, empty, merged or manual. brand matches sponsored-node or linked-campaign brands.

SDKlistener.clusters.list()client.clusters.list()

Query parameters

  • show_idsarray of stringsnullable
    Limit to these show ids (default: every show in the workspace). Repeat the parameter or comma-separate values.
  • time_rangestring
    7d | 14d | 30d | 90d | 180d | 365d | all, or a quarter like 2026-Q2. Windows are UTC days anchored to today.

    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.
  • custom_tagsarray of stringsnullable
    Repeat the parameter or comma-separate values.
  • brandstringnullable
    Brand name or slug filter.
  • statestring
    One ofallactiveemptymergedmanual

    Default "all"

  • searchstringnullable
    Case-insensitive text filter.
  • sort_bystring
    Sort field.
    One ofpublished_attotal_reachyoutube_reachaudio_reachsocial_reachnode_countlast_refreshed_atname

    Default "published_at"

  • sort_orderstring
    asc or desc.
    One ofascdesc

    Default "desc"

  • 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

Cluster report

GET/v1/clusters/report

Cluster analytics per show and for the network over a window, as the Episodes-page KPI bar computes them: reach by category (YouTube long-form and Shorts, audio, social) and form, cluster count, per-cluster averages, growth against the previous window, reach distribution, content mix and cluster counts by type.

This is cluster reach (the clips and posts attached to episodes), a different number from the account-level GET /v1/analytics/reach. Clusters are bucketed by their episode's publish date; merged clusters are excluded unless include_merged=true and never double-counted with their sources. Growth is null when the previous window had no clusters.

Windows. time_range is 7d, 14d, 30d, 90d, 180d, 365d, all (rolling UTC days, today inclusive) or a calendar quarter such as 2026-Q2 (a bare Q2 means the current year). start_date + end_date (YYYY-MM-DD, inclusive, both required) override it. Results carry window and coverage.

Growth. Growth compares against the equal-length window immediately before (the previous quarter for quarter tokens). It is null (show it as "—", never 0) when the previous window has under 50 % day coverage. Windows longer than 30 days read stored history and compare like-for-like: only platforms whose data covers at least half of the previous window, both windows summed over those platforms (coverage.growth_platforms).

SDKlistener.clusters.report()client.clusters.report()

Query parameters

  • show_idsarray of stringsnullable
    Limit to these show ids (default: every show in the workspace). Repeat the parameter or comma-separate values.
  • categoriesarray of stringsnullable
    Only shows whose categories contain one of these (case-insensitive substring). Repeat the parameter or comma-separate values.
  • time_rangestring
    7d | 14d | 30d | 90d | 180d | 365d | all, or a quarter like 2026-Q2. Windows are UTC days anchored to today.

    Default "30d"

  • 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.
  • include_mergedboolean

    Default false

Returns

200

OK

  • windowobjectrequired
    The resolved analytics window (from `core.dates.Window.as_dict()`).
    Show child attributesHide child attributes
  • networkobjectrequired
    `buildClusterSummaryFromRows` for one window (the episodes-page KPI bar).
    Show child attributesHide child attributes
  • showsarray of objectsrequired
    Show child attributesHide child attributes
  • show_idsarray of stringsrequired
  • coverageobjectrequired
    How much of the window actually has data (design §2).
    Show child attributesHide child attributes
  • computed_atstringrequired
  • cache_age_secondsnumberrequired
  • objectstringrequired
  • include_mergedboolean

    Default false

  • categoriesarray of stringsnullable
  • freshnessmap of object
    Show child attributesHide child attributes
  • stale_platformsarray of strings
  • missing_show_idsarray of strings
  • summary_stale_countinteger
    Cluster rows whose stored summary could not be verified and were not recomputed.

    Default 0

  • notestringnullable

Errors

400parameter_invalid401credential_invalid403scope_missing404resource_not_found429rate_limited500internal_error

Create a cluster

POST/v1/shows/{show_id}/clusters

Create a cluster for a show: an episode cluster (one id in episode_ids), a merged cluster (two or more ids) or a manual cluster (manual: {name, …}, no episode ids). Episode ids must be episodes of the show. content_nodes optionally attaches clips and posts (platform label such as "YouTube Shorts", url, optional type, title, thumbnail, views, likes, comments, date). An existing cluster id is never overwritten; a merged cluster first creates any missing per-episode clusters.

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

SDKlistener.shows.clusters.create()client.shows.clusters.create()

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 stringsnullable
    Episode ids on the show.
  • namestringnullable
    Name.
  • internal_titlestringnullable
  • content_nodesarray of objectsnullable
  • manualobjectnullable
    Show child attributesHide child attributes
  • emv_ratesobjectnullable
    EMV rates: {long_form_cpm, short_form_cpm, like_rate, comment_rate}.
    Show child attributesHide child attributes
  • 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

Merge episode clusters

POST/v1/shows/{show_id}/clusters/merge

Build, or rebuild, the merged cluster for several episodes from their episode clusters. Source clusters are never modified; missing ones are created first. Rebuilding keeps the merged cluster's EMV rates, customization, internal title, brand collaboration and creation date.

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

SDKlistener.shows.clusters.merge()client.shows.clusters.merge()

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.
  • namestringnullable
    Name.
  • 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

Retrieve a cluster

GET/v1/shows/{show_id}/clusters/{cluster_id}

One cluster in full: metadata (type, episodes, customization, brand collaboration), 14 reach and engagement metrics computed from its nodes, every content node (platform, URL, reach, category, form, engagement, publish date, custom tags, sponsor, parent_id / child_ids canvas links), the canvas edges, subcluster rollups, custom-tag rollups, linked campaigns, the EMV estimate with the rate precedence used, the first-30-days estimate, refresh freshness and errors, and share URLs.

Nodes are sorted by reach and capped at node_limit (nodes_truncated says when more exist); edges and subclusters always cover the whole cluster. Rebuild the canvas tree by grouping nodes on parent_id.

SDKlistener.shows.clusters.retrieve()client.shows.clusters.retrieve()

Path parameters

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

Query parameters

  • include_nodesboolean

    Default true

  • node_limitinteger1 to 1000

    Default 200

Returns

200

OK

  • clusterobjectrequired
    Show child attributesHide child attributes
  • metricsobjectrequired
    The 14 reach / engagement fields `calculateClusterMetricsFromNodes` returns.
    Show child attributesHide child attributes
  • metrics_sourcestringrequired
  • first_30_daysobjectrequired
    Show child attributesHide child attributes
  • emvobjectrequired
    Show child attributesHide child attributes
  • node_countintegerrequired
    Content nodes (episode / clip / post) on the document.
  • tag_rollupsobjectrequired
    Show child attributesHide child attributes
  • subclustersobjectrequired
    The cluster's subclusters, as the canvas Subclusters list and the Cluster Analytics 'Subcluster' tab show them.
    Show child attributesHide child attributes
  • freshnessobjectrequired
    Show child attributesHide child attributes
  • computed_atstringrequired
  • cache_age_secondsnumberrequired
  • objectstringrequired
  • nodesarray of objects
    Show child attributesHide child attributes
  • nodes_truncatedboolean

    Default false

  • nodes_returnedinteger

    Default 0

  • edgesarray of objects
    Every canvas edge between content nodes, never truncated by node_limit, so parent links resolve even for nodes not returned.
    Show child attributesHide child attributes
  • sponsor_brandsarray of strings
  • brandsarray of strings
  • linked_campaignsarray of objects
    Show child attributesHide child attributes
  • last_refreshed_atstringnullable
  • last_refresh_attempt_atstringnullable
  • last_refresh_sourcestringnullable
  • refresh_errorsarray of objects
    Show child attributesHide child attributes
  • refresh_error_countinteger

    Default 0

  • platform_freshnessmap of object
    Show child attributesHide child attributes
  • stale_platformsarray of strings
  • notestringnullable

Errors

400parameter_invalid401credential_invalid403scope_missing404resource_not_found429rate_limited500internal_error

Update a cluster

PATCH/v1/shows/{show_id}/clusters/{cluster_id}

Update cluster metadata: name, internal title, description, image, cover, date, public-page customization, emv_rates and brand_collab. Only the fields you send change. emv_rates merges over the current rates; customization merges over the stored page overrides, and customization.co_brand ({name, logo_url, href}, https URLs) adds a partner co-brand to the public cluster page ("co_brand": null removes it); clear_customization, clear_internal_title and clear_brand_collab remove those fields. Description, image, cover and date on non-manual clusters last only until the next canvas save in the app.

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

SDKlistener.shows.clusters.update()client.shows.clusters.update()

Path parameters

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

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
  • namestringnullable
    Name.
  • internal_titlestringnullable
  • clear_internal_titleboolean

    Default false

  • descriptionstringnullable
    Free-text description.
  • imagestringnullable
  • coverstringnullable
  • datestringnullable
  • customizationobjectnullable
    Public cluster page overrides (`clusterCustomization.js`). Only the keys you send change.
    Show child attributesHide child attributes
  • clear_customizationboolean

    Default false

  • emv_ratesobjectnullable
    EMV rates: {long_form_cpm, short_form_cpm, like_rate, comment_rate}.
    Show child attributesHide child attributes
  • brand_collabobjectnullable
    Show child attributesHide child attributes
  • clear_brand_collabboolean

    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

Delete a cluster

DELETE/v1/shows/{show_id}/clusters/{cluster_id}

Permanently delete a cluster (no undo) and report, without fixing, the campaigns and shared reports that still reference it. Send ?dry_run=true first to see them. A manual cluster's synthetic episode is removed too. Clusters spanning several episodes are refused unless allow_merged=true.

  • Send ?dry_run=true to get {"object": "dry_run", "would_write": …} without writing anything.
  • Requires ?confirm=true; without it the API answers 409 confirmation_required with the exact preview.

SDKlistener.shows.clusters.delete()client.shows.clusters.delete()

Path parameters

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

Query parameters

  • allow_mergedboolean

    Default false

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

    Default false

  • confirmboolean
    Must be true to apply destructive or metered changes; without it the API answers 409 with a preview.

    Default false

Headers

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

Returns

200

OK

One of:

WriteResult
DryRun

Errors

400parameter_invalid401credential_invalid403scope_missing404resource_not_found409confirmation_required413body_too_large429rate_limited500internal_error

Cluster timeline

GET/v1/shows/{show_id}/clusters/{cluster_id}/timeline

The cluster page's "views / engagement over time" curve (granularity daily, weekly or monthly; chart_type cumulative or per period). It spreads the cluster's lifetime totals from its earliest episode date to today on a fixed decay, so synthetic is always true; for measured dates use the nodes' published_at from GET /v1/shows/{show_id}/clusters/{cluster_id}.

SDKlistener.shows.clusters.timeline()client.shows.clusters.timeline()

Path parameters

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

Query parameters

  • granularitystring
    One ofdailyweeklymonthly

    Default "daily"

  • chart_typestring
    daily values or a cumulative running total.
    One ofdailycumulative

    Default "cumulative"

Returns

200

OK

  • showobjectrequired
    The minimal show reference embedded in cross-show results.
    Show child attributesHide child attributes
  • cluster_idstringrequired
  • titlestringrequired
  • granularitystringrequired
    One ofdailyweeklymonthly
  • chart_typestringrequired
    One ofdailycumulative
  • syntheticbooleanrequired
    True: the app's shape-only curve (lifetime totals spread over time), not measured data.
  • start_datestringrequired
  • start_date_from_episodebooleanrequired
  • total_daysintegerrequired
  • data_pointsintegerrequired
  • total_viewsinteger | numberrequired
  • total_engagementinteger | numberrequired
  • pointsarray of objectsrequired
    Show child attributesHide child attributes
  • computed_atstringrequired
  • cache_age_secondsnumberrequired
  • objectstringrequired
  • notestringnullable

Errors

400parameter_invalid401credential_invalid403scope_missing404resource_not_found429rate_limited500internal_error

Add posts to an episode's cluster

POST/v1/shows/{show_id}/episodes/{episode_id}/posts

Add clips or posts to an episode's cluster by URL (YouTube, Shorts, TikTok, Instagram, Facebook, X, Threads, LinkedIn and the show's own Substack posts), including posts from accounts that are not connected. Each link is fetched (title, thumbnail, views, likes, comments) and added as a node; the cluster is created when the episode has none. Links already in the cluster are skipped. Up to 25 URLs per request. A dry run checks every link (supported platform, duplicates, whether a cluster will be created) without fetching.

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

SDKlistener.shows.episodes.addPosts()client.shows.episodes.add_posts()

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).

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
  • urlsarray of stringsrequired
    Post or clip URLs (up to 25).
  • 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

Remove cluster nodes

POST/v1/shows/{show_id}/clusters/{cluster_id}/nodes/remove

Remove nodes, and every edge touching them, from a cluster. The last episode node cannot be removed; to remove the whole cluster use DELETE /v1/shows/{show_id}/clusters/{cluster_id}.

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

SDKlistener.shows.clusters.nodes.remove()client.shows.clusters.nodes.remove()

Path parameters

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

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
  • node_idsarray of stringsrequired
    Cluster node ids.
  • 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 cluster node tags

POST/v1/shows/{show_id}/clusters/{cluster_id}/nodes/tags

Set, add or remove custom tags on specific cluster nodes (mode = replace, add, remove). Tags are trimmed and de-duplicated case-insensitively; only episode, clip and post nodes take tags (others come back in data.skipped); unknown node ids are an error. Episode tags are a separate system (POST /v1/shows/{show_id}/episodes/tags).

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

SDKlistener.shows.clusters.nodes.setTags()client.shows.clusters.nodes.set_tags()

Path parameters

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

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
  • node_idsarray of stringsrequired
    Cluster node ids.
  • tagsarray of stringsrequired
    Tags to apply.
  • 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

Refresh a cluster's metrics

POST/v1/shows/{show_id}/clusters/{cluster_id}/refresh

Re-fetch every clip and post of one cluster from its platforms and return per-platform node counts, the provider-call estimate and the result. Needs "confirm": true (one metered provider call per node). Merged clusters are refused; refresh their source clusters. A node whose fresh value reads 0 keeps its previous value.

  • Send "dry_run": true to get {"object": "dry_run", "would_write": …} without writing anything.
  • Requires "confirm": true; without it the API answers 409 confirmation_required with the exact preview.

SDKlistener.shows.clusters.refresh()client.shows.clusters.refresh()

Path parameters

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

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
  • dry_runboolean
    true returns exactly what would be written without writing anything (the API's test mode).

    Default false

  • confirmboolean
    Must be true to apply destructive or metered changes; without it the API answers 409 with a preview.

    Default false

Returns

200

OK

One of:

WriteResult
DryRun

Errors

400parameter_invalid401credential_invalid403scope_missing404resource_not_found409confirmation_required413body_too_large429rate_limited500internal_error

Accounts the cluster builder searches

GET/v1/shows/{show_id}/cluster-sources

The accounts the cluster builder searches for a show's clips and posts: connected accounts, accounts learned from the show's past clusters (with their share of past nodes) and accounts a person added, each with platform, handle, role (own, partner, guest) and on/off, plus the date window around each episode. The first request for a show learns the list from its past clusters (no cluster is modified). configured=false means the builder is not reachable from this server.

SDKlistener.shows.clusterSources.retrieve()client.shows.cluster_sources.retrieve()

Path parameters

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

Returns

200

OK

  • show_idstringrequired
  • show_namestringrequired
  • configuredbooleanrequired
  • computed_atstringrequired
  • cache_age_secondsnumberrequired
  • objectstringrequired
  • handlesarray of objects
    Show child attributesHide child attributes
  • enabled_countinteger

    Default 0

  • by_platformmap of integer
    Enabled handles per platform.
  • windowobjectnullable
    Show child attributesHide child attributes
  • learned_atstringnullable
  • updated_atstringnullable
  • app_urlstringnullable
  • notestringnullable

Errors

400parameter_invalid401credential_invalid403scope_missing404resource_not_found429rate_limited500internal_error

Update the cluster builder's accounts

PATCH/v1/shows/{show_id}/cluster-sources

Change which accounts the cluster builder searches for a show: add accounts (profile URL, platform:handle or {handle, platform, role}), remove accounts a person added, or enable / disable any account (platform:handle, @handle or a profile URL). Connected and learned accounts can only be turned off. Added accounts default to role partner; use own for the show's brand accounts and guest for a guest. Takes effect on the next build or content search.

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

SDKlistener.shows.clusterSources.update()client.shows.cluster_sources.update()

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
  • addarray of objectsnullable
    Show child attributesHide child attributes
  • removearray of stringsnullable
  • enablearray of stringsnullable
  • disablearray of stringsnullable
  • 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

Episodes to cluster

GET/v1/shows/{show_id}/cluster-candidates

The show's episodes as the cluster builder sees them, newest first, including the newest episodes from the live feed that the episodes list may not have yet: id, title, date, whether it has a cluster, its build status (ready = suggestions waiting) and source (stored or live). query keeps titles containing every word; from_date / to_date bound the publish date. Not paginated.

SDKlistener.shows.clusterCandidates.list()client.shows.cluster_candidates.list()

Path parameters

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

Query parameters

  • querystringnullable
    Search text.
  • from_datestringnullable
    Earliest publish date, YYYY-MM-DD.
  • to_datestringnullable
    Latest publish date, YYYY-MM-DD.
  • limitinteger1 to 200
    Maximum number of items to return.

    Default 30

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

Find an episode's clips and posts

GET/v1/shows/{show_id}/episodes/{episode_id}/content-candidates

Find the clips and posts that belong to one episode, including external posts not in the Listener library: searches the show's connected accounts, every enabled account in its cluster sources and any extra handles, then an AI judge scores each candidate (confidence, sure / likely, reason) and flags the ones already in the cluster.

handles adds accounts for this search only (x:northwindaudio, tiktok:@name, a profile URL, or @name with platforms). query (a guest, a topic) is a ranking hint, never a filter; judge=false skips the scoring (faster). from_date / to_date override the window around the episode date. Writes nothing, but external accounts can spend scraper credits on their first fetch of a month. Add the posts you pick with POST /v1/shows/{show_id}/episodes/{episode_id}/posts.

SDKlistener.shows.episodes.contentCandidates()client.shows.episodes.content_candidates()

Path parameters

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

Query parameters

  • querystringnullable
    Search text.
  • handlesarray of stringsnullable
    Repeat the parameter or comma-separate values.
  • platformsarray of stringsnullable
    Only these platforms (e.g. youtube, spotify, instagram; aliases accepted). Repeat the parameter or comma-separate values.
    One oftiktokinstagramfacebookxyoutubethreadslinkedin
  • from_datestringnullable
    Earliest publish date, YYYY-MM-DD.
  • to_datestringnullable
    Latest publish date, YYYY-MM-DD.
  • limitinteger1 to 100
    Maximum number of items to return.

    Default 30

  • judgeboolean

    Default true

Returns

200

OK

  • show_idstringrequired
  • show_namestringrequired
  • configuredbooleanrequired
  • computed_atstringrequired
  • cache_age_secondsnumberrequired
  • objectstringrequired
  • episodeobjectnullable
    Show child attributesHide child attributes
  • candidatesarray of objects
    Set when episode_title matched several episodes: pick one and call again with its id.
    Show child attributesHide child attributes
  • itemsarray of objects
    Show child attributesHide child attributes
  • totalinteger

    Default 0

  • library_countinteger

    Default 0

  • external_countinteger

    Default 0

  • sure_countinteger

    Default 0

  • in_cluster_countinteger

    Default 0

  • searched_handlesarray of strings
    Extra accounts passed in for this search.
  • searchedarray of objects
    Every account searched, with posts found and any fetch error.
    Show child attributesHide child attributes
  • windowobjectnullable
    Days searched before/after the episode date.
    Show child attributesHide child attributes
  • notestringnullable

Errors

400parameter_invalid401credential_invalid403scope_missing404resource_not_found429rate_limited500internal_error

Start an AI cluster build

POST/v1/cluster-builds

Build episode clusters with AI for a period or chosen episodes: for every episode in scope the builder gathers clips and posts from the show's connected and other accounts, judges which belong to the episode, and records suggestions (mode=suggest, default) or writes the sure ones straight into clusters (mode=auto).

Send "dry_run": true first: would_write.data.estimate gives episodes in scope, already clustered, to build, minutes and scraper credits. The real request starts a background job (data.job.job_id); follow it with GET /v1/cluster-builds/{job_id}, then review with GET /v1/shows/{show_id}/cluster-suggestions. scope: week, last30, range (from_date + to_date), backcatalog (up to 500 episodes per job) or episodes (episode_ids). Clustered episodes are skipped unless force=true. One job runs per show; starting another returns the running one (data.already_running).

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

SDKlistener.clusterBuilds.create()client.cluster_builds.create()

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
  • show_idstringrequired
    A show id in this workspace (see GET /v1/shows).
  • scopestring
    One ofweeklast30rangebackcatalogepisodes

    Default "week"

  • from_datestringnullable
    Earliest publish date, YYYY-MM-DD.
  • to_datestringnullable
    Latest publish date, YYYY-MM-DD.
  • episode_idsarray of stringsnullable
    Episode ids on the show.
  • modestring
    How the change applies (see the operation description).
    One ofsuggestauto

    Default "suggest"

  • forceboolean

    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

Retrieve a show's latest cluster build

GET/v1/cluster-builds/latest

The show's most recent cluster build: status, done / total, current phase and episode, counts (built, ready to review, nothing found, failed, skipped), per-episode outcomes, a one-line summary and the app link where suggestions are reviewed. Never cached; wait_seconds (up to 50) returns as soon as the job progresses.

SDKlistener.clusterBuilds.latest()client.cluster_builds.latest()

Query parameters

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

    Default 0

Returns

200

OK

  • show_idstringrequired
  • show_namestringrequired
  • configuredbooleanrequired
  • computed_atstringrequired
  • cache_age_secondsnumberrequired
  • objectstringrequired
  • jobobjectnullable
    Show child attributesHide child attributes
  • finishedboolean
    True once the job is done, failed or cancelled; stop polling then.

    Default false

  • summarystring
    One plain-English line to relay to the user.

    Default ""

  • outcomesarray of objects
    Show child attributesHide child attributes
  • review_urlstringnullable
    App page where the suggestions are reviewed.
  • notestringnullable

Errors

400parameter_invalid401credential_invalid403scope_missing404resource_not_found429rate_limited500internal_error

Retrieve a cluster build

GET/v1/cluster-builds/{job_id}

One cluster build job (from POST /v1/cluster-builds): status, done / total, current phase and episode, counts (built, ready to review, nothing found, failed, skipped), per-episode outcomes, a one-line summary and the review link. Never cached. To follow a running job pass wait_seconds (up to 50): the request returns as soon as the job progresses or finishes.

SDKlistener.clusterBuilds.retrieve()client.cluster_builds.retrieve()

Path parameters

  • job_idstringrequirednullable
    Cluster build job id returned by POST /v1/cluster-builds.

Query parameters

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

    Default 0

Returns

200

OK

  • show_idstringrequired
  • show_namestringrequired
  • configuredbooleanrequired
  • computed_atstringrequired
  • cache_age_secondsnumberrequired
  • objectstringrequired
  • jobobjectnullable
    Show child attributesHide child attributes
  • finishedboolean
    True once the job is done, failed or cancelled; stop polling then.

    Default false

  • summarystring
    One plain-English line to relay to the user.

    Default ""

  • outcomesarray of objects
    Show child attributesHide child attributes
  • review_urlstringnullable
    App page where the suggestions are reviewed.
  • notestringnullable

Errors

400parameter_invalid401credential_invalid403scope_missing404resource_not_found429rate_limited500internal_error

List cluster suggestions

GET/v1/shows/{show_id}/cluster-suggestions

Episodes with AI cluster suggestions, newest first (status=ready by default), each with its suggested clips and posts: key, platform, title, URL, handle, source (library or external), confidence, label (sure / likely), reason and metrics. Other statuses: built, accepted, nothing, failed, building, dismissed, all. items_per_episode=0 returns counts only. Not paginated; has_more says when more episodes wait.

SDKlistener.shows.clusterSuggestions.list()client.shows.cluster_suggestions.list()

Path parameters

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

Query parameters

  • statusstring
    Status filter or value.
    One ofreadybuildingbuiltacceptednothingfaileddismissedall

    Default "ready"

  • limitinteger1 to 100
    Maximum number of items to return.

    Default 20

  • items_per_episodeinteger0 to 100

    Default 25

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

Review cluster suggestions

POST/v1/shows/{show_id}/cluster-suggestions/{episode_id}/review

Accept or dismiss one episode's AI suggestions. Send accept_keys (item keys from GET /v1/shows/{show_id}/cluster-suggestions) and/or accept_all_sure=true, or dismiss=true, never both. Accepted posts are added to the episode's cluster (created when missing); dismiss drops every remaining suggestion. Unknown keys are an error listing the valid ones.

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

SDKlistener.shows.clusterSuggestions.review()client.shows.cluster_suggestions.review()

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).

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
  • accept_keysarray of stringsnullable
  • accept_all_sureboolean

    Default false

  • dismissboolean

    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