"use client"; import { useEffect, useState } from "react"; import { API_DOCS_URL } from "@/lib/plans"; type ApiKeyStatus = { configured: boolean; prefix: string | null; }; type RateLimitStatus = { limit: number; used: number; remaining: number; windowSeconds: number; resetsInSeconds: number; bonus?: number; }; type Props = { initialStatus: ApiKeyStatus; initialRateLimit: RateLimitStatus; }; export function ApiKeySettings({ initialStatus, initialRateLimit }: Props) { const [status, setStatus] = useState(initialStatus); const [rateLimit, setRateLimit] = useState(initialRateLimit); const [newKey, setNewKey] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [success, setSuccess] = useState(null); useEffect(() => { let active = true; const refresh = () => { fetch("/api/account/api-rate-limit") .then(async (res) => { if (!res.ok) return; const data = await res.json(); if (active) setRateLimit(data); }) .catch(() => {}); }; refresh(); const id = setInterval(refresh, 5000); return () => { active = false; clearInterval(id); }; }, []); async function generateKey() { if ( status.configured && !confirm("This will replace your existing API key. Continue?") ) { return; } setLoading(true); setError(null); setSuccess(null); setNewKey(null); try { const res = await fetch("/api/account/api-key", { method: "POST" }); const data = await res.json(); if (!res.ok) throw new Error(data.error || "Failed to generate key"); setNewKey(data.apiKey); setStatus({ configured: true, prefix: data.prefix }); setSuccess("API key generated. Copy it now — it will not be shown again."); } catch (e) { setError(e instanceof Error ? e.message : "Failed to generate key"); } finally { setLoading(false); } } async function revokeKey() { if (!confirm("Revoke the current API key?")) return; setLoading(true); setError(null); setSuccess(null); setNewKey(null); try { const res = await fetch("/api/account/api-key", { method: "DELETE" }); const data = await res.json().catch(() => ({})); if (!res.ok) throw new Error(data.error || "Failed to revoke key"); setStatus({ configured: false, prefix: null }); setSuccess("API key revoked."); } catch (e) { setError(e instanceof Error ? e.message : "Failed to revoke key"); } finally { setLoading(false); } } return (

Use the REST API to upload files and create batch video jobs programmatically.

API rate limit

Resets in {rateLimit.resetsInSeconds}s

{rateLimit.used} / {rateLimit.limit} requests used this minute

Self-hosted: API rate limits are effectively unlimited.

{status.configured && status.prefix && (

Active key: {status.prefix}…

)} {newKey && (

Your new API key (copy now)

{newKey}
)} {success && (
{success}
)} {error && (
{error}
)}
{status.configured && ( )}

Endpoints

  • POST /api/v1/upload: upload image or audio file
  • POST /api/v1/jobs: create job from paths (recommended for large batches)
  • POST /api/v1/jobs/batch: small packs only (one-shot multipart)
  • GET /api/v1/playlists: list YouTube playlists
  • POST /api/v1/playlists: create a YouTube playlist
  • GET /api/v1/jobs: list jobs
  • GET /api/v1/jobs/:id: job status

Send Authorization: Bearer YOUR_API_KEY on every request.{" "} Full API docs

); }