All routes live under functions/api/ and share _middleware.js:
- Max body 256 KB
- Optional
Authorization: Bearer <supabase access token>(verified; bad token → 401) - Anonymous allowed (subject = IP +
X-Client-Id) with stricter IP budgets - Per-endpoint hourly rate limits in KV (
YT_JOBSbinding) - Response
429+Retry-Afterwhen limited
Base URL: same origin as the PWA (e.g. https://kar-tochki.pages.dev/api/...).
Fetch YouTube metadata + transcript via Supadata (client BYOK).
Body (JSON):
| Field | Required | Description |
|---|---|---|
url |
yes | YouTube watch/shorts URL |
supadataApiKey |
yes | User’s Supadata key from Settings |
Success: { video, transcript } or { pending, jobId, video } for long jobs.
GET /api/yt-video?jobId=…: poll job (scoped to middleware subject).
Limits: ~20 req/hour/subject; video duration ≤ 20 minutes.
LLM card generation from a prepared transcript (Gemini or Groq BYOK).
Body (JSON): video + transcript + mode (words | phrases | both | sentences) + API keys from settings.
Success: { cards: [...] } (front/back candidates).
Limits: ~20 req/hour/subject.
Optional neural TTS (Orpheus / Groq path). Requires user TTS key in settings when used.
Limits: ~40 req/hour/subject.
Stock image/GIF search (Openverse / Pixabay / Giphy keys from settings).
Limits: ~120 req/hour/subject; anonymous IP ceiling still applies (~300/hour).
JSON body typically { error: string, code?: string }. Common statuses: 400, 401, 413, 429, 502 (upstream).
There is no global auth gate on /api/* by design (local/demo users). Harden public demos with Cloudflare WAF / lower limits — see SECURITY.md.