{"openapi":"3.1.0","info":{"title":"PodHood creator API","version":"1.0.0","description":"Read-only programmatic access to your own channels' content graph: hybrid search over episodes and transcripts (drafts included), paginated catalog browsing, and per-episode resources. Every REST endpoint requires a credential: an API key, created in Studio → Settings → API keys (paid plans) and sent in the \"x-api-key\" header (or as a Bearer token), or an OAuth access token from the creator MCP sign-in, sent as a Bearer token. Either reads the channels its owner is a member of — every non-archived episode status included; anything else 404s. Anonymous reading of published content lives on each channel's MCP server, not on REST. Anonymous reading of published content lives on each channel's MCP server at https://<channel>.podhood.com/mcp.\n\nVersioning: /api/v1 is the documented, stable surface. A breaking change ships as /api/v2, with Deprecation and Sunset headers served on the retiring version before removal.\n\nSafety: Every documented endpoint is a read-only GET — idempotent, safe to retry, and with nothing to sandbox because no documented call can mutate state. Errors are always JSON, never HTML.","contact":{"name":"PodHood","email":"hello@podhood.com","url":"https://podhood.com/developers"}},"security":[{"apiKey":[]},{"bearer":[]}],"servers":[{"url":"https://podhood.com","description":"Apex host. The same /api paths also work on any channel host (<channel>.podhood.com or the show's custom domain)."}],"paths":{"/api/v1/channels":{"get":{"operationId":"listMyChannels","summary":"List the channels your key can read","description":"Every channel the key's owner is a member of — the discovery call that turns a bare key into the slugs the rest of the API is addressed by. apiAccess=false marks a channel whose owner's plan does not include the API: its data endpoints would answer 403.","responses":{"200":{"description":"The caller's channels.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelList"}}}},"401":{"description":"Missing or invalid credential (API key or OAuth access token).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"This API key's rate limit is exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/channels/{slug}/search":{"get":{"operationId":"searchChannelEpisodes","summary":"Search your channel's episodes","description":"Hybrid (semantic + keyword) search over your channel's episodes and transcripts — every non-archived status, drafts included. Returns ranked episodes with best-matching, timestamped segments; each segment carries a deep link to the exact moment. An empty result is 200 with an empty episodes array, never a 404.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"},"description":"The channel's slug (its subdomain on podhood.com)."},{"name":"query","in":"query","schema":{"type":"string"},"description":"The natural-language search query (alias: q). Missing or empty yields an empty result set, not an error."},{"name":"fast","in":"query","schema":{"type":"boolean"},"description":"true skips the semantic leg for a cheaper keyword-only search."},{"name":"topicIds","in":"query","schema":{"type":"string","description":"Comma-separated ids."},"description":"Filter to these topic ids."},{"name":"personIds","in":"query","schema":{"type":"string","description":"Comma-separated ids."},"description":"Filter to episodes featuring these person entity ids (hosts and guests)."},{"name":"entityIds","in":"query","schema":{"type":"string","description":"Comma-separated ids."},"description":"Filter to episodes mentioning these company/product entity ids."},{"name":"episodeIds","in":"query","schema":{"type":"string","description":"Comma-separated ids."},"description":"Scope to these episode ids (e.g. to search within a prior result set)."},{"name":"collectionId","in":"query","schema":{"type":"string"},"description":"Filter to one collection (mirrored playlist)."},{"name":"year","in":"query","schema":{"type":"integer"},"description":"Filter to a publication year."}],"responses":{"200":{"description":"Ranked episodes with matched segments.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResult"}}}},"401":{"description":"Missing or invalid credential (API key or OAuth access token).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The channel owner's plan does not include API access (Free plan).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found — an unknown id and a channel the key's owner is not a member of are indistinguishable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"This API key's rate limit is exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/channels/{slug}/episodes":{"get":{"operationId":"listChannelEpisodes","summary":"Browse your channel's episodes","description":"Keyset-paginated list of your channel's episodes — every non-archived status. Episode rows carry mediaUrl (the YouTube watch URL or RSS enclosure), so segment timestamps convert into source deep links. Pass the response's nextCursor back as cursor for the next page; the same facet filters as search apply.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"},"description":"The channel's slug."},{"name":"status","in":"query","schema":{"type":"string","enum":["all","queued","indexing","live","failed"],"default":"all"},"description":"Narrow to one processing-status view; all (or absent) = no narrowing."},{"name":"sort","in":"query","schema":{"type":"string","enum":["newest","popular","longest","oldest"],"default":"newest"}},{"name":"cursor","in":"query","schema":{"type":"string"},"description":"Opaque pagination cursor from a prior response's nextCursor."},{"name":"topicIds","in":"query","schema":{"type":"string","description":"Comma-separated ids."},"description":"Filter to these topic ids."},{"name":"personIds","in":"query","schema":{"type":"string","description":"Comma-separated ids."},"description":"Filter to episodes featuring these person entity ids (hosts and guests)."},{"name":"entityIds","in":"query","schema":{"type":"string","description":"Comma-separated ids."},"description":"Filter to episodes mentioning these company/product entity ids."},{"name":"episodeIds","in":"query","schema":{"type":"string","description":"Comma-separated ids."},"description":"Scope to these episode ids (e.g. to search within a prior result set)."},{"name":"collectionId","in":"query","schema":{"type":"string"},"description":"Filter to one collection (mirrored playlist)."},{"name":"year","in":"query","schema":{"type":"integer"},"description":"Filter to a publication year."}],"responses":{"200":{"description":"One page of episodes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EpisodePage"}}}},"401":{"description":"Missing or invalid credential (API key or OAuth access token).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The channel owner's plan does not include API access (Free plan).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found — an unknown id and a channel the key's owner is not a member of are indistinguishable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"This API key's rate limit is exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/episodes/{episodeId}/transcript":{"get":{"operationId":"getEpisodeTranscript","summary":"Get an episode's word-level transcript","description":"The episode's transcript document: speaker-attributed segments, each word aligned to the audio — the raw material for quotes, clips, and annotated transcripts.","parameters":[{"name":"episodeId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The transcript document.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscriptDocument"}}}},"401":{"description":"Missing or invalid credential (API key or OAuth access token).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The channel owner's plan does not include API access (Free plan).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found — an unknown id and a channel the key's owner is not a member of are indistinguishable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"This API key's rate limit is exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The stored transcript blob could not be read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/episodes/{episodeId}/chapters":{"get":{"operationId":"getEpisodeChapters","summary":"Get an episode's chapters and key moments","parameters":[{"name":"episodeId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Chapters with their key moments.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Chapter"}}}}},"401":{"description":"Missing or invalid credential (API key or OAuth access token).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The channel owner's plan does not include API access (Free plan).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found — an unknown id and a channel the key's owner is not a member of are indistinguishable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"This API key's rate limit is exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/episodes/{episodeId}/related":{"get":{"operationId":"getRelatedEpisodes","summary":"Get episodes related to this one","description":"Other published episodes of the same channel that share the most topics.","parameters":[{"name":"episodeId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Related episodes.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object"}}}}},"401":{"description":"Missing or invalid credential (API key or OAuth access token).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The channel owner's plan does not include API access (Free plan).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found — an unknown id and a channel the key's owner is not a member of are indistinguishable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"This API key's rate limit is exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"apiKey":{"type":"apiKey","in":"header","name":"x-api-key","description":"A user-owned key created in Studio → Settings → API keys (paid plans). It reads the channels its owner is a member of."},"bearer":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Either credential as a Bearer: a `podhood_`-prefixed API key, or an OAuth access token (`creator:read`) minted by the creator MCP sign-in — 401s carry the WWW-Authenticate challenge that points at the OAuth discovery metadata."}},"schemas":{"Error":{"type":"object","description":"The one error shape every endpoint returns: JSON with a human-readable message, never an HTML page.","properties":{"error":{"type":"string"}},"required":["error"]},"MatchedSegment":{"type":"object","description":"One matched utterance — the unit of citation.","properties":{"segmentId":{"type":"string"},"text":{"type":"string"},"startMs":{"type":"integer"},"endMs":{"type":"integer"},"speaker":{"type":["string","null"]},"url":{"type":"string","description":"Absolute citation URL to the moment on the episode page (?t= is milliseconds)."},"deepLink":{"type":"string","description":"The same moment as a path relative to the channel's Library root."},"matchedVia":{"type":"array","items":{"type":"string"}}},"additionalProperties":true},"SearchResultEpisode":{"type":"object","properties":{"episodeId":{"type":"string"},"title":{"type":"string"},"url":{"type":"string","description":"Absolute URL of the episode page."},"score":{"type":"number"},"matchedVia":{"type":"array","items":{"type":"string"}},"segments":{"type":"array","items":{"$ref":"#/components/schemas/MatchedSegment"}},"summary":{"type":["string","null"]},"publishedAt":{"type":"string"},"durationMs":{"type":["integer","null"]}},"additionalProperties":true},"SearchResult":{"type":"object","properties":{"query":{"type":"string"},"episodes":{"type":"array","items":{"$ref":"#/components/schemas/SearchResultEpisode"}}},"required":["episodes"],"additionalProperties":true},"ChannelList":{"type":"object","properties":{"channels":{"type":"array","items":{"$ref":"#/components/schemas/ChannelSummary"}}},"required":["channels"],"additionalProperties":true},"ChannelSummary":{"type":"object","description":"One channel the key's owner is a member of.","properties":{"id":{"type":"string"},"slug":{"type":"string","description":"The {slug} every channel-scoped endpoint is addressed by."},"title":{"type":"string"},"providerType":{"type":"string","enum":["youtube","rss"]},"apiAccess":{"type":"boolean","description":"Whether this channel's data endpoints will serve the key (the channel owner's plan includes API access). false ⇒ they answer 403."}},"required":["id","slug","title","providerType","apiAccess"],"additionalProperties":true},"EpisodePage":{"type":"object","properties":{"episodes":{"type":"array","items":{"$ref":"#/components/schemas/Episode"}},"nextCursor":{"type":["string","null"],"description":"Pass back as cursor for the next page; null on the last page."}},"required":["episodes"],"additionalProperties":true},"Episode":{"type":"object","description":"One catalog row, in every non-archived status.","properties":{"id":{"type":"string"},"title":{"type":"string"},"description":{"type":["string","null"]},"link":{"type":["string","null"],"description":"The show-notes page on the source platform (display-only)."},"thumbnail":{"type":["string","null"]},"mediaUrl":{"type":["string","null"],"description":"The source address — the YouTube watch URL or the RSS enclosure. On a watch URL, append &t=<seconds>s to deep-link a transcript timestamp to the moment in the video."},"durationMs":{"type":["integer","null"]},"publishedAt":{"type":"string"},"viewCount":{"type":["integer","null"]},"status":{"type":"string","enum":["pending","processing","indexed","failed","published"]},"summary":{"type":["string","null"]},"error":{"type":["string","null"]}},"required":["id","title","publishedAt","status"],"additionalProperties":true},"TranscriptDocument":{"type":"object","description":"The episode's transcript (format v1): speaker-attributed segments, each word aligned to the audio — the raw material for quotes, clips, and annotated transcripts.","properties":{"formatVersion":{"type":"integer"},"segments":{"type":"array","items":{"$ref":"#/components/schemas/TranscriptSegment"}}},"required":["formatVersion","segments"],"additionalProperties":true},"TranscriptSegment":{"type":"object","description":"One utterance — the unit of retrieval, citation, and quoting.","properties":{"speakerLabel":{"type":["string","null"],"description":"The speaking person's name once attributed; null when unknown."},"startMs":{"type":"integer"},"endMs":{"type":"integer"},"text":{"type":"string"},"words":{"type":"array","description":"Per-word timing: t = the word, s/e = start/end in milliseconds.","items":{"type":"object","properties":{"t":{"type":"string"},"s":{"type":"integer"},"e":{"type":"integer"}},"required":["t","s","e"]}}},"required":["startMs","endMs","text"],"additionalProperties":true},"Chapter":{"type":"object","description":"A titled time range with its typed key moments filed inside.","properties":{"title":{"type":"string"},"startMs":{"type":"integer"},"endMs":{"type":"integer"},"moments":{"type":"array","items":{"$ref":"#/components/schemas/KeyMoment"}}},"required":["title","startMs","endMs","moments"],"additionalProperties":true},"KeyMoment":{"type":"object","description":"A typed highlight; its title is written as a self-contained, citable claim — a ready-made pull quote.","properties":{"type":{"type":"string","description":"The highlight's kind (story, qa, prediction, insight, debate, quote, …)."},"title":{"type":"string"},"startMs":{"type":"integer"},"endMs":{"type":"integer"}},"required":["type","title","startMs"],"additionalProperties":true}}}}