API Documentation

Archivarix Tube Search provides a REST API for programmatic access to archived YouTube video metadata, subtitles, and AI summaries. This page is the complete API reference.

API access requires the Pro subscription tier. See Pricing for details.

Authentication

API keys are created in your Profile → API tab. Include the key in every request using one of these headers:

X-API-Key: tsk_YOUR_KEY_HERE

or

Authorization: Bearer tsk_YOUR_KEY_HERE

Keys use the tsk_ prefix. Keep your key secret — it grants full API access to your account.

Endpoints

Channel search is a two-step process: create a search session, then consume results via Server-Sent Events.

POST /api/search

Start a new channel search.

Request body (JSON):

{
  "query": "@handle or channel URL or video URL/ID"
}

Response:

{
  "data": {
    "searchSessionId": "uuid",
    "sseEndpointUrl": "/api/search/uuid/stream",
    "channelId": "UCxxxxxx",
    "channelTitle": "Channel Name"
  }
}

GET /api/search/:sessionId/stream

Connect to the SSE stream to receive search results in real time. Set the Accept: text/event-stream header. See the SSE Protocol section below for event types.

POST /api/search/:sessionId/continue

Continue a paginated search to load the next page of archive results. Returns a new SSE stream URL.

GET /api/search/background-status/:channelId

Check if a channel search is currently running in the background.

Response:

{
  "data": {
    "running": true,
    "searchSessionId": "uuid"
  }
}

Full-Text Search

GET /api/fts

Search across all indexed video titles and descriptions.

Query parameters:

ParameterTypeDescription
qstringSearch query (required)
pagenumberPage number (default: 1)
pageSizenumberResults per page (default: 50, max: 100)
statusstringFilter: deleted or live
hasVideobooleanOnly videos with archived video files
hasSubtitlesbooleanOnly videos with subtitles
hasSummarybooleanOnly videos with AI summaries
sortstringrelevance | date-desc | date-asc | duration-desc | duration-asc

Subtitles

GET /api/subtitles/:videoId

Download subtitles for a video.

Query parameters:

ParameterTypeDescription
formatstringsrt (default) or vtt

POST /api/subtitles/:videoId/fetch

Fetch subtitles on demand. Triggers a background job if subtitles are not yet available.

POST /api/subtitles/bulk-download

Download subtitles for multiple videos at once.

Request body (JSON):

{
  "videoIds": ["dQw4w9WgXcQ", "jNQXAC9IVRw"],
  "query": "optional highlight query"
}

AI Summaries

POST /api/summary/generate

Generate an AI summary for a video. Requires available subtitles.

Request body (JSON):

{
  "videoId": "dQw4w9WgXcQ",
  "private": false
}

Response (202 Accepted):

{
  "data": {
    "status": "generating",
    "videoId": "dQw4w9WgXcQ"
  }
}

GET /api/summary/:videoId

Get the AI summary for a specific video.

DELETE /api/summary/:videoId

Delete a summary. Admin only.

GET /api/summary/browse

Browse summaries with filters.

Query parameters:

ParameterTypeDescription
tagstringFilter by tag
channelIdstringFilter by channel
languagestringFilter by language code
pagenumberPage number (default: 1)
limitnumberResults per page (default: 20)

GET /api/summary/latest

Get the most recently generated summaries.

GET /api/summary/tags

Get popular tags across all summaries.

GET /api/summary/languages

Get available summary languages with counts.

GET /api/summary/channels

Get channels that have summaries.

AI Stenograms (L3+)

Stenogram endpoints require an API key with L3+ scope. Stenograms are private to the owner and never appear in public listings or sitemaps. See AI Stenograms for the privacy model.

POST /api/stenograms/:videoId/generate

Generate a private stenogram (full-text dialogue with speaker tags) from a completed transcript. Streams progress via Server-Sent Events.

Path parameters:

ParameterTypeDescription
videoIdstringYouTube video ID (11 characters)

Request body (JSON, optional):

{
  "language": "en"
}

language is an optional ISO 639-1/639-2 hint (e.g. en, ru, en-US). Omit for auto-detect.

Response: SSE stream with progress events, ending in a stenogram:complete event whose payload contains { stenogram, metadata }. On error: standard JSON error envelope (e.g. FEATURE_DISABLED, RATE_LIMITED, TRANSCRIPT_REQUIRED, STENOGRAM_CONFLICT).

Counter: apiStenogramsPerDay (per-key, resets at midnight UTC; shared with the MCP generate_stenogram tool).

GET /api/stenograms/@:userId/:videoId

Read your own stenogram as raw Markdown. Returns 404 to anyone but the owner.

GET /api/stenograms/@:userId/:videoId/meta

Get stenogram metadata as JSON: { videoId, title, channelTitle, channelId, language, tokensUsed, llmModel, generatedAt }.

GET /api/user/stenograms

Paginated listing of stenograms owned by the authenticated user. Supports ?page=N; page size is 20.

GET /api/user/stenograms/video-ids

Bulk existence — returns the YouTube IDs of every stenogram the caller owns (capped at 1000). Used by the web UI to render View vs Generate state per row in a single round-trip.

DELETE /api/stenograms/@:userId/:videoId

Permanently delete your stenogram. Removes the database row and the stored Markdown file. No soft-delete.

Video Status

GET /api/videos/:videoId/status-history

Get the status change history for a video (e.g., live → deleted → re-uploaded).

System

GET /api/health

Health check endpoint. Returns server status.

GET /api/features

Get feature flags and limits for the current user/tier.

GET /api/tiers

Get tier comparison data (limits, features per tier).

SSE Protocol

The channel search stream sends Server-Sent Events. Each event has a type and JSON data payload:

EventDescription
search:channel_resolvedChannel info resolved (channelId, title, avatar, etc.)
search:progressPipeline progress update (stage name, percentage)
search:videoIndividual video result
search:video_batchBatch of video results (more efficient)
search:caption_infoSubtitle availability info for discovered videos
search:completeSearch completed successfully
search:errorError occurred during search

Rate Limits

API requests (Pro tier) have the following rate limits:

When a limit is exceeded, the API returns HTTP 429 with the RATE_LIMITED error code.

Error Codes

All errors return JSON with an error object:

{"error": {"code": "RATE_LIMITED", "message": "Rate limit exceeded"}}
CodeMeaning
UNAUTHORIZEDMissing or invalid API key
FORBIDDENAPI access not enabled for your account
RATE_LIMITEDRate limit exceeded
API_KEY_REQUIREDProgrammatic access requires an API key
INVALID_INPUTInvalid query or parameters
NOT_FOUNDResource not found

Examples

curl

# Start a channel search
curl -X POST https://tube.archivarix.net/api/search \
  -H "X-API-Key: tsk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "@MrBeast"}'

# Stream results (SSE)
curl -N https://tube.archivarix.net/api/search/SESSION_ID/stream \
  -H "X-API-Key: tsk_YOUR_KEY" \
  -H "Accept: text/event-stream"

# Continue paginated search
curl -X POST https://tube.archivarix.net/api/search/SESSION_ID/continue \
  -H "X-API-Key: tsk_YOUR_KEY"

# Full-text search
curl "https://tube.archivarix.net/api/fts?q=deleted+video&pageSize=20&sort=relevance" \
  -H "X-API-Key: tsk_YOUR_KEY"

# Download subtitles (SRT)
curl https://tube.archivarix.net/api/subtitles/dQw4w9WgXcQ \
  -H "X-API-Key: tsk_YOUR_KEY"

# Download subtitles (VTT)
curl "https://tube.archivarix.net/api/subtitles/dQw4w9WgXcQ?format=vtt" \
  -H "X-API-Key: tsk_YOUR_KEY"

# Fetch subtitles on-demand
curl -X POST https://tube.archivarix.net/api/subtitles/dQw4w9WgXcQ/fetch \
  -H "X-API-Key: tsk_YOUR_KEY"

# Generate AI summary
curl -X POST https://tube.archivarix.net/api/summary/generate \
  -H "X-API-Key: tsk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"videoId": "dQw4w9WgXcQ"}'

# Get a summary
curl https://tube.archivarix.net/api/summary/dQw4w9WgXcQ \
  -H "X-API-Key: tsk_YOUR_KEY"

# Browse summaries by tag
curl "https://tube.archivarix.net/api/summary/browse?tag=music&limit=10" \
  -H "X-API-Key: tsk_YOUR_KEY"

# Health check
curl https://tube.archivarix.net/api/health

Python

import requests

API_KEY = "tsk_YOUR_KEY"
BASE = "https://tube.archivarix.net"
headers = {"X-API-Key": API_KEY}

# --- Channel search ---
resp = requests.post(f"{BASE}/api/search",
    headers=headers,
    json={"query": "@MrBeast"})
session = resp.json()["data"]
search_id = session["searchSessionId"]

# Stream results (SSE)
with requests.get(f"{BASE}/api/search/{search_id}/stream",
    headers={**headers, "Accept": "text/event-stream"},
    stream=True) as r:
    for line in r.iter_lines(decode_unicode=True):
        if line.startswith("data:"):
            print(line[5:])

# --- Full-text search ---
resp = requests.get(f"{BASE}/api/fts",
    headers=headers,
    params={"q": "deleted video", "pageSize": 20, "sort": "relevance"})
print(resp.json())

# --- Download subtitles ---
resp = requests.get(f"{BASE}/api/subtitles/dQw4w9WgXcQ",
    headers=headers,
    params={"format": "srt"})
with open("subtitles.srt", "w") as f:
    f.write(resp.text)

# --- Generate AI summary ---
resp = requests.post(f"{BASE}/api/summary/generate",
    headers=headers,
    json={"videoId": "dQw4w9WgXcQ"})
print(resp.json())  # {"data": {"status": "generating", ...}}

# --- Get summary ---
resp = requests.get(f"{BASE}/api/summary/dQw4w9WgXcQ",
    headers=headers)
summary = resp.json()["data"]
print(summary["title"], summary["tags"])

JavaScript (Node.js / Browser)

const API_KEY = "tsk_YOUR_KEY";
const BASE = "https://tube.archivarix.net";
const headers = { "X-API-Key": API_KEY };

// --- Channel search ---
const searchResp = await fetch(`${BASE}/api/search`, {
  method: "POST",
  headers: { ...headers, "Content-Type": "application/json" },
  body: JSON.stringify({ query: "@MrBeast" }),
});
const { data: { searchSessionId } } = await searchResp.json();

// Stream results (SSE)
const es = new EventSource(
  `${BASE}/api/search/${searchSessionId}/stream`
);
es.addEventListener("search:video", (e) => {
  const video = JSON.parse(e.data);
  console.log(video.title, video.videoId);
});
es.addEventListener("search:complete", () => es.close());

// --- Full-text search ---
const ftsResp = await fetch(
  `${BASE}/api/fts?q=deleted+video&pageSize=20`,
  { headers }
);
const results = await ftsResp.json();

// --- Download subtitles ---
const subResp = await fetch(
  `${BASE}/api/subtitles/dQw4w9WgXcQ?format=srt`,
  { headers }
);
const srtText = await subResp.text();

// --- Generate AI summary ---
const sumResp = await fetch(`${BASE}/api/summary/generate`, {
  method: "POST",
  headers: { ...headers, "Content-Type": "application/json" },
  body: JSON.stringify({ videoId: "dQw4w9WgXcQ" }),
});
console.log(await sumResp.json());

// --- Browse summaries ---
const browseResp = await fetch(
  `${BASE}/api/summary/browse?tag=music&limit=10`,
  { headers }
);
console.log(await browseResp.json());