API recipe

Get Episode Transcripts via iTunes API

Find recent episodes with iTunes, match them to Pod Engine, and retrieve available transcript text.

intermediate 15 minutes PodEngine Team
EpisodesTranscriptsiTunes APIIntegration

Before you start

  • API token
  • An Apple Podcasts collection ID

Choose your example

Your selection applies to every step. HTTP uses built-in clients without the Pod Engine libraries.

Use Node.js 22 or newer. Save the complete script as recipe.mjs.

npm install @podengine/sdk
# macOS / Linux shell; set the same variable in your shell on Windows
export PODENGINE_API_KEY="YOUR_API_KEY"
node recipe.mjs

Set up the client

Start your file with this setup, then append each step in order. The complete script below includes everything.

import { PodEngine } from '@podengine/sdk';

const apiKey = process.env.PODENGINE_API_KEY;
if (!apiKey) throw new Error('Set PODENGINE_API_KEY before running this recipe');
const pe = new PodEngine({
  apiKey,
  baseUrl: process.env.PODENGINE_API_URL || 'https://api.podengine.ai',
});
Complete runnable script
Download script
import { PodEngine } from '@podengine/sdk';

const apiKey = process.env.PODENGINE_API_KEY;
if (!apiKey) throw new Error('Set PODENGINE_API_KEY before running this recipe');
const pe = new PodEngine({
  apiKey,
  baseUrl: process.env.PODENGINE_API_URL || 'https://api.podengine.ai',
});

const appleId = 1200361736;
const response = await fetch(
  `https://itunes.apple.com/lookup?id=${appleId}&media=podcast&entity=podcastEpisode&limit=10`
);
if (!response.ok) throw new Error(`iTunes request failed: ${response.status}`);
const itunesData = await response.json();
const itunesEpisodes = itunesData.results.filter((item) => item.wrapperType === 'podcastEpisode');
console.log(`Found ${itunesEpisodes.length} iTunes episodes`);

const { podcast } = await pe.podcasts.getPodcastIdLookup({ appleId });
if (!podcast) throw new Error('This Apple podcast has not been matched to Pod Engine');
console.log(podcast.title, podcast.id);

const { podcastWithEpisodes } = await pe.podcasts.getPodcastEpisodes({ podcastIdOrSlug: podcast.id, limit: 100 });
// Only accept a unique exact audio URL or title match; skip ambiguous matches.
const matchedEpisodes = [];
for (const itunesEpisode of itunesEpisodes) {
  let candidates = podcastWithEpisodes.episodes.filter((ep) => ep.enclosureUrl === itunesEpisode.episodeUrl);
  if (!candidates.length) {
    candidates = podcastWithEpisodes.episodes.filter(
      (ep) => ep.title.trim().toLowerCase() === itunesEpisode.trackName.trim().toLowerCase()
    );
  }
  if (candidates.length === 1) matchedEpisodes.push(candidates[0]);
  else console.info(`No unique match: ${itunesEpisode.trackName}`);
}

// Set to true only when you want to request new transcriptions.
const requestMissingTranscripts = false;
const seen = new Set();
for (const episode of matchedEpisodes) {
  if (seen.has(episode.episodeId)) continue;
  seen.add(episode.episodeId);
  if (!episode.hasTranscript) {
    console.info(`No transcript available: ${episode.title}`);
    if (requestMissingTranscripts) {
      const result = await pe.transcriptions.requestEpisodeTranscription({ episodeId: episode.episodeId });
      console.log('Transcription request:', result);
    }
    continue;
  }
  const { episodeTranscriptText } = await pe.episodes.getEpisodeTranscriptText({ episodeId: episode.episodeId });
  console.log(episode.title, episodeTranscriptText.text?.slice(0, 500) ?? 'No transcript text available');
}
1

Fetch Recent Episodes from iTunes

Use the Apple collection ID to request recent podcast episodes. Filter by wrapperType rather than assuming the first item is always a podcast. This external request uses a built-in HTTP client in both SDK and HTTP variants. See the Apple iTunes Search API documentation.

const appleId = 1200361736;
const response = await fetch(
  `https://itunes.apple.com/lookup?id=${appleId}&media=podcast&entity=podcastEpisode&limit=10`
);
if (!response.ok) throw new Error(`iTunes request failed: ${response.status}`);
const itunesData = await response.json();
const itunesEpisodes = itunesData.results.filter((item) => item.wrapperType === 'podcastEpisode');
console.log(`Found ${itunesEpisodes.length} iTunes episodes`);
2

Lookup Pod Engine Podcast ID

Resolve the Apple ID to a Pod Engine podcast. A lookup can return a null podcast, so stop with a clear message if the podcast has not been matched. Python SDK examples convert response models to dictionaries using API field names so the processing steps match the HTTP examples.

const { podcast } = await pe.podcasts.getPodcastIdLookup({ appleId });
if (!podcast) throw new Error('This Apple podcast has not been matched to Pod Engine');
console.log(podcast.title, podcast.id);
3

Find Matching Episodes

Compare the recent iTunes episodes with up to 100 Pod Engine episodes. Prefer an exact audio URL match, then fall back to an exact title match after trimming whitespace and ignoring case. Skip missing or ambiguous matches.

const { podcastWithEpisodes } = await pe.podcasts.getPodcastEpisodes({ podcastIdOrSlug: podcast.id, limit: 100 });
// Only accept a unique exact audio URL or title match; skip ambiguous matches.
const matchedEpisodes = [];
for (const itunesEpisode of itunesEpisodes) {
  let candidates = podcastWithEpisodes.episodes.filter((ep) => ep.enclosureUrl === itunesEpisode.episodeUrl);
  if (!candidates.length) {
    candidates = podcastWithEpisodes.episodes.filter(
      (ep) => ep.title.trim().toLowerCase() === itunesEpisode.trackName.trim().toLowerCase()
    );
  }
  if (candidates.length === 1) matchedEpisodes.push(candidates[0]);
  else console.info(`No unique match: ${itunesEpisode.trackName}`);
}
4

Get Episode Transcripts

Retrieve transcript text for uniquely matched episodes marked as having a transcript. Print the first 500 characters and skip duplicate episode IDs. The text field can be null. Missing transcripts are reported and skipped by default. To request them, enable the boolean switch at the start of this step. This sends a separate transcription request; it does not wait for the transcript to become available.

// Set to true only when you want to request new transcriptions.
const requestMissingTranscripts = false;
const seen = new Set();
for (const episode of matchedEpisodes) {
  if (seen.has(episode.episodeId)) continue;
  seen.add(episode.episodeId);
  if (!episode.hasTranscript) {
    console.info(`No transcript available: ${episode.title}`);
    if (requestMissingTranscripts) {
      const result = await pe.transcriptions.requestEpisodeTranscription({ episodeId: episode.episodeId });
      console.log('Transcription request:', result);
    }
    continue;
  }
  const { episodeTranscriptText } = await pe.episodes.getEpisodeTranscriptText({ episodeId: episode.episodeId });
  console.log(episode.title, episodeTranscriptText.text?.slice(0, 500) ?? 'No transcript text available');
}