Repository Public
1# Video generation with Sora23> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.45## Overview67Sora is OpenAI’s newest frontier in generative media – a state-of-the-art video model capable of creating richly detailed, dynamic clips with audio from natural language or images. Built on years of research into multimodal diffusion and trained on diverse visual data, Sora brings a deep understanding of 3D space, motion, and scene continuity to text-to-video generation.89The [Videos API](https://developers.openai.com/api/reference/resources/videos) exposes these capabilities to developers for the first time, enabling programmatic creation, extension, editing, and management of videos.1011You can use it to:1213- Create new videos from prompts.14- Guide a generation with an image reference.15- Reuse character assets across multiple generations for stronger visual consistency.16- Continue a completed clip with video extensions.17- Edit an existing video with targeted changes.18- Download finished videos and supporting assets.19- Submit large offline render queues through the [Batch API](https://developers.openai.com/api/docs/guides/batch).2021## Models2223The second generation Sora model comes in two variants, each tailored for different use cases.2425### Sora 22627`sora-2` is designed for **speed and flexibility**. It’s ideal for the exploration phase, when you’re experimenting with tone, structure, or visual style and need quick feedback rather than perfect fidelity.2829It generates good quality results quickly, making it well suited for rapid iteration, concepting, and rough cuts. `sora-2` is often more than sufficient for social media content, prototypes, and scenarios where turnaround time matters more than ultra-high fidelity.3031### Sora 2 Pro3233`sora-2-pro` produces higher quality results. It’s the better choice when you need **production-quality output**.3435`sora-2-pro` takes longer to render and is more expensive to run, but it produces more polished, stable results. It’s best for high-resolution cinematic footage, marketing assets, and any situation where visual precision is critical.3637Use `sora-2-pro` when you need 1080p exports in `1920x1080` or `1080x1920`.3839Both `sora-2` and `sora-2-pro` support `16`- and `20`-second generations.4041## Generate a video4243Generating a video is an **asynchronous** process:44451. When you call the `POST /videos` endpoint, the API returns a job object with a job `id` and an initial `status`.46472. You can either poll the `GET /videos/{video_id}` endpoint until the status transitions to completed, or – for a more efficient approach – use webhooks (see the webhooks section below) to be notified automatically when the job finishes.48493. Once the job has reached the `completed` state you can fetch the final MP4 file with `GET /videos/{video_id}/content`.5051### Start a render job5253Start by calling `POST /videos` with a text prompt and the required parameters. The prompt defines the creative look and feel – subjects, camera, lighting, and motion – while parameters like `size` and `seconds` control the video's resolution and length.5455Create a video5657```javascript58import OpenAI from "openai";5960const openai = new OpenAI();6162let video = await openai.videos.create({63 model: "sora-2",64 prompt: "A video of the words 'Thank you' in sparkling letters",65});6667console.log("Video generation started: ", video);68```6970```python71from openai import OpenAI7273openai = OpenAI()7475video = openai.videos.create(76 model="sora-2",77 prompt="A video of a cool cat on a motorcycle in the night",78)7980print("Video generation started:", video)81```8283```go84package main8586import (87 "context"88 "fmt"8990 "github.com/openai/openai-go/v3"91)9293func main() {94 client := openai.NewClient()95 video, err := client.Videos.New(context.Background(), openai.VideoNewParams{96 Model: openai.VideoModelSora2,97 Prompt: "A video of the words 'Thank you' in sparkling letters",98 })99 if err != nil {100 panic(err)101 }102 fmt.Println("Video generation started:", video)103}104```105106```java107import com.openai.client.OpenAIClient;108import com.openai.client.okhttp.OpenAIOkHttpClient;109import com.openai.models.videos.VideoCreateParams;110111var video =112 client113 .videos()114 .create(115 VideoCreateParams.builder()116 .model("sora-2")117 .prompt("A paper airplane flying over a forest")118 .build());119120System.out.println(video.id());121```122123```ruby124require "openai"125126client = OpenAI::Client.new127video = client.videos.create(model: "sora-2", prompt: "A paper airplane flying over a forest")128puts(video.id)129```130131```bash132curl -X POST "https://api.openai.com/v1/videos" \133 -H "Authorization: Bearer $OPENAI_API_KEY" \134 -H "Content-Type: multipart/form-data" \135 -F prompt="Wide tracking shot of a teal coupe driving through a desert highway, heat ripples visible, hard sun overhead." \136 -F model="sora-2-pro" \137 -F size="1280x720" \138 -F seconds="8" \139```140141142The response is a JSON object with a unique id and an initial status such as `queued` or `in_progress`. This means the render job has started.143144```shell145{146 "id": "video_68d7512d07848190b3e45da0ecbebcde004da08e1e0678d5",147 "object": "video",148 "created_at": 1758941485,149 "status": "queued",150 "model": "sora-2-pro",151 "progress": 0,152 "seconds": "8",153 "size": "1280x720"154}155```156157### Choose size and duration158159Pick the smallest format that meets your production needs:160161- Use shorter clips when you are iterating on prompt, motion, or composition.162- Generate videos up to `20` seconds when you need longer beats, fuller scenes, or fuller spots.163- Use `sora-2-pro` for higher-resolution exports in `1920x1080` or `1080x1920`.164165Longer durations and 1080p jobs can take materially longer to complete than short 720p or 480p renders, so plan for higher latency in user-facing flows.166167### Guardrails and restrictions168169The API enforces several content restrictions:170171- Only content suitable for audiences under 18 (a setting to bypass this restriction will be available in the future).172- Copyrighted characters and copyrighted music will be rejected.173- Real people—including public figures—cannot be generated.174- Character uploads that depict human likeness are blocked by default.175- Input images with faces of humans are currently rejected.176177Make sure prompts, reference images, and transcripts respect these rules to avoid failed generations.178179### Effective prompting180181For best results, describe **shot type, subject, action, setting, and lighting**. For example:182183- _“Wide shot of a child flying a red kite in a grassy park, golden hour sunlight, camera slowly pans upward.”_184- _“Close-up of a steaming coffee cup on a wooden table, morning light through blinds, soft depth of field.”_185186This level of specificity helps the model produce consistent results without inventing unwanted details. For more advanced prompting techniques, please refer to our dedicated Sora 2 [prompting guide](https://developers.openai.com/cookbook/examples/sora/sora2_prompting_guide).187188### Monitor progress189190Video generation takes time. Depending on model, API load and resolution, **a single render may take several minutes**.191192To manage this efficiently, you can poll the API to request status updates or you can get notified via a webhook.193194#### Poll the status endpoint195196Call `GET /videos/{video_id}` with the id returned from the create call. The response shows the job’s current status, progress percentage (if available), and any errors.197198Typical states are `queued`, `in_progress`, `completed`, and `failed`. Poll at a reasonable interval (for example, every 10–20 seconds), use exponential backoff if necessary, and provide feedback to users that the job is still in progress.199200Poll the status endpoint201202```javascript203import OpenAI from "openai";204import { setTimeout as sleep } from "node:timers/promises";205206const openai = new OpenAI();207208async function main() {209 let video = await openai.videos.create({210 model: "sora-2",211 prompt: "A video of the words 'Thank you' in sparkling letters",212 });213214 while (video.status === "queued" || video.status === "in_progress") {215 await sleep(2000);216 video = await openai.videos.retrieve(video.id);217 }218219 if (video.status === "completed") {220 console.log("Video successfully completed: ", video);221 } else {222 console.log("Video creation failed. Status: ", video.status);223 }224}225226main();227```228229```python230import asyncio231232from openai import AsyncOpenAI233234client = AsyncOpenAI()235236237async def main() -> None:238 video = await client.videos.create_and_poll(239 model="sora-2",240 prompt="A video of a cat on a motorcycle",241 )242243 if video.status == "completed":244 print("Video successfully completed: ", video)245 else:246 print("Video creation failed. Status: ", video.status)247248249asyncio.run(main())250```251252```go253package main254255import (256 "context"257 "fmt"258259 "github.com/openai/openai-go/v3"260)261262func main() {263 client := openai.NewClient()264 video, err := client.Videos.NewAndPoll(context.Background(), openai.VideoNewParams{265 Model: openai.VideoModelSora2,266 Prompt: "A video of the words 'Thank you' in sparkling letters",267 }, 2000)268 if err != nil {269 panic(err)270 }271 if video.Status == openai.VideoStatusCompleted {272 fmt.Println("Video successfully completed:", video)273 return274 }275 fmt.Println("Video creation failed. Status:", video.Status)276}277```278279```java280import com.openai.client.OpenAIClient;281import com.openai.client.okhttp.OpenAIOkHttpClient;282import com.openai.models.videos.Video;283import com.openai.models.videos.VideoCreateParams;284285var video =286 client287 .videos()288 .create(289 VideoCreateParams.builder()290 .model("sora-2")291 .prompt("A paper airplane flying over a forest")292 .build());293294while (video.status().equals(Video.Status.QUEUED)295 || video.status().equals(Video.Status.IN_PROGRESS)) {296 Thread.sleep(1000);297 video = client.videos().retrieve(video.id());298}299if (!video.status().equals(Video.Status.COMPLETED)) {300 throw new IllegalStateException("Video generation failed: " + video.status());301}302System.out.println("Video completed: " + video.id());303```304305```ruby306require "openai"307308client = OpenAI::Client.new309video = client.videos.create(model: "sora-2", prompt: "A paper airplane flying over a forest")310311while [:queued, :in_progress].include?(video.status)312 sleep(2)313 video = client.videos.retrieve(video.id)314end315316unless video.status == OpenAI::Models::Video::Status::COMPLETED317 raise "Video creation failed. Status: #{video.status}"318end319320puts("Video successfully completed: #{video.id}")321```322323324Response example:325326```shell327{328 "id": "video_68d7512d07848190b3e45da0ecbebcde004da08e1e0678d5",329 "object": "video",330 "created_at": 1758941485,331 "status": "in_progress",332 "model": "sora-2-pro",333 "progress": 33,334 "seconds": "8",335 "size": "1280x720"336}337```338339#### Use webhooks for notifications340341Instead of polling job status repeatedly with `GET`, register a [webhook](https://developers.openai.com/api/docs/guides/webhooks) to be notified automatically when a video generation completes or fails.342343Webhooks can be configured in your [webhook settings page](https://platform.openai.com/settings/project/webhooks). When a job finishes, the API emits one of two event types: `video.completed` and `video.failed`. Each event includes the ID of the job that triggered it.344345Example webhook payload:346347```348{349 "id": "evt_abc123",350 "object": "event",351 "created_at": 1758941485,352 "type": "video.completed", // or "video.failed"353 "data": {354 "id": "video_abc123"355 }356}357```358359### Retrieve results360361#### Download the MP4362363Once the job reaches status `completed`, fetch the MP4 with `GET /videos/{video_id}/content`. This endpoint streams the binary video data and returns standard content headers, so you can either save the file directly to disk or pipe it to cloud storage.364365Download the MP4366367```javascript368import { writeFileSync } from "node:fs";369370import OpenAI from "openai";371372const openai = new OpenAI();373374let video = await openai.videos.create({375 model: "sora-2",376 prompt: "A video of the words 'Thank you' in sparkling letters",377});378379console.log("Video generation started: ", video);380let progress = video.progress ?? 0;381382while (video.status === "in_progress" || video.status === "queued") {383 video = await openai.videos.retrieve(video.id);384 progress = video.progress ?? 0;385386 // Display progress bar387 const barLength = 30;388 const filledLength = Math.floor((progress / 100) * barLength);389 // Simple ASCII progress visualization for terminal output390 const bar = "=".repeat(filledLength) + "-".repeat(barLength - filledLength);391 const statusText = video.status === "queued" ? "Queued" : "Processing";392393 process.stdout.write(`${statusText}: [${bar}] ${progress.toFixed(1)}%`);394395 await new Promise((resolve) => setTimeout(resolve, 2000));396}397398// Clear the progress line and show completion399process.stdout.write("\n");400401if (video.status === "failed") {402 throw new Error("Video generation failed");403}404405console.log("Video generation completed: ", video);406407console.log("Downloading video content...");408409const content = await openai.videos.downloadContent(video.id);410411const body = content.arrayBuffer();412const buffer = Buffer.from(await body);413414writeFileSync("video.mp4", buffer);415416console.log("Wrote video.mp4");417```418419```python420from openai import OpenAI421import sys422import time423424425openai = OpenAI()426427video = openai.videos.create(428 model="sora-2",429 prompt="A video of a cool cat on a motorcycle in the night",430)431432print("Video generation started:", video)433434progress = getattr(video, "progress", 0)435bar_length = 30436437while video.status in ("in_progress", "queued"):438 # Refresh status439 video = openai.videos.retrieve(video.id)440 progress = getattr(video, "progress", 0)441442 filled_length = int((progress / 100) * bar_length)443 bar = "=" * filled_length + "-" * (bar_length - filled_length)444 status_text = "Queued" if video.status == "queued" else "Processing"445446 sys.stdout.write(f"\r{status_text}: [{bar}] {progress:.1f}%")447 sys.stdout.flush()448 time.sleep(2)449450# Move to next line after progress loop451sys.stdout.write("\n")452453if video.status == "failed":454 message = getattr(455 getattr(video, "error", None), "message", "Video generation failed"456 )457 raise RuntimeError(message)458459print("Video generation completed:", video)460print("Downloading video content...")461462content = openai.videos.download_content(video.id, variant="video")463content.write_to_file("video.mp4")464465print("Wrote video.mp4")466```467468```go469package main470471import (472 "context"473 "fmt"474 "io"475 "os"476477 "github.com/openai/openai-go/v3"478)479480func main() {481 client := openai.NewClient()482 video, err := client.Videos.NewAndPoll(context.Background(), openai.VideoNewParams{483 Model: openai.VideoModelSora2,484 Prompt: "A video of the words 'Thank you' in sparkling letters",485 }, 2000)486 if err != nil {487 panic(err)488 }489 if video.Status != openai.VideoStatusCompleted {490 panic(fmt.Errorf("video generation failed with status %s", video.Status))491 }492493 response, err := client.Videos.DownloadContent(context.Background(), video.ID, openai.VideoDownloadContentParams{})494 if err != nil {495 panic(err)496 }497 defer response.Body.Close()498 file, err := os.Create("video.mp4")499 if err != nil {500 panic(err)501 }502 if _, err := io.Copy(file, response.Body); err != nil {503 panic(err)504 }505 if err := file.Close(); err != nil {506 panic(err)507 }508 fmt.Println("Wrote video.mp4")509}510```511512```java513import com.openai.client.OpenAIClient;514import com.openai.client.okhttp.OpenAIOkHttpClient;515import com.openai.models.videos.Video;516import com.openai.models.videos.VideoCreateParams;517import java.nio.file.Files;518import java.nio.file.Path;519import java.nio.file.StandardCopyOption;520521var video =522 client523 .videos()524 .create(525 VideoCreateParams.builder()526 .model("sora-2")527 .prompt("A video of the words 'Thank you' in sparkling letters")528 .build());529530while (video.status().equals(Video.Status.QUEUED)531 || video.status().equals(Video.Status.IN_PROGRESS)) {532 Thread.sleep(1000);533 video = client.videos().retrieve(video.id());534}535if (!video.status().equals(Video.Status.COMPLETED)) {536 throw new IllegalStateException("Video generation failed: " + video.status());537}538try (var content = client.videos().downloadContent(video.id())) {539 Files.copy(content.body(), Path.of("video.mp4"), StandardCopyOption.REPLACE_EXISTING);540}541System.out.println("Wrote video.mp4");542```543544```ruby545require "openai"546547client = OpenAI::Client.new548video = client.videos.create(549 model: "sora-2",550 prompt: "A video of the words 'Thank you' in sparkling letters"551)552pending_statuses = [553 OpenAI::Models::Video::Status::QUEUED,554 OpenAI::Models::Video::Status::IN_PROGRESS555]556while pending_statuses.include?(video.status)557 sleep(2)558 video = client.videos.retrieve(video.id)559end560raise "Video generation failed" if video.status == OpenAI::Models::Video::Status::FAILED561562content = client.videos.download_content(video.id)563File.binwrite("video.mp4", content.read)564puts("Wrote video.mp4")565```566567```bash568curl -L "https://api.openai.com/v1/videos/video_abc123/content" \569 -H "Authorization: Bearer $OPENAI_API_KEY" \570 --output video.mp4571```572573574You now have the final video file ready for playback, editing, or distribution. Download URLs are valid for a maximum of 1 hour after generation. If you need long-term storage, copy the file to your own storage system promptly.575576#### Download supporting assets577578For each completed video, you can also download a **thumbnail** and a **spritesheet**. These are lightweight assets useful for previews, scrubbers, or catalog displays. Use the `variant` query parameter to specify what you want to download. The default is `variant=video` for the MP4.579580```bash581# Download a thumbnail582curl -L "https://api.openai.com/v1/videos/video_abc123/content?variant=thumbnail" \583 -H "Authorization: Bearer $OPENAI_API_KEY" \584 --output thumbnail.webp585586# Download a spritesheet587curl -L "https://api.openai.com/v1/videos/video_abc123/content?variant=spritesheet" \588 -H "Authorization: Bearer $OPENAI_API_KEY" \589 --output spritesheet.jpg590```591592593## Use image references594595You can guide a generation with an input image, which acts as **the first frame of your video**. This is useful if you need the output video to preserve the look of a brand asset, a character, or a specific environment.596597Choose the `input_reference` format based on the request type:598599- Use `input_reference` with an uploaded image in `multipart/form-data` requests.600- Use `input_reference` with a JSON object in `application/json` requests, including Batch. The JSON form accepts either `file_id` or `image_url`.601602The image must match the target video's resolution (`size`).603604Supported file formats are `image/jpeg`, `image/png`, and `image/webp`.605606```bash607curl -X POST "https://api.openai.com/v1/videos" \608 -H "Authorization: Bearer $OPENAI_API_KEY" \609 -H "Content-Type: multipart/form-data" \610 -F prompt="She turns around and smiles, then slowly walks out of the frame." \611 -F model="sora-2-pro" \612 -F size="1280x720" \613 -F seconds="8" \614 -F input_reference="@sample_720p.jpeg;type=image/jpeg"615```616617618| Input image generated with [OpenAI GPT Image](https://developers.openai.com/api/docs/guides/image-generation) | Generated video using Sora 2 (converted to GIF) |619| :---------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------: |620| ![][sora_woman_skyline_original][Download this image](https://cdn.openai.com/API/docs/images/sora/woman_skyline_original_720p.jpeg) | ![][sora_woman_skyline_video] Prompt: _“She turns around and smiles, then slowly walks out of the frame.”_ |621| ![][sora_monster_original_jpeg][Download this image](https://cdn.openai.com/API/docs/images/sora/monster_original_720p.jpeg) | ![][sora_monster_original_gif] Prompt: _“The fridge door opens. A cute, chubby purple monster comes out of it.”_ |622623## Use characters for consistency624625Characters let you upload a reusable non-human subject and reference it across multiple generations. This is useful when you want an animal, mascot, or object to keep the same core appearance, styling, and screen presence across several shots.626627Character uploads currently work best with short `2`- to `4`-second clips in628 `16:9` or `9:16`, at `720p` to `1080p`. Character source videos work best when629 they match the aspect ratio of the requested output. If the aspect ratios630 differ, the character can appear stretched or distorted. A single video can631 include up to two characters.632633Characters are different from `input_reference`. An image reference conditions634the opening frame of a single generation, while a character asset can be reused635across future video requests.636637Create the character by uploading a short MP4 clip to `POST /v1/videos/characters`, then include the returned character ID in the `characters` array when you create a video.638639Character uploads that depict human likeness are blocked by default. Contact640 your account manager or [reach out to our sales641 team](https://openai.com/contact-sales/) to learn more about eligibility for642 human-likeness access.643644```bash645curl -X POST "https://api.openai.com/v1/videos/characters" \646 -H "Authorization: Bearer $OPENAI_API_KEY" \647 -H "Content-Type: multipart/form-data" \648 -F "video=@character.mp4;type=video/mp4" \649 -F "name=Mossy"650```651652653Mention the character name verbatim in your prompt. Passing the character ID654alone isn't enough to reliably preserve the character in the shot.655656Characters can be combined with `input_reference`. Extensions don't support657characters.658659```bash660curl -X POST "https://api.openai.com/v1/videos" \661 -H "Authorization: Bearer $OPENAI_API_KEY" \662 -H "Content-Type: application/json" \663 -d '{664 "model": "sora-2",665 "prompt": "A cinematic tracking shot of Mossy, a moss-covered teapot mascot, weaving through a lantern-lit market at dusk.",666 "size": "1280x720",667 "seconds": "8",668 "characters": [669 { "id": "char_123" }670 ]671 }'672```673674675## Extend completed videos676677Video extensions let you continue an existing completed video and create a new stitched result. Provide the source video in the `video` field to `POST /v1/videos/extensions`, add a prompt describing how the scene should continue, and the API generates the next segment using the full source clip as context.678679Use extensions when you want to preserve motion, camera direction, and scene continuity. If you only need to control the opening frame of a new generation, use `input_reference` instead.680681Each extension can add up to `20` seconds. A single video can be extended up682 to six times, for a maximum total length of `120` seconds. Extensions683 currently accept only a source video and prompt. They don't support characters684 or image references.685686```bash687curl -X POST "https://api.openai.com/v1/videos/extensions" \688 -H "Authorization: Bearer $OPENAI_API_KEY" \689 -H "Content-Type: application/json" \690 -d '{691 "video": {692 "id": "video_abc123"693 },694 "prompt": "Continue the scene as the camera rises over the rooftops and reveals the sunrise.",695 "seconds": "8"696 }'697```698699700## Edit existing videos701702Editing lets you take an existing video and make targeted adjustments without regenerating everything from scratch. Send `POST /v1/videos/edits` with a prompt and a `video` reference, and the system reuses the original structure, continuity, and composition while applying the modification. This works best when you make a single, well-defined change because smaller, focused edits preserve more of the original fidelity and reduce the risk of introducing artifacts.703704Video generations could previously be edited using the remix endpoint, which705 is being deprecated. Use the edits endpoint for new integrations.706707The `video` field accepts either a video ID or an uploaded video. If you pass a708video ID, the API infers the model from the source video.709710Editing uploaded videos is only available to eligible customers. Contact your711 account manager or [reach out to our sales712 team](https://openai.com/contact-sales/) if you need this workflow.713714```bash715curl -X POST "https://api.openai.com/v1/videos/edits" \716 -H "Authorization: Bearer $OPENAI_API_KEY" \717 -H "Content-Type: application/json" \718 -d '{719 "video": {720 "id": "video_abc123"721 },722 "prompt": "Shift the color palette to teal, sand, and rust, with a warm backlight."723 }'724```725726727If you upload a new video instead of editing an existing generation, set728`model` explicitly in the request.729730```bash731curl -X POST "https://api.openai.com/v1/videos/edits" \732 -H "Authorization: Bearer $OPENAI_API_KEY" \733 -H "Content-Type: multipart/form-data" \734 -F "video=@source.mp4;type=video/mp4" \735 -F "model=sora-2-pro" \736 -F "prompt=Shift the color palette to teal, sand, and rust, with a warm backlight."737```738739740Editing is especially valuable for iteration because it lets you refine without discarding what already works. By constraining each edit to one clear adjustment, you keep the visual style, subject consistency, and camera framing stable, while still exploring variations in mood, palette, or staging. This makes it far easier to build polished sequences through small, reliable steps.741742| Original video | Edited generated video |743| :----------------------------: | :-----------------------------------------------------------------------------: |744| ![][sora_monster_original_gif] | ![][sora_monster_orange] Prompt: _“Change the color of the monster to orange.”_ |745| ![][sora_monster_original_gif] | ![][sora_monster_2monsters] Prompt: _“A second monster comes out right after.”_ |746747## Run video jobs through the Batch API748749Use the [Batch API](https://developers.openai.com/api/docs/guides/batch) when you need to queue many video renders for offline processing, review pipelines, or studio workflows. Each line in the batch input file uses the same JSON request body you would send to `POST /v1/videos`, which makes it a good fit for shot lists and scheduled render queues.750751For video generation in Batch:752753- Batch currently supports `POST /v1/videos` only.754- Batch requests must use JSON, not multipart.755- Upload assets ahead of time and reference them from the JSON request body.756- Use `input_reference` for image-guided generations in Batch. In JSON requests, pass `input_reference` as an object with either `file_id` or `image_url`.757- Multipart `input_reference` uploads, including video reference inputs, aren't supported in Batch.758- Batch-generated videos are available for download for up to `24` hours after the batch completes.759760```jsonl761{"custom_id":"shot-001","method":"POST","url":"/v1/videos","body":{"model":"sora-2-pro","prompt":"Slow dolly shot through a miniature paper city at blue hour, soft fog, practical window lights flickering on.","size":"1920x1080","seconds":"20"}}762{"custom_id":"shot-002","method":"POST","url":"/v1/videos","body":{"model":"sora-2-pro","prompt":"Portrait close-up of a red panda chef plating noodles in a stainless-steel kitchen, shallow depth of field.","size":"1080x1920","seconds":"16"}}763```764765When a batch reaches `completed`, the video jobs in its output have already reached a terminal state such as `completed`, `failed`, or `expired`. Use stable `custom_id` values so you can map batch results back to your internal shot IDs, editorial queue, or asset pipeline, then download final assets with the returned video IDs.766767## Maintain your library768769Use `GET /videos` to enumerate your videos. The endpoint supports optional query parameters for pagination and sorting.770771```bash772curl "https://api.openai.com/v1/videos?limit=20&after=video_123&order=asc" \773 -H "Authorization: Bearer $OPENAI_API_KEY" | jq .774```775776777Use `DELETE /videos/{video_id}` to remove videos you no longer need from OpenAI’s storage.778779```bash780curl -X DELETE "https://api.openai.com/v1/videos/REPLACE_WITH_YOUR_VIDEO_ID" \781 -H "Authorization: Bearer $OPENAI_API_KEY" | jq .782```783784785[sora_woman_skyline_original]: https://cdn.openai.com/API/docs/images/sora/sora_woman_skyline_original_2.jpeg786[sora_woman_skyline_video]: https://cdn.openai.com/API/docs/images/sora/sora_woman_skyline_video.gif787[sora_monster_original_jpeg]: https://cdn.openai.com/API/docs/images/sora/sora_monster_original_2.jpeg788[sora_monster_original_gif]: https://cdn.openai.com/API/docs/images/sora/sora_monster_original.gif789[sora_monster_orange]: https://cdn.openai.com/API/docs/images/sora/sora_monster_orange.gif790[sora_monster_2monsters]: https://cdn.openai.com/API/docs/images/sora/sora_monster_2monsters.gif