REST API reference

Libsyn API

14 endpoints for Libsyn. Each one is a POST with a JSON body, called with your API key.

Base URL
https://cotlet.dev/api/v1/libsyn
Connects with
Your own Libsyn account (the account owner or a user with publishing rights)
Integration page
Libsyn
Libsyn Endpoints
Endpoint Access Description
list_shows read Podcasts in your Libsyn account with show ID, title and plan.
list_episodes read Episodes of a show, newest first, with status, release date and ad count.
get_episode read Title, description (show notes), status, release date, season, episode number, type, rating, author and media file info.
get_show_stats read Show downloads: all time, this week, this month, and per day for a date range.
get_episode_stats read Downloads in the first 7, 30 and 90 days, all time, and per day for a date range.
get_downloads_breakdown read Downloads split by country, listening app or publishing destination. Advanced Plan and higher.
export_stats_report read Stats Export as a download link: one file per report, zipped. Advanced Plan and higher.
upload_media write Add an audio, video or PDF file to your media library from a URL. Nothing is published.
create_episode write New episode saved as a Draft. Nothing reaches listeners until you schedule or publish it.
update_episode write Change the title, show notes or details of a draft or published episode; the RSS feed updates.
bulk_edit_episodes write Set author or season on many episodes at once and renumber them in order.
upload_transcript write Attach an SRT or VTT transcript; Libsyn adds it to the feed for Apple Podcasts and other apps.
schedule_episode write Release a draft at a set time to every destination your show is connected to.
publish_episode write Publish a draft now to every destination your show is connected to.

List shows

read

POST https://cotlet.dev/api/v1/libsyn/list_shows

Returns every podcast your Libsyn account can open, with its show ID, title and plan. Use the show_id from each item as the show_id parameter of the other tools. If you have one show, the other tools default to it.

Parameters

No parameters.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/libsyn/list_shows \
  -H "Authorization: Bearer $COTLET_API_KEY"
Python
result = client.call("libsyn", "list_shows")
Node.js
const result = await cotlet.call('libsyn', 'list_shows');
CLI
cotlet call libsyn list_shows

Example response

{
  "shows": [
    {
      "show_id": "412907",
      "title": "The Solo Founder Show",
      "plan": "Advanced"
    },
    {
      "show_id": "518344",
      "title": "Founder Shorts",
      "plan": "Basic"
    }
  ]
}

POST https://cotlet.dev/api/v1/libsyn/list_episodes

Lists episodes of one show, newest first, with status (draft, scheduled or published) and release date, as in the dashboard Episodes list. Filter by status or match titles with search. Pages hold up to 100 episodes; pass next_cursor back as cursor for the next page.

Parameters

Parameters: list_episodes
NameTypeDescription
show_id string Show ID from list_shows. Default: your only or first show.
status enum all, draft, scheduled or published. Default: all.
search string Match episode titles.
limit integer 1 to 100. Default: 25.
cursor string Next page cursor from the previous call.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/libsyn/list_episodes \
  -H "Authorization: Bearer $COTLET_API_KEY"
Python
result = client.call("libsyn", "list_episodes")
Node.js
const result = await cotlet.call('libsyn', 'list_episodes');
CLI
cotlet call libsyn list_episodes

Example response

{
  "episodes": [
    {
      "episode_id": "38415502",
      "title": "Pricing your first product",
      "status": "scheduled",
      "release_at": "2026-10-15T06:00:00-04:00",
      "ads": 0
    },
    {
      "episode_id": "38201877",
      "title": "Your first ten customers",
      "status": "published",
      "release_at": "2026-10-01T06:00:00-04:00",
      "ads": 2
    }
  ],
  "next_cursor": "eyJvZmZzZXQiOjI1fQ"
}

POST https://cotlet.dev/api/v1/libsyn/get_episode

Returns one episode with its full show notes, status, release date, season and episode number, type, rating, author, tags and media file. Use it before update_episode so your agent edits the current text and does not overwrite it.

Parameters

Parameters: get_episode
NameTypeDescription
episode_id required string Episode ID from list_episodes.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/libsyn/get_episode \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"episode_id":"episode_8c41d2"}'
Python
result = client.call("libsyn", "get_episode", {"episode_id":"episode_8c41d2"})
Node.js
const result = await cotlet.call('libsyn', 'get_episode', {"episode_id":"episode_8c41d2"});
CLI
cotlet call libsyn get_episode --data '{"episode_id":"episode_8c41d2"}'

Example response

{
  "episode_id": "38415502",
  "title": "Pricing your first product",
  "description": "How to price your first product.",
  "status": "scheduled",
  "release_at": "2026-10-15T06:00:00-04:00",
  "season_number": 3,
  "episode_number": 12,
  "episode_type": "full",
  "rating": "clean",
  "author": "Dana Kim",
  "tags": [
    "pricing",
    "startups"
  ],
  "media_file": {
    "file_name": "ep-142.mp3",
    "media_file_id": "f_88120"
  }
}

POST https://cotlet.dev/api/v1/libsyn/get_show_stats

Returns downloads for the whole show: all time, this week, this month and per day for the range you choose. The metric is IAB downloads by default, or unique downloads. Read only.

Parameters

Parameters: get_show_stats
NameTypeDescription
show_id string Show ID from list_shows. Default: your only or first show.
start_date date First day of the range. Default: 30 days ago.
end_date date Last day of the range. Default: today.
metric enum iab or unique downloads. Default: iab.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/libsyn/get_show_stats \
  -H "Authorization: Bearer $COTLET_API_KEY"
Python
result = client.call("libsyn", "get_show_stats")
Node.js
const result = await cotlet.call('libsyn', 'get_show_stats');
CLI
cotlet call libsyn get_show_stats

Example response

{
  "show_id": "412907",
  "metric": "iab",
  "all_time": 214530,
  "this_week": 1260,
  "this_month": 5210,
  "daily": [
    {
      "date": "2026-10-01",
      "downloads": 190
    },
    {
      "date": "2026-10-02",
      "downloads": 205
    },
    {
      "date": "2026-10-03",
      "downloads": 198
    }
  ],
  "range_total": 593
}

POST https://cotlet.dev/api/v1/libsyn/get_episode_stats

Returns downloads for one episode in its first 7, 30 and 90 days and all time, so episodes of different age compare fairly. daily lists downloads per day for the range you choose and range_total adds them up. Read only.

Parameters

Parameters: get_episode_stats
NameTypeDescription
episode_id required string Episode ID from list_episodes.
start_date date First day of the range. Default: 30 days ago.
end_date date Last day of the range. Default: today.
metric enum iab or unique downloads. Default: iab.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/libsyn/get_episode_stats \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"episode_id":"episode_8c41d2"}'
Python
result = client.call("libsyn", "get_episode_stats", {"episode_id":"episode_8c41d2"})
Node.js
const result = await cotlet.call('libsyn', 'get_episode_stats', {"episode_id":"episode_8c41d2"});
CLI
cotlet call libsyn get_episode_stats --data '{"episode_id":"episode_8c41d2"}'

Example response

{
  "episode_id": "38415502",
  "title": "Pricing your first product",
  "metric": "iab",
  "first_7_days": 1840,
  "first_30_days": 4215,
  "first_90_days": 6120,
  "all_time": 7310,
  "daily": [
    {
      "date": "2026-09-01",
      "downloads": 212
    },
    {
      "date": "2026-09-02",
      "downloads": 198
    },
    {
      "date": "2026-09-03",
      "downloads": 187
    }
  ],
  "range_total": 597
}

POST https://cotlet.dev/api/v1/libsyn/get_downloads_breakdown

Splits a show's downloads for a date range by country, listening app (user_agent) or publishing destination, biggest first. total_downloads is the sum of the rows. It needs the Advanced Plan or higher; on other plans the call fails with a plan error. Read only.

Parameters

Parameters: get_downloads_breakdown
NameTypeDescription
show_id string Show ID from list_shows. Default: your only or first show.
by required enum country, user_agent or destination.
start_date date First day of the range. Default: 30 days ago.
end_date date Last day of the range. Default: today.
metric enum iab or unique downloads. Default: iab.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/libsyn/get_downloads_breakdown \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"by":"country","start_date":"2026-09-01","end_date":"2026-09-30","metric":"iab"}'
Python
result = client.call("libsyn", "get_downloads_breakdown", {"by":"country","start_date":"2026-09-01","end_date":"2026-09-30","metric":"iab"})
Node.js
const result = await cotlet.call('libsyn', 'get_downloads_breakdown', {"by":"country","start_date":"2026-09-01","end_date":"2026-09-30","metric":"iab"});
CLI
cotlet call libsyn get_downloads_breakdown --data '{"by":"country","start_date":"2026-09-01","end_date":"2026-09-30","metric":"iab"}'

Example response

{
  "show_id": "412907",
  "by": "country",
  "metric": "iab",
  "rows": [
    {
      "key": "United States",
      "downloads": 9120
    },
    {
      "key": "Canada",
      "downloads": 2210
    },
    {
      "key": "United Kingdom",
      "downloads": 1875
    },
    {
      "key": "Germany",
      "downloads": 940
    },
    {
      "key": "Other",
      "downloads": 3855
    }
  ],
  "total_downloads": 18000
}

POST https://cotlet.dev/api/v1/libsyn/export_stats_report

Runs the Stats Export for a show and returns a link to a zip with one file per report you ask for. Formats are csv, xlsb and pdf. It needs the Advanced Plan or higher. It only reads, and the link is for you to fetch or pass to the next step of your automation.

Parameters

Parameters: export_stats_report
NameTypeDescription
show_id string Show ID from list_shows. Default: your only or first show.
reports required string[] Any of total_downloads, by_episodes, by_country, by_destination, by_user_agent.
range enum last_30_days, last_60_days, last_90_days or all_time. Default: last_30_days.
format enum csv, xlsb or pdf. Default: csv.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/libsyn/export_stats_report \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reports":["reports"]}'
Python
result = client.call("libsyn", "export_stats_report", {"reports":["reports"]})
Node.js
const result = await cotlet.call('libsyn', 'export_stats_report', {"reports":["reports"]});
CLI
cotlet call libsyn export_stats_report --data '{"reports":["reports"]}'

Example response

{
  "show_id": "412907",
  "status": "ready",
  "range": "last_30_days",
  "format": "csv",
  "files": [
    "total_downloads.csv",
    "by_country.csv"
  ],
  "download_url": "https://example.com/exports/412907-last_30_days.zip"
}

POST https://cotlet.dev/api/v1/libsyn/upload_media

Fetches a file from a public or signed URL and adds it to the media library of one show. It accepts mp3, m4a, mp4, m4v, ogg and pdf. The file name must be unique in your show. Nothing is published and nobody is notified, so it runs without asking; attach the returned media_file_id to a draft with create_episode.

Parameters

Parameters: upload_media
NameTypeDescription
show_id string Show ID from list_shows. Default: your only or first show.
file_url required string Public or signed URL of an mp3, m4a, mp4, m4v, ogg or pdf file.
file_name string Must be unique in your show. Default: name from the URL.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/libsyn/upload_media \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"file_url":"https://example.com"}'
Python
result = client.call("libsyn", "upload_media", {"file_url":"https://example.com"})
Node.js
const result = await cotlet.call('libsyn', 'upload_media', {"file_url":"https://example.com"});
CLI
cotlet call libsyn upload_media --data '{"file_url":"https://example.com"}'

Example response

{
  "media_file_id": "f_88120",
  "file_name": "ep-142.mp3",
  "status": "uploaded"
}

POST https://cotlet.dev/api/v1/libsyn/create_episode

Saves a new episode as a Draft with its title, show notes and details. Show notes are HTML or Markdown; Apple Podcasts shows up to 4,000 characters. Nothing reaches listeners until schedule_episode or publish_episode, so this call runs without asking and you can delete the draft in the dashboard.

Parameters

Parameters: create_episode
NameTypeDescription
show_id string Show ID from list_shows. Default: your only or first show.
title required string Episode title, shown in podcast apps.
description required string Show notes, HTML or Markdown. Apple Podcasts shows up to 4,000 characters.
media_file_id string File from upload_media.
season_number integer Season number.
episode_number integer Episode number.
episode_type enum full, trailer or bonus. Default: full.
rating enum clean or explicit. Default: show setting.
author string Episode author.
tags string[] Episode tags.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/libsyn/create_episode \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"title","description":"description"}'
Python
result = client.call("libsyn", "create_episode", {"title":"title","description":"description"})
Node.js
const result = await cotlet.call('libsyn', 'create_episode', {"title":"title","description":"description"});
CLI
cotlet call libsyn create_episode --data '{"title":"title","description":"description"}'

Example response

{
  "episode_id": "38415502",
  "status": "draft",
  "title": "Pricing your first product",
  "season_number": 3,
  "episode_number": 12,
  "media_file_id": "f_88120"
}

Update episode

write Confirm

POST https://cotlet.dev/api/v1/libsyn/update_episode

Changes the fields you pass and leaves the rest. On a published episode the RSS feed updates, so podcast apps show the change. Read the current text with get_episode first if you add to it. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: update_episode
NameTypeDescription
episode_id required string Episode ID from list_episodes.
fields required object Any of title, description, season_number, episode_number, episode_type, rating, author, tags.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/libsyn/update_episode \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"episode_id":"38201877","fields":{"description":"<p>How to land your first ten customers.</p><p>This episode is supported by Acme: <a href=\"https://acme.example/podcast\">acme.example/podcast</a></p>"}}'
Python
result = client.call("libsyn", "update_episode", {"episode_id":"38201877","fields":{"description":"<p>How to land your first ten customers.</p><p>This episode is supported by Acme: <a href=\"https://acme.example/podcast\">acme.example/podcast</a></p>"}})
Node.js
const result = await cotlet.call('libsyn', 'update_episode', {"episode_id":"38201877","fields":{"description":"<p>How to land your first ten customers.</p><p>This episode is supported by Acme: <a href=\"https://acme.example/podcast\">acme.example/podcast</a></p>"}});
CLI
cotlet call libsyn update_episode --data '{"episode_id":"38201877","fields":{"description":"<p>How to land your first ten customers.</p><p>This episode is supported by Acme: <a href=\"https://acme.example/podcast\">acme.example/podcast</a></p>"}}'

Example response

{
  "episode_id": "38415502",
  "status": "scheduled",
  "updated_fields": [
    "description"
  ]
}

Bulk edit episodes

write Confirm

POST https://cotlet.dev/api/v1/libsyn/bulk_edit_episodes

Sets the author and season on the episodes you list and can number them in the order you pass them: top_to_bottom numbers from the first ID, bottom_to_top from the last. The RSS feed refreshes after the change. It returns every episode with its new number. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: bulk_edit_episodes
NameTypeDescription
episode_ids required string[] Episodes to edit.
author string New author for all of them.
season_number integer New season for all of them.
numbering enum none, top_to_bottom or bottom_to_top. Default: none.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/libsyn/bulk_edit_episodes \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"episode_ids":["episode ids"]}'
Python
result = client.call("libsyn", "bulk_edit_episodes", {"episode_ids":["episode ids"]})
Node.js
const result = await cotlet.call('libsyn', 'bulk_edit_episodes', {"episode_ids":["episode ids"]});
CLI
cotlet call libsyn bulk_edit_episodes --data '{"episode_ids":["episode ids"]}'

Example response

{
  "updated": 3,
  "season_number": 3,
  "numbering": "bottom_to_top",
  "episodes": [
    {
      "episode_id": "38415502",
      "episode_number": 3
    },
    {
      "episode_id": "38201877",
      "episode_number": 2
    },
    {
      "episode_id": "38009640",
      "episode_number": 1
    }
  ]
}

Upload transcript

write Confirm

POST https://cotlet.dev/api/v1/libsyn/upload_transcript

Fetches an .srt or .vtt file from a URL and attaches it to one episode. Libsyn adds the transcript to the feed for Apple Podcasts and other apps, so it can reach listeners. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: upload_transcript
NameTypeDescription
episode_id required string Episode ID from list_episodes.
file_url required string URL of an .srt or .vtt file.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/libsyn/upload_transcript \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"episode_id":"38415502","file_url":"https://files.example.com/transcripts/ep-142.vtt"}'
Python
result = client.call("libsyn", "upload_transcript", {"episode_id":"38415502","file_url":"https://files.example.com/transcripts/ep-142.vtt"})
Node.js
const result = await cotlet.call('libsyn', 'upload_transcript', {"episode_id":"38415502","file_url":"https://files.example.com/transcripts/ep-142.vtt"});
CLI
cotlet call libsyn upload_transcript --data '{"episode_id":"38415502","file_url":"https://files.example.com/transcripts/ep-142.vtt"}'

Example response

{
  "episode_id": "38415502",
  "transcript": {
    "format": "vtt",
    "status": "attached"
  }
}

Schedule episode

write Confirm

POST https://cotlet.dev/api/v1/libsyn/schedule_episode

Sets a release time for a draft. At that time Libsyn publishes it to every destination the show is connected to, such as Apple Podcasts, Spotify and YouTube. release_at needs a time zone. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: schedule_episode
NameTypeDescription
episode_id required string Episode ID from list_episodes.
release_at required datetime ISO 8601 with time zone.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/libsyn/schedule_episode \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"episode_id":"38415502","release_at":"2026-10-15T06:00:00-04:00"}'
Python
result = client.call("libsyn", "schedule_episode", {"episode_id":"38415502","release_at":"2026-10-15T06:00:00-04:00"})
Node.js
const result = await cotlet.call('libsyn', 'schedule_episode', {"episode_id":"38415502","release_at":"2026-10-15T06:00:00-04:00"});
CLI
cotlet call libsyn schedule_episode --data '{"episode_id":"38415502","release_at":"2026-10-15T06:00:00-04:00"}'

Example response

{
  "episode_id": "38415502",
  "status": "scheduled",
  "title": "Pricing your first product",
  "release_at": "2026-10-15T06:00:00-04:00",
  "url": "https://yourname.libsyn.com/pricing-your-first-product"
}

Publish episode

write Confirm

POST https://cotlet.dev/api/v1/libsyn/publish_episode

Publishes a draft at once to every destination the show is connected to. Listeners can see it as soon as their apps refresh the feed. To release later, use schedule_episode. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: publish_episode
NameTypeDescription
episode_id required string Episode ID from list_episodes.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/libsyn/publish_episode \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"episode_id":"episode_8c41d2"}'
Python
result = client.call("libsyn", "publish_episode", {"episode_id":"episode_8c41d2"})
Node.js
const result = await cotlet.call('libsyn', 'publish_episode', {"episode_id":"episode_8c41d2"});
CLI
cotlet call libsyn publish_episode --data '{"episode_id":"episode_8c41d2"}'

Example response

{
  "episode_id": "38415502",
  "status": "published",
  "title": "Pricing your first product",
  "url": "https://yourname.libsyn.com/pricing-your-first-product"
}