Documentation Index
Fetch the complete documentation index at: https://docs.nano-gpt.com/llms.txt
Use this file to discover all available pages before exploring further.
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:
- API Key header:
x-api-key: <your-api-key>
- Bearer token:
Authorization: Bearer <your-api-key>
- Cookie session: for web clients (automatic)
# 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
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
| Parameter | Type | Description |
|---|
conversationUUID | string | Link a generation to a conversation |
resolution | string | Output resolution (480p, 720p, 1080p) |
imageAttachmentId | string | Reference to a library-stored image |
videoUrl | string | Public HTTPS link to a source video (extend/edit/upscale) |
videoDataUrl | string | Base64-encoded data URL for a source video (max 4 MB) |
videoAttachmentId | string | Reference to a library-stored video |
video | string | Alternate video field accepted by select models (for example, wan-wavespeed-25-extend) |
referenceImages | array | Multiple reference images for reference-to-video mode |
referenceVideos | array | Multiple reference videos |
Prefer videoUrl (camelCase) for source videos. Only send video if the model explicitly requires it.
Audio Parameters (lipsync/avatar models)
| Parameter | Type | Description |
|---|
audioDataUrl | string | Base64-encoded audio |
audioDuration | number | Duration of provided audio in seconds |
voiceId | string | Voice selection for lipsync models |
Model-Specific Parameters
| Parameter | Type | Description |
|---|
pro / pro_mode | boolean | Enable pro/higher-quality mode |
generateAudio | boolean | Generate audio with video (Veo 3) |
mode | string | text-to-video, image-to-video, reference-to-video, video-edit |
orientation | string | Portrait or landscape (Sora 2) |
size | string | Output dimensions (Pixverse, Wan models) |
animation | boolean | Enable animation (Longstories) |
language | string | Output language (Longstories) |
characters | array | Character definitions (Longstories) |
style | string | Style 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.
{
"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
| Model | Duration | Notes |
|---|
veo2-video | 5-8s | Also supports image-to-video |
veo3-video | 5-8s | Audio generation supported |
veo3-1-video | Variable | T2V and I2V modes |
veo3-fast-video | Variable | Fast generation |
sora-2 | Variable | Pro mode, multiple resolutions |
kling-video | 5-10s | Basic text-to-video |
kling-video-v2 | 5-10s | Enhanced quality |
kling-video-o1 | 5-10s | Multi-mode support |
kling-video-o1-standard | 5-10s | Standard variant |
kling-v25-turbo-pro | 5-10s | Turbo pro |
kling-v25-turbo-std | 5-10s | Turbo standard |
kling-v26-pro | 5-10s | Latest pro |
minimax-video | 6s | Fixed duration |
minimax-hailuo-02 | Variable | Per-second pricing |
minimax-hailuo-02-pro | Variable | Premium variant |
minimax-hailuo-23-standard | Variable | Hailuo 2.3 |
minimax-hailuo-23-pro | Variable | Hailuo 2.3 pro |
hunyuan-video | 5s | Pro mode available |
hunyuan-video-15 | Variable | Resolution-based |
wan-video-22 | 5s | 14B full model |
wan-video-22-5b | 5s | 5B lite model |
wan-video-22-turbo | 5s | Simplified |
wan-wavespeed-25 | Variable | Wan 2.5 |
wan-wavespeed-26 | Variable | Wan 2.6 |
wan-wavespeed-22-plus | Variable | Plus variant |
seedance-video | Variable | Resolution-duration pricing |
seedance-lite-video | Variable | Lite version |
pixverse-v45 | Variable | V4.5 |
pixverse-v5 | Variable | V5 |
pixverse-v55 | Variable | V5.5 with effects |
pixverse-v55-effects | Variable | Effects variant |
lightricks-ltx-2-fast | Variable | Fast generation |
lightricks-ltx-2-pro | Variable | Pro quality |
vidu-video | Variable | Vidu Q1 |
runwayml-gen4-aleph | Variable | Runway Gen4 |
veed-fabric-1.0 | Variable | Veed fabric |
midjourney-video | Variable | Returns 4 videos |
Image-to-Video Only Models
| Model | Notes |
|---|
kling-v21-standard | Standard quality |
kling-v21-pro | Pro quality |
kling-v21-master | Master quality, requires prompt |
hunyuan-video-image-to-video | Image input required |
wan-video-image-to-video | Image input required |
Avatar/Lipsync Models
| Model | Input | Notes |
|---|
kling-v2-avatar-standard | Audio + Image | Standard avatar |
kling-v2-avatar-pro | Audio + Image | Pro avatar |
kling-lipsync-t2v | Text | Text-to-video lipsync |
kling-lipsync-a2v | Audio | Audio-to-video lipsync |
latentsync | Audio + Video | Audio-video sync |
bytedance-avatar-omni-human-1.5 | Audio + Image | Bytedance avatar |
bytedance-waver-1.0 | Audio + Image | Waver model |
Utility Models
| Model | Purpose |
|---|
video-upscaler | Upscale video resolution |
bytedance-seedance-upscaler | Alternative upscaler |
seedvr2-video-upscaler | SeedVR upscaler |
wan-wavespeed-video-edit | Video editing |
wan-wavespeed-22-animate | Video animation |
wan-wavespeed-s2v | Speech-to-video |
magicapi-video-face-swap | Face 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.
| Model | Purpose |
|---|
wan-wavespeed-25-extend | Extend Wan videos |
wan-wavespeed-22-spicy-extend | Spicy extension |
veo3-1-extend | Extend VEO videos |
veo3-1-fast-extend | Fast VEO extension |
bytedance-seedance-v1.5-pro-extend | Extend Seedance videos |
Longstories Models (Scripted Video)
| Model | Notes |
|---|
longstories-movie | Movie generation with voice narration |
longstories-pixel-art | Pixel art style |
Initial Response (202 Accepted)
{
"runId": "vid_m1abc123def456",
"id": "vid_m1abc123def456",
"status": "pending",
"model": "veo2-video",
"cost": 0.35,
"paymentSource": "XNO",
"remainingBalance": 12.5,
"prechargeLabel": "string"
}
Response Fields
runId: NanoGPT job ID used for polling (format vid_...)
id: Alias for runId (same value)
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: Billing label for the precharge
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/video/status?requestId={runId}
You can send requestId or runId; no model parameter is required.
Polling Example
async function pollVideoStatus(runId) {
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/video/status?requestId=${runId}`
);
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
{
"requestId": "vid_m1abc123def456",
"model": "veo2-video",
"data": {
"status": "IN_PROGRESS",
"requestId": "vid_m1abc123def456",
"details": "Video is being generated"
}
}
Completed
{
"requestId": "vid_m1abc123def456",
"model": "veo2-video",
"data": {
"status": "COMPLETED",
"requestId": "vid_m1abc123def456",
"output": {
"video": {
"url": "https://storage.example.com/video.mp4"
}
},
"cost": 0.35
}
}
Failed
{
"requestId": "vid_m1abc123def456",
"model": "veo2-video",
"data": {
"status": "FAILED",
"requestId": "vid_m1abc123def456",
"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
| Parameter | Type | Required | Description |
|---|
model | string | No | Filter by model |
limit | number | No | Max results (default 10, max 50) |
conversationUUID | string | No | Filter 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: runId (preferred) or taskId (legacy alias), index (0-3)
Notes:
- This endpoint does not accept
video, videoUrl, videoDataUrl, or videoAttachmentId.
- Session ownership is enforced; requests for jobs you do not own return
403.
- 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
| Parameter | Type | Required | Description |
|---|
runId | string | Yes | The run ID |
model | string | Yes | Must be sora-2 |
variant | string | No | video, 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/video/status?requestId=$RUN_ID" \
-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
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')
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);
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);
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)
)
);
return videos;
}
Error Handling
{
"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);
return videoUrl;
} catch (error) {
console.error('Video generation failed:', error);
throw error;
}
}
Rate Limits
| Endpoint | Limit |
|---|
POST /api/generate-video | 50 requests/minute per IP |
GET /api/video/status | No explicit limit (cached) |
GET /api/generate-video/status | Deprecated (legacy compatibility) |
POST /api/generate-video/extend | 20 requests/minute per IP |
GET /api/generate-video/recover | 20 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:
- The job returns a failure
- Content policy violation (before processing)
- Submission fails before acknowledgement
Best Practices
-
Choose the Right Model
- Use text-to-video for creative generation
- Use image-to-video for animating existing content
- Consider cost vs quality tradeoffs
-
Optimize Prompts
- Be specific and descriptive
- Include motion and camera directions
- Avoid content policy violations
-
Handle Async Operations
- Implement proper polling with delays
- Set reasonable timeouts (5-10 minutes)
- Show progress to users
-
Error Recovery
- Implement retry logic for transient failures
- Handle rate limits with exponential backoff
- Provide clear error messages to users
-
Cost Management
- Check balance before submitting
- Estimate costs before generation
- Use shorter durations for testing