Base URL: /api
All endpoints accept and return JSON. Set Content-Type: application/json on all requests with a body.
| Method | Endpoint | Description | Rate Limit |
|---|---|---|---|
| POST | /api/transcript |
Fetch transcript via YoutubeTranscript | 20/min |
| POST | /api/transcript/ytdlp |
Fetch transcript via yt-dlp | 20/min |
| POST | /api/discover |
Discover videos from playlist or channel | 10/min |
| POST | /api/channel |
Get channel info and top videos | 10/min |
| GET | /api/ai-summary/config |
Check configured LLM providers | None |
| POST | /api/ai-summary |
Generate AI summary from transcript | 10/min |
Fetch a video transcript using the YoutubeTranscript library. At least one of url or videoId must be provided.
interface TranscriptRequest {
url?: string;
videoId?: string;
}Status: 200 OK
interface TranscriptResponse {
success: true;
data: {
videoId: string;
segments: TranscriptSegment[];
segmentCount: number;
};
}
interface TranscriptSegment {
text: string;
offset: number;
duration: number;
lang?: string;
}| Status | Type | Description |
|---|---|---|
| 400 | INVALID_URL |
Missing or invalid url and videoId |
| 404 | NO_TRANSCRIPT |
Video exists but has no transcript available |
| 404 | VIDEO_NOT_FOUND |
No video found for the given URL or ID |
| 429 | RATE_LIMIT |
Rate limit exceeded (20 requests/min) |
| 503 | NETWORK_ERROR |
Upstream request to YouTube failed |
20 requests per minute per IP.
Fetch a video transcript using yt-dlp. More reliable than the YoutubeTranscript method and includes optional video metadata. At least one of url or videoId must be provided.
interface YtdlpTranscriptRequest {
url?: string;
videoId?: string;
options?: {
language?: string;
format?: string;
writeAutoSubs?: boolean;
};
}Status: 200 OK
interface YtdlpTranscriptResponse {
success: true;
data: {
videoId: string;
segments: TranscriptSegment[];
segmentCount: number;
title?: string;
channelTitle?: string;
publishedAt?: string;
thumbnail?: string;
duration?: number;
};
}| Status | Type | Description |
|---|---|---|
| 400 | INVALID_URL |
Missing or invalid url and videoId |
| 404 | NO_TRANSCRIPT |
Video exists but has no transcript available |
| 404 | VIDEO_NOT_FOUND |
No video found for the given URL or ID |
| 429 | RATE_LIMIT |
Rate limit exceeded (20 requests/min) |
| 503 | NETWORK_ERROR |
Upstream request to YouTube failed |
20 requests per minute per IP.
Discover videos from a YouTube playlist or channel URL.
interface DiscoverRequest {
url: string;
type?: "playlist" | "channel";
maxVideos?: number; // default: 100, max: 500
}Status: 200 OK
interface DiscoverResponse {
success: true;
data: {
id: string;
title: string;
url: string;
videoCount: number;
videos: VideoMetadata[];
};
}
interface VideoMetadata {
videoId: string;
title: string;
url: string;
thumbnail?: string;
duration?: number;
publishedAt?: string;
}| Status | Type | Description |
|---|---|---|
| 400 | INVALID_URL |
Missing or invalid URL |
| 429 | RATE_LIMIT |
Rate limit exceeded (10 requests/min) |
| 500 | PROCESSING_ERROR |
Internal error while discovering videos |
10 requests per minute per IP.
Get channel information and the top 10 videos from a video URL.
interface ChannelRequest {
videoUrl: string;
}Status: 200 OK
interface ChannelResponse {
success: true;
data: {
channel: ChannelDetails;
videos: VideoMetadata[];
};
}
interface ChannelDetails {
channelId: string;
title: string;
description?: string;
subscriberCount?: number;
videoCount?: number;
thumbnail?: string;
url: string;
}| Status | Type | Description |
|---|---|---|
| 400 | INVALID_URL |
Missing or invalid video URL |
| 429 | RATE_LIMIT |
Rate limit exceeded (10 requests/min) |
| 500 | PROCESSING_ERROR |
Internal error while fetching channel data |
10 requests per minute per IP.
Check which LLM providers have API keys configured on the server. No authentication or request body required.
None.
Status: 200 OK
interface AISummaryConfigResponse {
success: true;
providers: {
anthropic: boolean;
"google-gemini": boolean;
perplexity: boolean;
};
}| Status | Type | Description |
|---|---|---|
| 500 | UNKNOWN |
Unexpected server error |
None.
Generate an AI-powered summary of a transcript using one or all configured LLM providers.
interface AISummaryRequest {
transcript: string; // max 500,000 characters
provider: "anthropic" | "google-gemini" | "perplexity" | "all";
summaryStyle?: "bullets" | "narrative" | "technical";
videoUrl?: string;
}Status: 200 OK
interface AISummarySuccessResponse {
success: true;
summaries: AISummaryResponse[];
}
interface AISummaryResponse {
provider: string;
modelName: string;
summary: string;
success: boolean;
error?: string;
}When provider is "all", the summaries array contains one entry per configured provider. Individual entries may have success: false with an error message if that specific provider failed, while the overall response remains success: true.
| Status | Type | Description |
|---|---|---|
| 400 | INVALID_URL |
Missing or invalid request parameters |
| 400 | PROCESSING_ERROR |
Transcript exceeds 500,000 character limit |
| 429 | RATE_LIMIT |
Rate limit exceeded (10 requests/min) |
| 500 | PROCESSING_ERROR |
All providers failed to generate a summary |
10 requests per minute per IP.
All error responses include a type field from the following enum:
enum ErrorType {
INVALID_URL = "INVALID_URL",
VIDEO_NOT_FOUND = "VIDEO_NOT_FOUND",
NO_TRANSCRIPT = "NO_TRANSCRIPT",
PROCESSING_ERROR = "PROCESSING_ERROR",
NETWORK_ERROR = "NETWORK_ERROR",
RATE_LIMIT = "RATE_LIMIT",
UNKNOWN = "UNKNOWN",
}All error responses follow a consistent structure:
interface ErrorResponse {
error: string;
type: string;
suggestion?: string;
}Example:
{
"error": "No transcript available for this video",
"type": "NO_TRANSCRIPT",
"suggestion": "Try the yt-dlp endpoint with writeAutoSubs enabled, or check that the video has captions."
}Rate limits are enforced per IP address using an in-memory store.
| Endpoint Group | Limit | Window |
|---|---|---|
/api/transcript, /api/transcript/ytdlp |
20 requests | 1 minute |
/api/discover, /api/channel, /api/ai-summary |
10 requests | 1 minute |
/api/ai-summary/config |
No limit | -- |
When a rate limit is exceeded, the server responds with:
Status: 429 Too Many Requests
{
"error": "Rate limit exceeded. Please try again later.",
"type": "RATE_LIMIT"
}Rate limit windows are configurable on the server side. The in-memory store resets on server restart.
For a visual overview of how these API endpoints fit into the application architecture, see the interactive How It Works page available within the running application.