Query the entire library
Every published episode — metadata, TL;DR, digest, full transcript, bilingual editorial fields, Assets / Sector / Focus tags, participants, and provenance — plus the show registry, as JSON or portable files. The BidClub API and downloads require no account and no API key.
Fastest path: use the complete catalog endpoint to discover every slug, then fetch full episodes with modest parallelism. Use the versioned API for common reads, file routes for individual research artifacts, or the public read-only PostgREST endpoint for custom queries. JSON and download routes support cross-origin browser requests. Cache policies are listed below.
This page runs top to bottom from the smallest surface to the largest: the REST endpoints, then the discovery files a machine reads on its own, then agent installs, then feeds for human readers, and finally direct database access for queries the REST layer does not express.
Endpoints
GET /api/v1/shows
show registry: identity, language, hosts, tracking state, source URLs
GET /api/feed-index
complete compact catalog in one JSON response: count + every published episode
GET /api/feed-index/show/{show_id}
GET /api/feed-index/person/{person}
same shape, scoped to one show or exact URL-encoded participant name
GET /api/v1/episodes?show={show_id}&lang={EN|ZH}&limit={1..100}&offset={0..}
paginated newest episode metadata; default limit 50 and offset 0
pagination.next_offset is null on the final page
optional asset=...§or=...&focus=... filters
GET /api/v1/episodes/{slug}
one full episode: editorial markdown, transcript, alternates, provenance
GET /api/v1/search?q={query}&show=...&person=...&lang=...&len=...&time=...
deep full-library search; up to 20 newest matches
optional asset=...§or=...&focus=... filters
returned fallback matches carry partial: true; see search limits
GET /dl/{slug}/{summary|transcript|full}.{md|txt|pdf}
one episode artifact
GET /dl/show/{show_id}/{summary|transcript|full}.{md|txt|pdf}
one ZIP of the show's available published artifacts; see download limitsGrab everything
No account, cookie, or BidClub API key is required. The catalog endpoint returns every published episode, including untracked shows, as compact metadata in one request. Each slug resolves to the complete JSON record, including available editorial Markdown and transcript. Use /api/v1/episodes for pagination or tag filters; feed-index routes do not accept query filters.
# download the complete compact catalog (currently a few MB)
curl -o bidclub-catalog.json "https://bidclub.ai/api/feed-index"
# fetch every complete episode JSON with modest parallelism
jq -r '.episodes[].slug' bidclub-catalog.json |
xargs -P 6 -I {} sh -c 'curl -fsS "https://bidclub.ai/api/v1/episodes/{}" -o "{}.json"'
# browser / JavaScript: no auth header required
const catalog = await fetch("https://bidclub.ai/api/feed-index").then(r => r.json());Episode tags
Three independent groups: Assets describes the asset class, Sector the industry, and Focus the discussion's purpose. Each array contains zero to two stable IDs, primary first; keep both when two are returned. These are the same tags used in the feed, article sidebar, and grouped filters. Empty groups are intentional when nothing clearly applies. Other combines commodities, fixed income, FX, and other asset classes. Participant chips remain separate; there is no company or ticker bucket.
asset → tag_assets
equities Equities
crypto Crypto
vc_pe VC/PE
other Other
sector → tag_sectors
ai_software AI & Software
semis Semis
robotics Robotics
biotech Biotech
consumer Consumer
finance Finance
blockchain Blockchain
energy Energy
space_defense Space & Defense
focus → tag_focus
investing Investing
company_building Company Building
technical Technical
macro Macro
policy PolicyUse asset, sector, and focus on the episode list and search endpoints. Comma-separated IDs match any selected tag within a group; supplied groups must all match. For example, asset=equities,vc_pe§or=ai_software means (Equities OR VC/PE) AND AI & Software. Either a primary or secondary tag can match. Omit a group, or send all its IDs, to include everything in that group, including episodes with no tag. The literal none selects nothing and returns no episodes; it does not find untagged episodes. Unknown IDs return 400.
tagging_status is classified, pending, failed, or skipped; tagging_version identifies the taxonomy revision. classified with an empty array is a valid result, not a failed backfill. Tags are generated after the source TL;DR and digest. Changes to the source title, language, TL;DR, or digest invalidate the previous classification unless a replacement is published with the edit; Wireroom retries pending or outdated published episodes during refresh and backfill.
Search filters
Search uses the full-text index across titles, descriptions, TL;DRs, digests, and transcripts. If no indexed match is found, it falls back to substring matching on titles, descriptions, and transcripts, including Chinese. Substring and timeout fallbacks cover at most the 300 newest episodes matching all filters. Returned fallback matches, and all timeout fallbacks, carry partial: true and searched_recent: 300; that number is a window limit, not the actual number scanned. An empty response can omit these markers after a successful full-text query; it still does not rule out older substring matches.
q required · 2–100 characters
show exact show id, or __untracked__ for untracked shows (search only)
person exact participant name, without the "person:" prefix
lang EN | ZH · original episode language, not desired output language
len lt30 (<30) | 30to60 (30–59) | 60to120 (60–119) | gt120 (≥120)
time 24h | 7d | 30d (exact release time; date fallback where unavailable)
asset, sector, focus comma-separated tag IDs from the groups aboveExamples
# English and Chinese search
curl "https://bidclub.ai/api/v1/search?q=OpenAI&lang=EN"
curl --get "https://bidclub.ai/api/v1/search" --data-urlencode "q=泡沫"
# combine transcript search filters
curl --get "https://bidclub.ai/api/v1/search" \
--data-urlencode "q=AI" \
--data-urlencode "person=Jensen Huang" \
--data-urlencode "len=60to120" \
--data-urlencode "time=30d"
# five newest metadata records from one show
curl "https://bidclub.ai/api/v1/episodes?show=iltb&limit=5"
# match either asset class, plus the selected sector and focus
curl "https://bidclub.ai/api/v1/episodes?asset=equities,vc_pe§or=ai_software&focus=investing&limit=20"
curl "https://bidclub.ai/api/v1/search?q=AI§or=semis,ai_software&focus=technical"
# next page of metadata; follow pagination.next_offset until it is null
curl "https://bidclub.ai/api/v1/episodes?limit=100&offset=100"
# full episode as JSON, then a terminal-friendly transcript
curl "https://bidclub.ai/api/v1/episodes/ai-selloff-gavin-baker"
curl "https://bidclub.ai/dl/ai-selloff-gavin-baker/transcript.txt"Response fields
The collection returns episodes plus pagination {limit, offset, next_offset}; search returns q, count, results and optional partial / searched_recent markers; feed indexes return count and episodes. All episode surfaces include all three tag arrays and classification status/version. Collections sort by published_at descending (nulls last), then date descending and slug ascending. title is the full canonical publisher title; display_title is a nullable feed-only compression. Nullable source and translation fields return null when unavailable.
SHOW
id, name, lang, hosts, tracked, position, sources
TAGS · on every episode list, detail, search result, and feed-index row
tag_assets, tag_sectors, tag_focus, tagging_status, tagging_version
EPISODE LIST ITEM
slug, show_id, title, display_title, title_orig, dek, lang, date, published_at, duration_min,
source_url, source_label, rss_url, youtube_id, youtube_url,
chips, provenance, tags above, shows { name }
EPISODE DETAIL
list fields + thumbnail_url, title_alt, display_title_alt, dek_alt, lang_alt,
tldr_md, digest_md, transcript_md, tldr_md_alt, digest_md_alt,
shows { name, hosts }
SEARCH RESULT
list fields minus provenance + title_alt, display_title_alt, dek_alt, thumbnail_url
FEED INDEX ROW
search fields minus rss_url, youtube_url, shows
+ show (flattened show name)lang describes the source-language editorial fields. lang_alt names the alternate-language title, dek, TL;DR, and digest on the full record. The catalog's lang filter selects source episodes; it does not select translated output. transcript_md always preserves the source language and has no alternate. chips contains only person:<name> entries. A slug remains stable when a title is corrected.
Download behavior
summary contains the TL;DR and digest; transcript contains the full transcript; full combines available sections. Markdown preserves structure, text removes most Markdown syntax, and PDF is typeset and cached on first request. A show route streams a ZIP with a README recording unavailable artifacts. ZIP assembly uses one database listing, subject to its row cap; use paginated JSON for complete exports of large shows. Large PDF archives can take minutes.
Both download routes accept ?lang=EN|ZH|orig (default orig). A translated summary requires both the translated TL;DR and digest; otherwise it falls back to the source edition. Transcripts always remain in their original language, including within full exports. Single-episode responses report X-BidClub-Language and X-BidClub-Language-Fallback; ZIPs record fallbacks in README.txt. Transcript-only Markdown is served inline and cached, including full.md when no summary exists; other downloads are attachments with private, no-store. Use the JSON API for automated bulk reads.
Caching and updates
Configured Cache-Control policies (seconds)
Shows and episode lists public, s-maxage=300, stale-while-revalidate=600
Episode detail public, s-maxage=60
Search public, s-maxage=60
All feed indexes public, max-age=60, s-maxage=300, stale-while-revalidate=600
RSS feeds public, max-age=60, s-maxage=300, stale-while-revalidate=600
Transcript-only Markdown public, s-maxage=300, stale-while-revalidate=86400
Other downloads private, no-storePublished episodes can be corrected, translated, or reclassified. Publication hooks refresh site/data caches, but different routes may briefly reflect different revisions. Shared-cache directives can be consumed by the CDN rather than repeated in the browser's response headers. These policies are not a guaranteed end-to-end update deadline; avoid tight polling. JSON routes do not promise ETag or conditional 304 responses.
Limits and errors
The versioned episode collection accepts at most 100 rows per request; follow pagination.next_offset until it is null. The one-response catalog is deliberately much larger and is intended for occasional discovery or mirroring, not tight polling. JSON API errors use an error field: 400 for an invalid query, 404 for a missing episode, and 502 when the upstream is unavailable. Download routes use the corresponding HTTP status with a short text response.
Machine discovery
Agents and code generators can read /openapi.json for the public REST and download routes. For direct PostgREST queries, use the public schema below; database-root schema discovery is not available with the public key. Every /api/* response carries X-Robots-Tag: noindex — that governs search indexing of the JSON, not access to it.
GET /openapi.json
OpenAPI 3.1 schema: every endpoint, parameter, and response shape
GET /llms.txt
short discovery file for language models (llmstxt.org convention)
GET /llms-full.txt
the same, with the complete API reference and live show registry inlinedAgents
An agent can reach the library three ways, in descending order of preference. The MCP server is the richest: it speaks Model Context Protocol over Streamable HTTP at /api/mcp, needs no key, and exposes six tools. It is rate limited to 30 requests per minute per IP because these calls bypass the CDN; for bulk reads use the cached REST endpoints above.
POST https://bidclub.ai/api/mcp
bidclub_list_shows the show registry
bidclub_list_episodes episode metadata, newest first
bidclub_search_episodes full-text and Chinese substring search
bidclub_get_episode one section, paged deterministically for long text
bidclub_feed_index compact index; may truncate large results
bidclub_download_links the /dl URL matrix, built without fetching
# Claude Code
claude mcp add --transport http bidclub https://bidclub.ai/api/mcp
# any client that speaks Streamable HTTP
{ "mcpServers": { "bidclub": { "url": "https://bidclub.ai/api/mcp" } } }
# stdio-only clients bridge through mcp-remote
npx -y mcp-remote https://bidclub.ai/api/mcpMCP list, search, and feed-index tools accept the same comma-separated asset / sector / focus filters. source_lang filters original episode language; lang on bidclub_get_episode selects editorial output language. MCP feed-index has a response-size limit: if truncated is true, count is the full matching total but episodes contains only a subset. Use the REST feed index for a complete catalog.
Long sections page rather than truncate: when a result carries truncated: true, call bidclub_get_episode again with offset set to next_offset and concatenate content_md until next_offset is null. The window defaults to 20,000 characters for Latin text and 14,000 for Chinese, where one character costs roughly one token.
For an agent with no MCP support, the Claude Code skill teaches the same workflow over plain HTTP. It is one file and installs with one command. Shells and scripts can use the curl examples above.
mkdir -p ~/.claude/skills/bidclub \
&& curl -fsSL https://bidclub.ai/skill/SKILL.md \
-o ~/.claude/skills/bidclub/SKILL.mdFeeds
RSS 2.0 feeds, no account and no API key. Each feed is published as two separate language editions rather than one mixed feed, and the site's language toggle decides which edition a link on the site points at. Global feeds cover tracked shows and carry the 30 newest episodes; a per-show feed exists for every show with a page — tracked or not — and carries 20. Per-show feed links live on the coverage page, one per row; the OPML files list tracked shows only.
GET /feeds/global.xml · /feeds/global.zh.xml
all tracked shows, 30 newest episodes
GET /feeds/{show_id}.xml · /feeds/{show_id}.zh.xml
one show, 20 newest episodes
linked per row on /coverage
GET /opml · /opml.zh
OPML 2.0 download: per-show feeds in ONE language, no global feed
importing both languages would subscribe you to every show twiceEntry bodies are the dek plus the TL;DR and links back — the full digest, transcript, and downloads stay on the episode page. Item guids are stable, slug-based, and language-qualified: tag:bidclub.ai,2026:e:{slug}:en or :zh. They are tag: URIs rather than URLs, hence isPermaLink="false". A republished episode keeps its guid, so corrections refresh in place instead of arriving as a new unread item, and subscribing to both editions of one show never collapses them. Feeds carry Last-Modified and answer If-Modified-Since with 304. Their cache policy is listed above; reader refresh intervals also affect when an update appears. These are article feeds: no audio enclosures and no podcast namespace tags.
Direct database access
For explicit field selection, filtering, ordering, joins, and pagination, query the public database directly with the PostgREST query language. The key below is intentionally public; row-level security permits reads of published content and denies writes. PostgREST commonly caps one response at 1,000 rows, so direct-database clients must page with limit and offset (or Range headers) and should select only the fields they need.
SB="https://fexwajqmulkvqfsdxodo.supabase.co/rest/v1"
KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImZleHdhanFtdWxrdnFmc2R4b2RvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3ODU5OTk5MDIsImV4cCI6MjEwMTU3NTkwMn0.vqZwHNNcpcFmPQ-Ca1Xlps9kjvsB0QMZOtdL4Nu4Rj4"
# page through compact rows; never assume an unbounded response
curl "$SB/episodes?select=slug,show_id,title,date,published_at&order=published_at.desc.nullslast,date.desc,slug.asc&limit=100&offset=0" \
-H "apikey: $KEY"
# indexed full-text search across titles, summaries, and transcripts
curl "$SB/episodes?select=slug,title,date,published_at&fts=wfts(simple).NVIDIA&limit=50" \
-H "apikey: $KEY"
# join each episode to its show
curl "$SB/episodes?select=slug,title,published_at,shows(name)&order=published_at.desc.nullslast,date.desc&limit=100" \
-H "apikey: $KEY"
# any selected asset, AND the chosen sector (array overlap)
curl --get "$SB/episodes" -H "apikey: $KEY" \
--data-urlencode "select=slug,title,tag_assets,tag_sectors,tag_focus,tagging_status" \
--data-urlencode "tag_assets=ov.{equities,vc_pe}" \
--data-urlencode "tag_sectors=ov.{ai_software}" \
--data-urlencode "order=published_at.desc.nullslast,date.desc,slug.asc" \
--data-urlencode "limit=100"Public schema
shows
id, name, lang, hosts, tracked, position, sources (jsonb), updated_at
episodes · identity and discovery
slug, show_id → shows.id, title, display_title, title_orig, title_alt,
display_title_alt, dek, dek_alt,
lang, lang_alt, date, published_at, duration_min, thumbnail_url
episodes · source and provenance
source_url, source_label, rss_url, youtube_id, youtube_url,
chips (jsonb: ["person:<name>"]), provenance (jsonb: [[stage, tool], ...])
episodes · editorial content
tldr_md, digest_md, transcript_md, tldr_md_alt, digest_md_alt
episodes · taxonomy
tag_assets, tag_sectors, tag_focus (text[], 0–2 unique IDs each, primary first)
tagging_status (pending | classified | failed | skipped)
tagging_version (text, nullable)
tagging_input_sha256 (text, nullable; direct database only)
tagging_source_revision (bigint, ≥0; direct database only)
episodes · system
status, updated_at, ftspublished_at is the original episode release timestamp, not the time it was added to BidClub. updated_at is not a reliable cursor for every correction. tagging_source_revision only tracks source title / language / TL;DR / digest changes; tagging_input_sha256 binds a classification to its input and classifier settings. Neither field is a general change feed. Direct database arrays use PostgreSQL operators: ov matches any listed value; eq.{} finds an empty array. Unlike the REST filter shortcut, listing every ID in an ov filter still excludes empty arrays.
A link back to the BidClub episode and its original source is appreciated.