Sora2Everything
Back to Tutorials
APIadvanced
18 min

Sora 2 API Integration Guide

Learn how to seamlessly integrate Sora 2 API into your applications with complete code examples and best practices.

By: Sora2Everything Team Β· 2025-10-12

Sora 2 API Documentation: Complete Integration Tutorial with Code Examples

Introduction: How to Use the Sora 2 API

The Sora 2 API enables developers to programmatically generate AI videos, opening up possibilities for automated content creation, workflow integration, and scalable video production. This comprehensive Sora 2 API tutorial covers everything from setup to production deployment, with complete Python and JavaScript code examples.

What You'll Learn

  • Setting up API authentication
  • Making your first API request
  • Advanced generation parameters
  • Managing video outputs
  • Error handling and rate limits
  • Production best practices
  • Real-world integration examples

Prerequisites

  • OpenAI API account with Sora access
  • Basic programming knowledge (Python or JavaScript)
  • Understanding of REST APIs
  • Familiarity with async/await patterns

Getting Started with Sora 2 API: Authentication and Setup

Sora 2 API Access and Pricing

The Sora 2 API is currently available to:

  • OpenAI API users with approved access
  • ChatGPT Pro subscribers (20 requests/day)
  • Enterprise customers (custom limits)

Pricing (as of October 2025):

  • $0.10 per second of generated video
  • 5-second video: $0.50
  • 20-second video: $2.00
  • 60-second video: $6.00

Authentication

Step 1: Get Your API Key

  1. Visit platform.openai.com/api-keys
  2. Click "Create new secret key"
  3. Name it (e.g., "Sora Integration")
  4. Copy and save the key securely

Step 2: Set Up Environment

# Install OpenAI SDK
npm install openai
# or
pip install openai
// JavaScript/Node.js
import OpenAI from 'openai';

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY
});
# Python
from openai import OpenAI

client = OpenAI(
    api_key=os.environ.get("OPENAI_API_KEY")
)

Sora 2 API Tutorial: Your First Video Generation Request

How to Generate Videos with Sora 2 API

JavaScript Example

async function generateVideo() {
  try {
    const video = await openai.videos.generate({
      model: "sora-2",
      prompt: "A golden retriever playing in a sunny garden",
      duration: 10,
      resolution: "1080p",
      aspect_ratio: "16:9"
    });

    console.log("Video URL:", video.url);
    return video;
  } catch (error) {
    console.error("Error:", error);
  }
}

Python Example

def generate_video():
    try:
        video = client.videos.generate(
            model="sora-2",
            prompt="A golden retriever playing in a sunny garden",
            duration=10,
            resolution="1080p",
            aspect_ratio="16:9"
        )

        print(f"Video URL: {video.url}")
        return video
    except Exception as error:
        print(f"Error: {error}")

Response Object

{
  "id": "video_abc123",
  "object": "video",
  "created": 1704672000,
  "model": "sora-2",
  "status": "processing",
  "prompt": "A golden retriever playing in a sunny garden",
  "duration": 10,
  "resolution": "1080p",
  "aspect_ratio": "16:9",
  "url": null,
  "expires_at": 1704758400
}

Sora 2 API Advanced Parameters: Customizing Video Generation

Complete List of Sora API Generation Options

const advancedVideo = await openai.videos.generate({
  // Required
  model: "sora-2",
  prompt: "Your detailed prompt here",

  // Duration (5-60 seconds)
  duration: 20,

  // Resolution
  resolution: "1080p", // "480p", "720p", "1080p"

  // Aspect ratio
  aspect_ratio: "16:9", // "16:9", "9:16", "1:1", "21:9"

  // Frame rate
  fps: 30, // 24 or 30

  // Optional: Seed for reproducibility
  seed: 12345,

  // Optional: Number of variations
  n: 1, // 1-4

  // Optional: Quality setting
  quality: "standard", // "standard" or "hd"

  // Optional: Webhook for async notification
  webhook_url: "https://yourapi.com/webhook",

  // Optional: Custom metadata
  metadata: {
    project: "Marketing Campaign Q1",
    user_id: "user_123"
  }
});

Aspect Ratios Guide

const aspectRatios = {
  "16:9": {
    use_cases: ["YouTube", "Website", "Presentations"],
    dimensions: "1920x1080"
  },
  "9:16": {
    use_cases: ["TikTok", "Instagram Reels", "Stories"],
    dimensions: "1080x1920"
  },
  "1:1": {
    use_cases: ["Instagram Feed", "Social Media"],
    dimensions: "1080x1080"
  },
  "21:9": {
    use_cases: ["Cinematic", "Ultra-wide"],
    dimensions: "2560x1080"
  }
};

Polling for Completion

Videos are generated asynchronously. You need to poll for completion:

JavaScript Implementation

async function waitForVideo(videoId) {
  const maxAttempts = 60; // 10 minutes max
  const pollInterval = 10000; // 10 seconds

  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    const video = await openai.videos.retrieve(videoId);

    if (video.status === "completed") {
      return video;
    } else if (video.status === "failed") {
      throw new Error(`Video generation failed: ${video.error}`);
    }

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

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

// Usage
const video = await generateVideo();
const completedVideo = await waitForVideo(video.id);
console.log("Video ready:", completedVideo.url);

Python Implementation

import time

def wait_for_video(video_id):
    max_attempts = 60  # 10 minutes max
    poll_interval = 10  # 10 seconds

    for attempt in range(max_attempts):
        video = client.videos.retrieve(video_id)

        if video.status == "completed":
            return video
        elif video.status == "failed":
            raise Exception(f"Video generation failed: {video.error}")

        time.sleep(poll_interval)

    raise Exception("Video generation timed out")

# Usage
video = generate_video()
completed_video = wait_for_video(video.id)
print(f"Video ready: {completed_video.url}")

Webhook Integration

For better performance, use webhooks instead of polling:

Setting Up Webhook Endpoint

// Express.js example
import express from 'express';
import crypto from 'crypto';

const app = express();
app.use(express.json());

app.post('/webhooks/sora', (req, res) => {
  // Verify webhook signature
  const signature = req.headers['x-openai-signature'];
  const payload = JSON.stringify(req.body);

  if (!verifySignature(payload, signature)) {
    return res.status(401).send('Invalid signature');
  }

  // Process webhook
  const { id, status, url, error } = req.body;

  if (status === 'completed') {
    console.log(`Video ${id} completed: ${url}`);
    // Download and process video
    downloadVideo(url, id);
  } else if (status === 'failed') {
    console.error(`Video ${id} failed: ${error}`);
    // Handle error
  }

  res.status(200).send('OK');
});

function verifySignature(payload, signature) {
  const secret = process.env.WEBHOOK_SECRET;
  const hmac = crypto.createHmac('sha256', secret);
  const digest = hmac.update(payload).digest('hex');
  return digest === signature;
}

Downloading Videos

JavaScript Download

import fs from 'fs';
import https from 'https';

async function downloadVideo(url, filename) {
  return new Promise((resolve, reject) => {
    const file = fs.createWriteStream(`./videos/${filename}.mp4`);

    https.get(url, (response) => {
      response.pipe(file);

      file.on('finish', () => {
        file.close();
        console.log(`Downloaded: ${filename}.mp4`);
        resolve();
      });
    }).on('error', (err) => {
      fs.unlink(`./videos/${filename}.mp4`, () => {});
      reject(err);
    });
  });
}

Python Download

import requests

def download_video(url, filename):
    response = requests.get(url, stream=True)
    response.raise_for_status()

    filepath = f"./videos/{filename}.mp4"

    with open(filepath, 'wb') as file:
        for chunk in response.iter_content(chunk_size=8192):
            file.write(chunk)

    print(f"Downloaded: {filename}.mp4")
    return filepath

Sora 2 API Error Handling: Retry Logic and Best Practices

Common Sora API Errors and Solutions

async function robustGenerate(prompt, options = {}) {
  try {
    const video = await openai.videos.generate({
      model: "sora-2",
      prompt,
      ...options
    });
    return video;

  } catch (error) {
    switch (error.status) {
      case 400:
        console.error("Invalid request:", error.message);
        // Prompt may violate content policy or be malformed
        break;

      case 401:
        console.error("Authentication failed");
        // Check API key
        break;

      case 429:
        console.error("Rate limit exceeded");
        // Implement exponential backoff
        return retryWithBackoff(prompt, options);

      case 500:
        console.error("Server error");
        // Retry after delay
        break;

      default:
        console.error("Unknown error:", error);
    }
    throw error;
  }
}

Retry Logic with Exponential Backoff

async function retryWithBackoff(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (i === maxRetries - 1) throw error;

      const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
      console.log(`Retry ${i + 1}/${maxRetries} after ${delay}ms`);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}

// Usage
const video = await retryWithBackoff(() =>
  openai.videos.generate({
    model: "sora-2",
    prompt: "Your prompt here"
  })
);

Rate Limiting

Implementing Rate Limiter

class RateLimiter {
  constructor(requestsPerMinute) {
    this.requestsPerMinute = requestsPerMinute;
    this.queue = [];
    this.processing = false;
  }

  async add(fn) {
    return new Promise((resolve, reject) => {
      this.queue.push({ fn, resolve, reject });
      this.process();
    });
  }

  async process() {
    if (this.processing || this.queue.length === 0) return;

    this.processing = true;
    const { fn, resolve, reject } = this.queue.shift();

    try {
      const result = await fn();
      resolve(result);
    } catch (error) {
      reject(error);
    }

    // Wait based on rate limit
    const delay = 60000 / this.requestsPerMinute;
    await new Promise(resolve => setTimeout(resolve, delay));

    this.processing = false;
    this.process();
  }
}

// Usage
const limiter = new RateLimiter(20); // 20 requests per minute

for (const prompt of prompts) {
  await limiter.add(() => openai.videos.generate({
    model: "sora-2",
    prompt
  }));
}

Production Best Practices

1. Configuration Management

// config.js
export const config = {
  openai: {
    apiKey: process.env.OPENAI_API_KEY,
    model: "sora-2",
    defaultDuration: 10,
    defaultResolution: "1080p",
    timeout: 600000 // 10 minutes
  },
  storage: {
    provider: "s3", // or "gcs", "azure"
    bucket: process.env.STORAGE_BUCKET,
    region: process.env.STORAGE_REGION
  },
  webhooks: {
    endpoint: process.env.WEBHOOK_URL,
    secret: process.env.WEBHOOK_SECRET
  },
  rateLimit: {
    requestsPerMinute: 20,
    maxConcurrent: 5
  }
};

2. Logging and Monitoring

import winston from 'winston';

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' })
  ]
});

async function generateWithLogging(prompt, metadata = {}) {
  const startTime = Date.now();

  logger.info('Video generation started', {
    prompt: prompt.substring(0, 100),
    ...metadata
  });

  try {
    const video = await openai.videos.generate({
      model: "sora-2",
      prompt,
      metadata
    });

    const duration = Date.now() - startTime;
    logger.info('Video generation completed', {
      videoId: video.id,
      duration: `${duration}ms`,
      ...metadata
    });

    return video;
  } catch (error) {
    logger.error('Video generation failed', {
      error: error.message,
      prompt: prompt.substring(0, 100),
      ...metadata
    });
    throw error;
  }
}

3. Database Integration

// Using Prisma ORM
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

async function createVideoRecord(userId, prompt, options) {
  // Create database record
  const record = await prisma.video.create({
    data: {
      userId,
      prompt,
      status: 'pending',
      duration: options.duration,
      resolution: options.resolution,
      aspectRatio: options.aspect_ratio
    }
  });

  try {
    // Generate video
    const video = await openai.videos.generate({
      model: "sora-2",
      prompt,
      ...options,
      metadata: { recordId: record.id }
    });

    // Update record with video ID
    await prisma.video.update({
      where: { id: record.id },
      data: {
        videoId: video.id,
        status: 'processing'
      }
    });

    return record;
  } catch (error) {
    // Update record with error
    await prisma.video.update({
      where: { id: record.id },
      data: {
        status: 'failed',
        error: error.message
      }
    });
    throw error;
  }
}

Sora 2 API Code Examples: Real-World Integration Scenarios

Example 1: Batch Video Generator with Sora API

class BatchVideoGenerator {
  constructor(openai, options = {}) {
    this.openai = openai;
    this.maxConcurrent = options.maxConcurrent || 5;
    this.onProgress = options.onProgress || (() => {});
    this.onComplete = options.onComplete || (() => {});
  }

  async generateBatch(prompts) {
    const results = [];
    const chunks = this.chunkArray(prompts, this.maxConcurrent);

    for (let i = 0; i < chunks.length; i++) {
      const chunk = chunks[i];
      const chunkResults = await Promise.all(
        chunk.map(prompt => this.generateSingle(prompt))
      );

      results.push(...chunkResults);
      this.onProgress({
        completed: results.length,
        total: prompts.length,
        percentage: (results.length / prompts.length) * 100
      });
    }

    this.onComplete(results);
    return results;
  }

  async generateSingle(prompt) {
    try {
      const video = await this.openai.videos.generate({
        model: "sora-2",
        prompt
      });

      const completed = await this.waitForVideo(video.id);
      return { success: true, prompt, video: completed };
    } catch (error) {
      return { success: false, prompt, error: error.message };
    }
  }

  chunkArray(array, size) {
    const chunks = [];
    for (let i = 0; i < array.length; i += size) {
      chunks.push(array.slice(i, i + size));
    }
    return chunks;
  }

  async waitForVideo(videoId) {
    // Implementation from earlier
  }
}

// Usage
const generator = new BatchVideoGenerator(openai, {
  maxConcurrent: 3,
  onProgress: (progress) => {
    console.log(`Progress: ${progress.percentage.toFixed(1)}%`);
  }
});

const prompts = [
  "A cat playing with yarn",
  "Sunset over mountains",
  "Busy city street at night"
];

const results = await generator.generateBatch(prompts);

Example 2: Social Media Content Pipeline

class SocialMediaPipeline {
  async createCampaign(content, platforms) {
    const videos = {};

    for (const platform of platforms) {
      const config = this.getPlatformConfig(platform);
      const prompt = this.optimizePromptForPlatform(content, platform);

      const video = await openai.videos.generate({
        model: "sora-2",
        prompt,
        aspect_ratio: config.aspectRatio,
        duration: config.maxDuration
      });

      const completed = await waitForVideo(video.id);
      const downloaded = await downloadVideo(completed.url, `${platform}_${Date.now()}`);

      videos[platform] = {
        file: downloaded,
        url: completed.url,
        platform,
        config
      };
    }

    return videos;
  }

  getPlatformConfig(platform) {
    const configs = {
      youtube: { aspectRatio: "16:9", maxDuration: 60 },
      instagram: { aspectRatio: "1:1", maxDuration: 30 },
      tiktok: { aspectRatio: "9:16", maxDuration: 60 },
      twitter: { aspectRatio: "16:9", maxDuration: 30 }
    };
    return configs[platform];
  }

  optimizePromptForPlatform(content, platform) {
    const styles = {
      youtube: "cinematic, professional",
      instagram: "aesthetic, trendy, high saturation",
      tiktok: "energetic, fast-paced, engaging",
      twitter: "attention-grabbing, clear message"
    };

    return `${content}, ${styles[platform]}, optimized for ${platform}`;
  }
}

// Usage
const pipeline = new SocialMediaPipeline();
const videos = await pipeline.createCampaign(
  "Product launch teaser for new smartphone",
  ["youtube", "instagram", "tiktok"]
);

Security Considerations

1. API Key Protection

// ❌ Never do this
const openai = new OpenAI({
  apiKey: "sk-..."  // Hardcoded key
});

// βœ… Use environment variables
const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY
});

// βœ… Use secret management services
import { SecretsManager } from '@aws-sdk/client-secrets-manager';

async function getApiKey() {
  const client = new SecretsManager({ region: 'us-east-1' });
  const response = await client.getSecretValue({
    SecretId: 'openai-api-key'
  });
  return JSON.parse(response.SecretString).apiKey;
}

2. Content Filtering

async function safeGenerate(prompt) {
  // Pre-validation
  if (!isValidPrompt(prompt)) {
    throw new Error("Invalid prompt content");
  }

  // Content moderation check
  const moderation = await openai.moderations.create({
    input: prompt
  });

  if (moderation.results[0].flagged) {
    throw new Error("Prompt violates content policy");
  }

  // Generate video
  return await openai.videos.generate({
    model: "sora-2",
    prompt
  });
}

function isValidPrompt(prompt) {
  return prompt.length > 10 && prompt.length < 1000;
}

Conclusion

You now have a comprehensive understanding of the Sora 2 API, including:

βœ… Authentication and setup βœ… Basic and advanced generation parameters βœ… Async handling with polling and webhooks βœ… Error handling and retry logic βœ… Rate limiting and optimization βœ… Production best practices βœ… Real-world integration examples βœ… Security considerations

Next Steps

  1. Experiment with the API in a development environment
  2. Build a small proof-of-concept application
  3. Optimize for your specific use case
  4. Scale to production with proper monitoring
  5. Stay Updated with API changes and new features

Additional Resources


Last Updated: October 2025 Author: Sora2Everything Team Reading Time: 18 minutes

Related Tutorials

Understanding Sora 2 Prompt Engineering

Learn how to craft effective prompts for better AI video generation results.

Optimizing Video Quality in Sora 2

Tips and tricks to maximize video quality and reduce generation time.

Sora 2 API Documentation: Complete Integration Guide 2025