Skip to main content

Overview

The NanoGPT API provides advanced video generation capabilities using state-of-the-art models. This guide covers how to use our video generation endpoints.

API Authentication

Supported authentication methods:
  1. API Key header: x-api-key: <your-api-key>
  2. Bearer token: Authorization: Bearer <your-api-key>
  3. Cookie session: for web clients (automatic)

Header Examples

# Using x-api-key header
curl -H "x-api-key: YOUR_API_KEY"

# Using Bearer token
curl -H "Authorization: Bearer YOUR_API_KEY"

Making a Video Generation Request

Endpoint

POST /api/generate-video

Request Headers

Content-Type: application/json
x-api-key: YOUR_API_KEY  # Optional, for API key auth

Request Body

Basic Text-to-Video Request

{
  "model": "veo2-video",
  "prompt": "A majestic eagle soaring through mountain peaks at sunset",
  "duration": "5s",
  "aspect_ratio": "16:9"
}

Image-to-Video Request

Image-conditioned models accept either imageDataUrl (base64) or imageUrl (a public HTTPS link). The platform always uses the explicit field you send before falling back to any library attachments.
Uploads sent via the API must be 4 MB or smaller. For larger assets, host them externally and provide an imageUrl.
Base64 input
{
  "model": "kling-v21-standard",
  "prompt": "Make the person in the image wave hello",
  "imageDataUrl": "data:image/jpeg;base64,/9j/4AAQ...",
  "duration": "5",
  "aspect_ratio": "16:9"
}
Public URL input
{
  "model": "kling-v21-standard",
  "prompt": "Make the person in the image wave hello",
  "imageUrl": "https://assets.example.com/reference/wave-hello.jpg",
  "duration": "5",
  "aspect_ratio": "16:9"
}

Additional Request Parameters

These parameters are available across models. Only send the fields your chosen model supports.

Core Parameters

ParameterTypeDescription
conversationUUIDstringLink a generation to a conversation
resolutionstringOutput resolution (480p, 720p, 1080p)
imageAttachmentIdstringReference to a library-stored image
videoUrlstringPublic HTTPS link to a source video (extend/edit/upscale)
videoDataUrlstringBase64-encoded data URL for a source video (max 4 MB)
videoAttachmentIdstringReference to a library-stored video
videostringAlternate video field accepted by select models (for example, wan-wavespeed-25-extend)
referenceImagesarrayMultiple reference images for reference-to-video mode
referenceVideosarrayMultiple reference videos
Prefer videoUrl (camelCase) for source videos. Only send video if the model explicitly requires it.

Audio Parameters (lipsync/avatar models)

ParameterTypeDescription
audioDataUrlstringBase64-encoded audio
audioDurationnumberDuration of provided audio in seconds
voiceIdstringVoice selection for lipsync models

Model-Specific Parameters

ParameterTypeDescription
pro / pro_modebooleanEnable pro/higher-quality mode
generateAudiobooleanGenerate audio with video (Veo 3)
modestringtext-to-video, image-to-video, reference-to-video, video-edit
orientationstringPortrait or landscape (Sora 2)
sizestringOutput dimensions (Pixverse, Wan models)
animationbooleanEnable animation (Longstories)
languagestringOutput language (Longstories)
charactersarrayCharacter definitions (Longstories)
stylestringStyle preset

Model-Specific Parameters

Veo Models

{
  "model": "veo2-video",
  "prompt": "Your prompt",
  "duration": "5s",  // 5s-30s for Veo2, fixed 8s for Veo3
  "aspect_ratio": "16:9"  // 16:9, 9:16, 1:1, 4:3, 3:4
}

Kling Models

{
  "model": "kling-video-v2",
  "prompt": "Your prompt",
  "duration": "5",  // "5" or "10"
  "aspect_ratio": "16:9",
  "negative_prompt": "blur, distortion",  // Optional
  "cfg_scale": 0.5  // 0-1, default 0.5
}

Hunyuan Models

{
  "model": "hunyuan-video",
  "prompt": "Your prompt",
  "pro_mode": false,  // true for higher quality (2x cost)
  "aspect_ratio": "16:9",
  "resolution": "720p",  // 480p, 720p, 1080p
  "num_frames": 129,  // 65, 97, 129
  "num_inference_steps": 20,  // 10-50
  "showExplicitContent": false  // Safety filter
}

Wan Image-to-Video

Accepts base64 via imageDataUrl or a public URL via imageUrl.
{
  "model": "wan-video-image-to-video",
  "prompt": "Your prompt",
  "imageDataUrl": "data:image/...",
  "num_frames": 81,  // 81-100
  "frames_per_second": 16,  // 5-24
  "resolution": "720p",  // 480p or 720p
  "num_inference_steps": 30,  // 1-40
  "negative_prompt": "blur, distortion",
  "seed": 42  // Optional
}

Seedance Models

Accepts base64 via imageDataUrl or a public URL via imageUrl. Ensure URLs are directly fetchable by the provider.
{
  "model": "seedance-video",
  "prompt": "Your prompt",
  "resolution": "1080p",  // 480p or 1080p (standard), 480p or 720p (lite)
  "duration": "5",  // "5" or "10"
  "aspect_ratio": "16:9",  // T2V only
  "camera_fixed": false,  // Static camera
  "seed": 42  // Optional
}

Supported Models

Text-to-Video Models

ModelDurationNotes
veo2-video5-8sAlso supports image-to-video
veo3-video5-8sAudio generation supported
veo3-1-videoVariableT2V and I2V modes
veo3-fast-videoVariableFast generation
sora-2VariablePro mode, multiple resolutions
kling-video5-10sBasic text-to-video
kling-video-v25-10sEnhanced quality
kling-video-o15-10sMulti-mode support
kling-video-o1-standard5-10sStandard variant
kling-v25-turbo-pro5-10sTurbo pro
kling-v25-turbo-std5-10sTurbo standard
kling-v26-pro5-10sLatest pro
minimax-video6sFixed duration
minimax-hailuo-02VariablePer-second pricing
minimax-hailuo-02-proVariablePremium variant
minimax-hailuo-23-standardVariableHailuo 2.3
minimax-hailuo-23-proVariableHailuo 2.3 pro
hunyuan-video5sPro mode available
hunyuan-video-15VariableResolution-based
wan-video-225s14B full model
wan-video-22-5b5s5B lite model
wan-video-22-turbo5sSimplified
wan-wavespeed-25VariableWan 2.5
wan-wavespeed-26VariableWan 2.6
wan-wavespeed-22-plusVariablePlus variant
seedance-videoVariableResolution-duration pricing
seedance-lite-videoVariableLite version
pixverse-v45VariableV4.5
pixverse-v5VariableV5
pixverse-v55VariableV5.5 with effects
pixverse-v55-effectsVariableEffects variant
lightricks-ltx-2-fastVariableFast generation
lightricks-ltx-2-proVariablePro quality
vidu-videoVariableVidu Q1
runwayml-gen4-alephVariableRunway Gen4
veed-fabric-1.0VariableVeed fabric
midjourney-videoVariableReturns 4 videos

Image-to-Video Only Models

ModelNotes
kling-v21-standardStandard quality
kling-v21-proPro quality
kling-v21-masterMaster quality, requires prompt
hunyuan-video-image-to-videoImage input required
wan-video-image-to-videoImage input required

Avatar/Lipsync Models

ModelInputNotes
kling-v2-avatar-standardAudio + ImageStandard avatar
kling-v2-avatar-proAudio + ImagePro avatar
kling-lipsync-t2vTextText-to-video lipsync
kling-lipsync-a2vAudioAudio-to-video lipsync
latentsyncAudio + VideoAudio-video sync
bytedance-avatar-omni-human-1.5Audio + ImageBytedance avatar
bytedance-waver-1.0Audio + ImageWaver model

Utility Models

ModelPurpose
video-upscalerUpscale video resolution
bytedance-seedance-upscalerAlternative upscaler
seedvr2-video-upscalerSeedVR upscaler
wan-wavespeed-video-editVideo editing
wan-wavespeed-22-animateVideo animation
wan-wavespeed-s2vSpeech-to-video
magicapi-video-face-swapFace swap

Extension Models

Extend models run through POST /api/generate-video with prompt plus a source video input (videoUrl, videoDataUrl, or videoAttachmentId). The task-based /api/generate-video/extend endpoint is only for Midjourney extensions. Max source video length: 120 seconds.
ModelPurpose
wan-wavespeed-25-extendExtend Wan videos
wan-wavespeed-22-spicy-extendSpicy extension
veo3-1-extendExtend VEO videos
veo3-1-fast-extendFast VEO extension
bytedance-seedance-v1.5-pro-extendExtend Seedance videos

Longstories Models (Scripted Video)

ModelNotes
longstories-movieMovie generation with voice narration
longstories-pixel-artPixel art style

Response Format

Initial Response (202 Accepted)

{
  "runId": "fal-request-abc123xyz",
  "status": "pending",
  "model": "veo2-video",
  "cost": 0.35,
  "paymentSource": "XNO",
  "remainingBalance": 12.5,
  "prechargeLabel": "string"
}

Response Fields

  • runId: Unique identifier for polling status
  • status: Always “pending” for initial response
  • model: The model used for generation
  • cost: Estimated/pre-charged cost
  • paymentSource: “USD” or “XNO”
  • remainingBalance: Account balance after deduction
  • prechargeLabel: Provider label for the precharge

Cost Information

Both the initial generation response and the status response include cost:
  • Initial response: estimated/pre-charged cost
  • Status response: final cost (when status is COMPLETED)
Pricing structures vary by model:
  • Fixed: flat rate per generation
  • Per-Second: rate x duration
  • Resolution-Based: rates per resolution tier
  • Duration-Based: step pricing (5s vs 10s)
  • Mode-Based: different rates for T2V vs I2V

Polling for Status

After receiving a runId, poll the status endpoint until completion.

Status Endpoint

GET /api/generate-video/status?runId={runId}&model={model}
model is optional but recommended for faster lookups.

Polling Example

async function pollVideoStatus(runId, model) {
  const maxAttempts = 120; // ~10 minutes total
  const delayMs = 5000; // 5 seconds (max ~10 minutes)

  for (let i = 0; i < maxAttempts; i++) {
    const response = await fetch(
      `/api/generate-video/status?runId=${runId}&model=${model}`
    );
    const result = await response.json();

    if (result.data.status === 'COMPLETED') {
      return result.data.output.video.url;
    } else if (result.data.status === 'FAILED') {
      throw new Error(result.data.error || 'Video generation failed');
    }

    // Wait before next poll
    await new Promise((resolve) => setTimeout(resolve, delayMs));
  }

  throw new Error('Video generation timed out');
}

Status Response States

In Progress

{
  "data": {
    "status": "IN_PROGRESS",
    "request_id": "fal-request-abc123xyz",
    "details": "Video is being generated"
  }
}

Completed

{
  "data": {
    "status": "COMPLETED",
    "request_id": "fal-request-abc123xyz",
    "output": {
      "video": {
        "url": "https://storage.example.com/video.mp4"
      }
    },
    "cost": 0.35
  }
}

Failed

{
  "data": {
    "status": "FAILED",
    "request_id": "fal-request-abc123xyz",
    "error": "Content policy violation",
    "isNSFWError": true,
    "userFriendlyError": "Content flagged as inappropriate. Please modify your prompt and try again."
  }
}

Status Values

  • IN_QUEUE: Request is queued
  • IN_PROGRESS: Video is being generated
  • COMPLETED: Video ready for download
  • FAILED: Generation failed
  • CANCELED: Request was canceled

Additional Endpoints

GET /api/generate-video/recover

Recover recent video generation runs for a user. Query Parameters
ParameterTypeRequiredDescription
modelstringNoFilter by model
limitnumberNoMax results (default 10, max 50)
conversationUUIDstringNoFilter by conversation
Rate Limit: 20 requests/minute

POST /api/generate-video/extend

Extend a Midjourney video using a task-based flow. Rate Limit: 20 requests/minute Required Fields: taskId (from the original Midjourney runId), index (0-3) Notes:
  • This endpoint does not accept video, videoUrl, videoDataUrl, or videoAttachmentId.
  • Use POST /api/generate-video with an extend model for source-video extension.

GET /api/generate-video/content

Proxy content retrieval for Sora 2 videos. Query Parameters
ParameterTypeRequiredDescription
runIdstringYesThe run ID
modelstringYesMust be sora-2
variantstringNovideo, thumbnail, or spritesheet

Complete Examples

The submit + poll flow works the same regardless of how you supply the image: image-conditioned models accept either imageDataUrl (base64) or a public imageUrl, and the platform prefers whichever field you send before checking library attachments.

Example 1: Text-to-Video with cURL

# 1) Submit
RUN_ID=$(curl -s -X POST https://nano-gpt.com/api/generate-video \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kling-video-v2",
    "prompt": "A cat playing piano in a jazz club",
    "duration": "5"
  }' | jq -r '.runId')

echo "Run ID: $RUN_ID"

# 2) Poll status (max ~10 minutes)
for i in {1..120}; do
  RESP=$(curl -s "https://nano-gpt.com/api/generate-video/status?runId=$RUN_ID&model=kling-video-v2" \
    -H "x-api-key: YOUR_API_KEY")
  STATUS=$(echo "$RESP" | jq -r '.data.status // empty')
  echo "Attempt $i: status=$STATUS"
  if [ "$STATUS" = "COMPLETED" ]; then
    echo "Completed response:"
    echo "$RESP" | jq .
    VIDEO_URL=$(echo "$RESP" | jq -r '.data.output.video.url')
    echo "Video URL: $VIDEO_URL"
    break
  fi
  sleep 5
done

# Download when ready
# curl -L "$VIDEO_URL" -o output.mp4

Example 2: Image-to-Video with cURL

Base64 input

RUN_ID=$(curl -s -X POST https://nano-gpt.com/api/generate-video \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kling-v21-pro",
    "prompt": "The subject walks toward the camera",
    "imageDataUrl": "data:image/jpeg;base64,/9j/4AAQ...",
    "duration": "5",
    "aspect_ratio": "16:9"
  }' | jq -r '.runId')

Public URL input

RUN_ID=$(curl -s -X POST https://nano-gpt.com/api/generate-video \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kling-v21-pro",
    "prompt": "The subject walks toward the camera",
    "imageUrl": "https://images.unsplash.com/photo-1504196606672-aef5c9cefc92?w=1024",
    "duration": "5",
    "aspect_ratio": "16:9"
  }' | jq -r '.runId')
Use the same polling loop from Example 1 to monitor either request.

Example 3: Image-to-Video with JavaScript

// 1. Convert image to base64
async function imageToBase64(imageFile) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result);
    reader.onerror = reject;
    reader.readAsDataURL(imageFile);
  });
}

// 2. Submit video generation
async function generateVideo(imageFile) {
  const imageDataUrl = await imageToBase64(imageFile);
  
  const response = await fetch('/api/generate-video', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-api-key': 'YOUR_API_KEY'
    },
    body: JSON.stringify({
      model: 'kling-v21-pro',
      prompt: 'Add gentle camera movement to this scene',
      imageDataUrl: imageDataUrl,
      duration: '5',
      aspect_ratio: '16:9'
    })
  });
  
  const result = await response.json();
  console.log('Video generation started:', result.runId);
  
  // 3. Poll for completion
  const videoUrl = await pollVideoStatus(result.runId, result.model);
  console.log('Video ready:', videoUrl);
  
  return videoUrl;
}

Using a public image URL directly

async function generateVideoFromUrl(imageUrl) {
  const response = await fetch('/api/generate-video', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-api-key': 'YOUR_API_KEY'
    },
    body: JSON.stringify({
      model: 'kling-v21-pro',
      prompt: 'Add gentle camera movement to this scene',
      imageUrl,
      duration: '5',
      aspect_ratio: '16:9'
    })
  });

  const result = await response.json();
  const videoUrl = await pollVideoStatus(result.runId, result.model);
  return videoUrl;
}

Example 4: Image-to-Video with Python

import base64
import json
import requests

API_URL = "https://nano-gpt.com/api/generate-video"
API_KEY = "YOUR_API_KEY"

def submit_image_to_video(image_path: str) -> str:
    with open(image_path, "rb") as image_file:
        encoded = base64.b64encode(image_file.read()).decode("utf-8")

    payload = {
        "model": "kling-v21-pro",
        "prompt": "Animate this scene with a slow dolly zoom",
        "imageDataUrl": f"data:image/jpeg;base64,{encoded}",
        "duration": "5",
        "aspect_ratio": "16:9",
    }
    response = requests.post(
        API_URL,
        headers={
            "x-api-key": API_KEY,
            "Content-Type": "application/json",
        },
        data=json.dumps(payload),
        timeout=60,
    )
    response.raise_for_status()
    return response.json()["runId"]
def submit_image_url(image_url: str) -> str:
    payload = {
        "model": "kling-v21-pro",
        "prompt": "Animate this scene with a slow dolly zoom",
        "imageUrl": image_url,
        "duration": "5",
        "aspect_ratio": "16:9",
    }
    response = requests.post(
        API_URL,
        headers={
            "x-api-key": API_KEY,
            "Content-Type": "application/json",
        },
        data=json.dumps(payload),
        timeout=60,
    )
    response.raise_for_status()
    return response.json()["runId"]
Reuse the polling helper from the JavaScript example (or your own status loop) to watch these run IDs until completion.

Example 5: Batch Processing

async function generateMultipleVideos(prompts) {
  // Submit all requests
  const requests = await Promise.all(
    prompts.map(async (prompt) => {
      const response = await fetch('/api/generate-video', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'x-api-key': 'YOUR_API_KEY'
        },
        body: JSON.stringify({
          model: 'seedance-lite-video',
          prompt: prompt,
          duration: '5',
          resolution: '720p'
        })
      });
      return response.json();
    })
  );
  
  // Poll all statuses concurrently
  const videos = await Promise.all(
    requests.map(({ runId, model }) => 
      pollVideoStatus(runId, model)
    )
  );
  
  return videos;
}

Error Handling

Error Response Format

{
  "error": {
    "message": "User-friendly error message",
    "type": "ERROR_TYPE"
  },
  "refundMessage": "Refund notification if applicable"
}
Error Types: CONTENT_POLICY_VIOLATION, PRO_REQUIRED, INSUFFICIENT_BALANCE, RATE_LIMITED

Error Handling Best Practices

async function generateVideoWithErrorHandling(params) {
  try {
    // Submit request
    const response = await fetch('/api/generate-video', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'x-api-key': 'YOUR_API_KEY'
      },
      body: JSON.stringify(params)
    });
    
    if (!response.ok) {
      const error = await response.json();
      
      // Handle specific error types
      if (response.status === 429 || error.error?.type === 'RATE_LIMITED') {
        console.error('Rate limited, retry after delay');
        // Implement exponential backoff
      } else if (response.status === 402 || error.error?.type === 'INSUFFICIENT_BALANCE') {
        console.error('Insufficient balance');
        // Prompt user to add credits
      } else if (error.error?.type === 'CONTENT_POLICY_VIOLATION') {
        console.error('Content policy violation');
        // Show user-friendly message
      }
      
      throw new Error(error.error?.message || error.error);
    }
    
    const result = await response.json();
    
    // Poll for status with timeout
    const videoUrl = await pollVideoStatus(result.runId, result.model);
    return videoUrl;
    
  } catch (error) {
    console.error('Video generation failed:', error);
    throw error;
  }
}

Rate Limits

EndpointLimit
POST /api/generate-video50 requests/minute per IP
GET /api/generate-video/statusNo explicit limit (cached)
POST /api/generate-video/extend20 requests/minute per IP
GET /api/generate-video/recover20 requests/minute per IP

Additional Notes

Pro Mode

  • sora-2: Pro mode required for 1792x1024 resolution
  • hunyuan-video: Pro mode available
  • Various Kling models: Pro variants

Audio Generation

  • veo3-video: Set generateAudio: true to include audio

Reference-to-Video

Supported by kling-video-o1, kling-video-o1-standard, wan-wavespeed-26. Use referenceImages with image URLs or data URLs.

Automatic Refunds

Refunds are automatically issued when:
  • Provider returns a failure
  • Content policy violation (before processing)
  • Submission fails before acknowledgement

Best Practices

  1. Choose the Right Model
    • Use text-to-video for creative generation
    • Use image-to-video for animating existing content
    • Consider cost vs quality tradeoffs
  2. Optimize Prompts
    • Be specific and descriptive
    • Include motion and camera directions
    • Avoid content policy violations
  3. Handle Async Operations
    • Implement proper polling with delays
    • Set reasonable timeouts (5-10 minutes)
    • Show progress to users
  4. Error Recovery
    • Implement retry logic for transient failures
    • Handle rate limits with exponential backoff
    • Provide clear error messages to users
  5. Cost Management
    • Check balance before submitting
    • Estimate costs before generation
    • Use shorter durations for testing