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

The video generation API supports three authentication methods:

1. Session Authentication (Browser)

  • Uses browser cookies automatically
  • No additional headers required

2. API Key Authentication

# 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

{
  "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"
}

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

{
  "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

{
  "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
}

Response Format

Initial Response (202 Accepted)

{
  "runId": "fal-request-abc123xyz",
  "status": "pending",
  "model": "veo2-video",
  "cost": 2.5,
  "paymentSource": "USD",
  "remainingBalance": 47.5
}

Response Fields

  • runId: Unique identifier for polling status
  • status: Always “pending” for initial response
  • model: The model used for generation
  • cost: Cost in USD or XNO
  • paymentSource: “USD” or “XNO”
  • remainingBalance: Account balance after deduction

Polling for Status

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

Status Endpoint

GET /api/generate-video/status?runId={runId}&modelSlug={model}

Polling Example

async function pollVideoStatus(runId, model) {
  const maxAttempts = 100;
  const delayMs = 3000; // 3 seconds
  
  for (let i = 0; i < maxAttempts; i++) {
    const response = await fetch(
      `/api/generate-video/status?runId=${runId}&modelSlug=${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"
      }
    }
  }
}

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
  • CANCELLED: Request was cancelled

Complete Examples

Example 1: Text-to-Video with cURL

# 1. Submit video generation request
curl -X POST https://nano-gpt.com/api/generate-video \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "model": "veo2-video",
    "prompt": "A beautiful sunset over the ocean with waves",
    "duration": "10s",
    "aspect_ratio": "16:9"
  }'

# Response:
# {
#   "runId": "fal-request-abc123",
#   "status": "pending",
#   "model": "veo2-video",
#   "cost": 4.0,
#   "paymentSource": "USD"
# }

# 2. Poll for status
curl "https://nano-gpt.com/api/generate-video/status?runId=fal-request-abc123&modelSlug=veo2-video"

# Final response:
# {
#   "data": {
#     "status": "COMPLETED",
#     "output": {
#       "video": {
#         "url": "https://storage.example.com/video.mp4"
#       }
#     }
#   }
# }

Example 2: 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;
}

Example 3: 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

Common Error Responses

Insufficient Balance

{
  "error": "Insufficient balance",
  "status": 402
}

Invalid Session

{
  "error": "Invalid session",
  "status": 401
}

Rate Limit Exceeded

{
  "error": "Rate limit exceeded. Please wait before generating another video.",
  "status": 429
}

Content Policy Violation

{
  "error": {
    "message": "Your prompt was blocked due to safety concerns. Please modify your prompt.",
    "type": "CONTENT_POLICY_VIOLATION"
  },
  "status": 400
}

Model-Specific Errors

{
  "error": "Kling 2.1 Standard requires an input image. Please select an image to generate a video.",
  "status": 400
}

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) {
        console.error('Rate limited, retry after delay');
        // Implement exponential backoff
      } else if (response.status === 402) {
        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

  • Default: 50 requests per minute per IP address
  • API Key: Same as default
  • Internal Auth: No rate limit

Rate limits apply to the submission endpoint (/api/generate-video). Status polling endpoints have no rate limits.

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