For AI agents, LLMs, and automated tooling. These files are always available at the root of the site.
Full machine-parseable spec with schemas, examples, and error definitions for the full API surface.
Open SpecDiscovery file for AI agents. Like robots.txt but for LLMs — lists capabilities and links.
Open IndexComplete plain-text API reference. One file an agent can ingest to fully integrate.
Pick your language tab to see colored code samples. Every example returns immediately with a job_id — use it to poll for completion.✨ v2 adds: artist_face_image_url · lipsync
Create an API key from your dashboard to get started.
Keys stay server-side only.
Set your API key as an environment variable in your runtime.
export PEAKMV_API_KEY="peak_..."Use our Client Wrapper pattern to handle submission and polling automatically. Copy the helper function below to get an "SDK-like" experience.
// 1. Paste this helper functionconst peakmv = { subscribe: async (options) => { // Submit Job const res = await fetch('https://peakmv.com/api/v2/generate', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.PEAKMV_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify(options) }); const { status_url } = await res.json(); // Poll for Completion while (true) { const check = await fetch(status_url, { headers: { 'Authorization': `Bearer ${process.env.PEAKMV_API_KEY}` } }); const job = await check.json(); if (job.status === 'ready') return job; if (job.status === 'failed') throw new Error(job.error); await new Promise(r => setTimeout(r, 3000)); } }};// 2. Use it (Just like an SDK)const result = await peakmv.subscribe({ audio_url: 'https://example.com/song.mp3', title: 'My Peak Music Video', // Optional prompt: 'Cyberpunk city', // Optional duration: 30, // Optional (seconds) video_quality: '1080p', // Optional // v2 only ↓ artist_face_image_url: 'https://example.com/face.jpg', // Optional lipsync: true, // Optional — sync vocals to faces});console.log("Video Ready:", result.video_url);Include your API key in the Authorization header as a Bearer token.
Authorization: Bearer peak_...audio_urlversionv1 or v2. Default: v2. Set to v1 to route this request to the Classic pipeline (artist_face_image_url and lipsync are not allowed in v1 mode).titlepromptdurationstart_timevideo_quality720p or 1080p. Default: 1080p (Premium Peak 1080p Full HD).artist_face_image_urllipsyncsuccessproject_idmessagecost_creditsvideo_urlgeneration_modeimage_to_video for v2.video_quality720p or 1080p.clips_generatedlipsync_seconds_billed// Poll with the job_id from POST /generate responseconst jobId = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'; // from job_idconst res = await fetch(`https://peakmv.com/api/v2/jobs/${jobId}`, {headers: { Authorization: `Bearer ${process.env.PEAKMV_API_KEY}` }});const job = await res.json();// job.video_url -> https://auth.peakmv.com/.../vid.mp4// job.status -> 'ready' | 'processing' | 'queued'// job.clips_generated -> 6 (v2 only)// job.lipsync_seconds_billed -> 30 (v2 only, if lipsync enabled)
Video generation is async (2-20 minutes). After submitting, poll the status_url every 5-10 seconds.
/v2/jobs/{id}Primary polling endpoint. Returns status, video_url (when ready), and error (when failed).
// Response when ready
{
"id": "a1b2c3d4-...",
"status": "ready",
"video_url": "https://auth.peakmv.com/.../video.mp4",
"created_at": "2026-02-21T12:00:00Z",
"duration_seconds": 185,
"clips_generated": 6
}/v2/projects/{id}Same as /jobs but also includes a shareable project_page_url.
// Response when ready
{
"id": "a1b2c3d4-...",
"status": "ready",
"video_url": "https://auth.peakmv.com/.../video.mp4",
"duration_seconds": 185,
"project_page_url": "https://peakmv.com/project/a1b2...",
"created_at": "2026-02-21T12:00:00Z"
}Credits are deducted when generation starts. Slide to preview the same math logic used on the pricing page (up to 5 minutes). View full pricing →
429 response:/v1/* and /v2/* endpoints.400{"error": "audio_url is required"}401{"error": "Invalid or inactive API Key"}402{"error": "Insufficient credits",
"required": 18, "available": 5.5}404{"error": "Project not found"}429retry_after_seconds, then retry.{"error": "Rate limit exceeded",
"retry_after_seconds": 42}500{"error": "Server Error"}429 (after waiting) and 500 (with backoff). Never retry 400/401/402 — fix the request instead. Never retry a successful POST /generate — you will be charged again.