Search and Filter Podcasts
Search podcast titles and descriptions, apply filters, and retrieve multiple pages of results.
Before you start
- API token
- A topic you want to search for
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 Use Node.js 22 or newer. Save the complete script as recipe.mjs. No package installation is needed.
# macOS / Linux shell; set the same variable in your shell on Windows
export PODENGINE_API_KEY="YOUR_API_KEY"
node recipe.mjs Use Python 3.10 or newer. Save the complete script as recipe.py.
python -m pip install podengine
# macOS / Linux shell; set the same variable in your shell on Windows
export PODENGINE_API_KEY="YOUR_API_KEY"
python recipe.py Use Python 3.10 or newer. Save the complete script as recipe.py. No package installation is needed.
# macOS / Linux shell; set the same variable in your shell on Windows
export PODENGINE_API_KEY="YOUR_API_KEY"
python recipe.py 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',
}); const apiKey = process.env.PODENGINE_API_KEY;
if (!apiKey) throw new Error('Set PODENGINE_API_KEY before running this recipe');
const baseUrl = process.env.PODENGINE_API_URL || 'https://api.podengine.ai';
async function request(path, body) {
const response = await fetch(`${baseUrl}${path}`, {
method: body === undefined ? 'GET' : 'POST',
headers: { Authorization: apiKey, 'Content-Type': 'application/json' },
...(body === undefined ? {} : { body: JSON.stringify(body) }),
});
if (!response.ok) throw new Error(`Pod Engine request failed: ${response.status} ${response.statusText}`);
return (await response.json()).data;
} import os
from podengine import PodEngine
pe = PodEngine(
api_key=os.environ["PODENGINE_API_KEY"],
base_url=os.environ.get("PODENGINE_API_URL", "https://api.podengine.ai"),
) import json
import os
from urllib.parse import urlencode, quote
from urllib.request import Request, urlopen
api_key = os.environ["PODENGINE_API_KEY"]
base_url = os.environ.get("PODENGINE_API_URL", "https://api.podengine.ai")
def request(path, body=None):
req = Request(
base_url + path,
data=json.dumps(body).encode() if body is not None else None,
headers={"Authorization": api_key, "Content-Type": "application/json"},
method="POST" if body is not None else "GET",
)
# urlopen raises HTTPError for non-success responses.
with urlopen(req, timeout=30) as response:
return json.load(response)["data"] Complete runnable 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 searchTerms = [
{
searchTerm: 'technology',
searchType: 'text',
searchTargets: ['podcast-title', 'podcast-description'],
searchTermOptions: { matchMode: 'must' },
},
];
const query = { searchTerms, pageSize: 20 };
const firstPage = await pe.search.searchPodcasts(query);
console.log(`Found ${firstPage.result.hits.length} podcasts on this page`);
const filteredQuery = {
...query,
languages: ['en'],
includeItunesGenres: ['Technology'],
minTotalEpisodes: 10,
};
const filteredPage = await pe.search.searchPodcasts(filteredQuery);
console.log(filteredPage.result.hits);
const maxResults = 100;
const podcasts = [];
let cursor = null;
do {
const page = await pe.search.searchPodcasts({ ...filteredQuery, cursor });
podcasts.push(...page.result.hits.slice(0, maxResults - podcasts.length));
const nextCursor = page.cursor;
if (!page.result.hits.length || !nextCursor || nextCursor === cursor) break;
cursor = nextCursor;
} while (podcasts.length < maxResults);
console.log(`Retrieved ${podcasts.length} podcasts`); const apiKey = process.env.PODENGINE_API_KEY;
if (!apiKey) throw new Error('Set PODENGINE_API_KEY before running this recipe');
const baseUrl = process.env.PODENGINE_API_URL || 'https://api.podengine.ai';
async function request(path, body) {
const response = await fetch(`${baseUrl}${path}`, {
method: body === undefined ? 'GET' : 'POST',
headers: { Authorization: apiKey, 'Content-Type': 'application/json' },
...(body === undefined ? {} : { body: JSON.stringify(body) }),
});
if (!response.ok) throw new Error(`Pod Engine request failed: ${response.status} ${response.statusText}`);
return (await response.json()).data;
}
const searchTerms = [
{
searchTerm: 'technology',
searchType: 'text',
searchTargets: ['podcast-title', 'podcast-description'],
searchTermOptions: { matchMode: 'must' },
},
];
const query = { searchTerms, pageSize: 20 };
const firstPage = await request('/api/v1/search/podcasts', query);
console.log(`Found ${firstPage.result.hits.length} podcasts on this page`);
const filteredQuery = {
...query,
languages: ['en'],
includeItunesGenres: ['Technology'],
minTotalEpisodes: 10,
};
const filteredPage = await request('/api/v1/search/podcasts', filteredQuery);
console.log(filteredPage.result.hits);
const maxResults = 100;
const podcasts = [];
let cursor = null;
do {
const page = await request('/api/v1/search/podcasts', { ...filteredQuery, cursor });
podcasts.push(...page.result.hits.slice(0, maxResults - podcasts.length));
const nextCursor = page.cursor;
if (!page.result.hits.length || !nextCursor || nextCursor === cursor) break;
cursor = nextCursor;
} while (podcasts.length < maxResults);
console.log(`Retrieved ${podcasts.length} podcasts`); import os
from podengine import PodEngine
pe = PodEngine(
api_key=os.environ["PODENGINE_API_KEY"],
base_url=os.environ.get("PODENGINE_API_URL", "https://api.podengine.ai"),
)
search_terms = [
{
"searchTerm": "technology",
"searchType": "text",
"searchTargets": ["podcast-title", "podcast-description"],
"searchTermOptions": {"matchMode": "must"},
}
]
query = {"search_terms": search_terms, "page_size": 20}
first_page = pe.search.search_podcasts(**query)
print("Podcasts on this page:", len(first_page.result.hits))
filtered_query = {
**query,
"languages": ["en"],
"include_itunes_genres": ["Technology"],
"min_total_episodes": 10,
}
filtered_page = pe.search.search_podcasts(**filtered_query)
print(filtered_page.result.hits)
max_results = 100
podcasts = []
cursor = None
while len(podcasts) < max_results:
page = pe.search.search_podcasts(**{**filtered_query, "cursor": cursor})
hits = page.result.hits
podcasts.extend(hits[: max_results - len(podcasts)])
next_cursor = page.cursor
if not hits or not next_cursor or next_cursor == cursor:
break
cursor = next_cursor
print("Retrieved podcasts:", len(podcasts)) import json
import os
from urllib.parse import urlencode, quote
from urllib.request import Request, urlopen
api_key = os.environ["PODENGINE_API_KEY"]
base_url = os.environ.get("PODENGINE_API_URL", "https://api.podengine.ai")
def request(path, body=None):
req = Request(
base_url + path,
data=json.dumps(body).encode() if body is not None else None,
headers={"Authorization": api_key, "Content-Type": "application/json"},
method="POST" if body is not None else "GET",
)
# urlopen raises HTTPError for non-success responses.
with urlopen(req, timeout=30) as response:
return json.load(response)["data"]
search_terms = [
{
"searchTerm": "technology",
"searchType": "text",
"searchTargets": ["podcast-title", "podcast-description"],
"searchTermOptions": {"matchMode": "must"},
}
]
query = {"searchTerms": search_terms, "pageSize": 20}
first_page = request("/api/v1/search/podcasts", query)
print("Podcasts on this page:", len(first_page["result"]["hits"]))
filtered_query = {
**query,
"languages": ["en"],
"includeItunesGenres": ["Technology"],
"minTotalEpisodes": 10,
}
filtered_page = request("/api/v1/search/podcasts", filtered_query)
print(filtered_page["result"]["hits"])
max_results = 100
podcasts = []
cursor = None
while len(podcasts) < max_results:
page = request("/api/v1/search/podcasts", {**filtered_query, "cursor": cursor})
hits = page["result"]["hits"]
podcasts.extend(hits[: max_results - len(podcasts)])
next_cursor = page["cursor"]
if not hits or not next_cursor or next_cursor == cursor:
break
cursor = next_cursor
print("Retrieved podcasts:", len(podcasts)) Create a text search over podcast titles and descriptions. The must match mode requires the term to match. HTTP requests send a JSON body to POST /api/v1/search/podcasts. The SDK unwraps the HTTP response’s data envelope; the returned podcasts are in result.hits.
const searchTerms = [
{
searchTerm: 'technology',
searchType: 'text',
searchTargets: ['podcast-title', 'podcast-description'],
searchTermOptions: { matchMode: 'must' },
},
];
const query = { searchTerms, pageSize: 20 };
const firstPage = await pe.search.searchPodcasts(query);
console.log(`Found ${firstPage.result.hits.length} podcasts on this page`); const searchTerms = [
{
searchTerm: 'technology',
searchType: 'text',
searchTargets: ['podcast-title', 'podcast-description'],
searchTermOptions: { matchMode: 'must' },
},
];
const query = { searchTerms, pageSize: 20 };
const firstPage = await request('/api/v1/search/podcasts', query);
console.log(`Found ${firstPage.result.hits.length} podcasts on this page`); search_terms = [
{
"searchTerm": "technology",
"searchType": "text",
"searchTargets": ["podcast-title", "podcast-description"],
"searchTermOptions": {"matchMode": "must"},
}
]
query = {"search_terms": search_terms, "page_size": 20}
first_page = pe.search.search_podcasts(**query)
print("Podcasts on this page:", len(first_page.result.hits)) search_terms = [
{
"searchTerm": "technology",
"searchType": "text",
"searchTargets": ["podcast-title", "podcast-description"],
"searchTermOptions": {"matchMode": "must"},
}
]
query = {"searchTerms": search_terms, "pageSize": 20}
first_page = request("/api/v1/search/podcasts", query)
print("Podcasts on this page:", len(first_page["result"]["hits"])) Narrow the same search to English-language podcasts in the Technology genre with at least ten episodes. Filters combine with the search terms. Python SDK keyword arguments use snake_case, while HTTP JSON fields and nested search-term dictionaries use the API’s camelCase names.
const filteredQuery = {
...query,
languages: ['en'],
includeItunesGenres: ['Technology'],
minTotalEpisodes: 10,
};
const filteredPage = await pe.search.searchPodcasts(filteredQuery);
console.log(filteredPage.result.hits); const filteredQuery = {
...query,
languages: ['en'],
includeItunesGenres: ['Technology'],
minTotalEpisodes: 10,
};
const filteredPage = await request('/api/v1/search/podcasts', filteredQuery);
console.log(filteredPage.result.hits); filtered_query = {
**query,
"languages": ["en"],
"include_itunes_genres": ["Technology"],
"min_total_episodes": 10,
}
filtered_page = pe.search.search_podcasts(**filtered_query)
print(filtered_page.result.hits) filtered_query = {
**query,
"languages": ["en"],
"includeItunesGenres": ["Technology"],
"minTotalEpisodes": 10,
}
filtered_page = request("/api/v1/search/podcasts", filtered_query)
print(filtered_page["result"]["hits"]) - Filters can substantially reduce results. Start with the basic search, then add filters one at a time.
Keep the search and filters unchanged and pass each response’s cursor into the next request. Stop when there are no hits, no new cursor, or the local maximum is reached. This example starts a fresh traversal and collects at most 100 results.
const maxResults = 100;
const podcasts = [];
let cursor = null;
do {
const page = await pe.search.searchPodcasts({ ...filteredQuery, cursor });
podcasts.push(...page.result.hits.slice(0, maxResults - podcasts.length));
const nextCursor = page.cursor;
if (!page.result.hits.length || !nextCursor || nextCursor === cursor) break;
cursor = nextCursor;
} while (podcasts.length < maxResults);
console.log(`Retrieved ${podcasts.length} podcasts`); const maxResults = 100;
const podcasts = [];
let cursor = null;
do {
const page = await request('/api/v1/search/podcasts', { ...filteredQuery, cursor });
podcasts.push(...page.result.hits.slice(0, maxResults - podcasts.length));
const nextCursor = page.cursor;
if (!page.result.hits.length || !nextCursor || nextCursor === cursor) break;
cursor = nextCursor;
} while (podcasts.length < maxResults);
console.log(`Retrieved ${podcasts.length} podcasts`); max_results = 100
podcasts = []
cursor = None
while len(podcasts) < max_results:
page = pe.search.search_podcasts(**{**filtered_query, "cursor": cursor})
hits = page.result.hits
podcasts.extend(hits[: max_results - len(podcasts)])
next_cursor = page.cursor
if not hits or not next_cursor or next_cursor == cursor:
break
cursor = next_cursor
print("Retrieved podcasts:", len(podcasts)) max_results = 100
podcasts = []
cursor = None
while len(podcasts) < max_results:
page = request("/api/v1/search/podcasts", {**filtered_query, "cursor": cursor})
hits = page["result"]["hits"]
podcasts.extend(hits[: max_results - len(podcasts)])
next_cursor = page["cursor"]
if not hits or not next_cursor or next_cursor == cursor:
break
cursor = next_cursor
print("Retrieved podcasts:", len(podcasts)) - pageSize accepts 1–1000. These examples use 20 results per request.
- Results may change between pages as the index changes, so long-running exports may need deduplication by podcast ID.
- SDK requests retry transient failures automatically. The HTTP examples stop on errors; add bounded retries with backoff for production bulk jobs.