Get Project
curl --request GET \
--url https://app.nouvel.ai/api/v1/projects/{projectId}import requests
url = "https://app.nouvel.ai/api/v1/projects/{projectId}"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://app.nouvel.ai/api/v1/projects/{projectId}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.nouvel.ai/api/v1/projects/{projectId}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.nouvel.ai/api/v1/projects/{projectId}"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://app.nouvel.ai/api/v1/projects/{projectId}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.nouvel.ai/api/v1/projects/{projectId}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_body{
"project": {
"id": "<string>",
"title": "<string>",
"description": {},
"status": "<string>",
"final_output_url": {},
"aspect_ratio": {},
"target_duration_seconds": {},
"product_url": {},
"quality": {},
"tone": {},
"energy": {},
"pacing": {},
"caption_style": {},
"content_mode": {},
"created_at": "<string>",
"updated_at": "<string>"
},
"scenes": [
{
"id": "<string>",
"scene_order": 123,
"prompt": {},
"script_text": {},
"output_url": {},
"status": "<string>",
"avatar_id": {},
"duration_seconds": {},
"created_at": "<string>",
"updated_at": "<string>"
}
],
"404 Not Found": {},
"401 Unauthorized": {}
}Projects
Get Project
Retrieve detailed information about a specific project including its scenes
GET
/
api
/
v1
/
projects
/
{projectId}
Get Project
curl --request GET \
--url https://app.nouvel.ai/api/v1/projects/{projectId}import requests
url = "https://app.nouvel.ai/api/v1/projects/{projectId}"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://app.nouvel.ai/api/v1/projects/{projectId}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.nouvel.ai/api/v1/projects/{projectId}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.nouvel.ai/api/v1/projects/{projectId}"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://app.nouvel.ai/api/v1/projects/{projectId}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.nouvel.ai/api/v1/projects/{projectId}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_body{
"project": {
"id": "<string>",
"title": "<string>",
"description": {},
"status": "<string>",
"final_output_url": {},
"aspect_ratio": {},
"target_duration_seconds": {},
"product_url": {},
"quality": {},
"tone": {},
"energy": {},
"pacing": {},
"caption_style": {},
"content_mode": {},
"created_at": "<string>",
"updated_at": "<string>"
},
"scenes": [
{
"id": "<string>",
"scene_order": 123,
"prompt": {},
"script_text": {},
"output_url": {},
"status": "<string>",
"avatar_id": {},
"duration_seconds": {},
"created_at": "<string>",
"updated_at": "<string>"
}
],
"404 Not Found": {},
"401 Unauthorized": {}
}Returns comprehensive details about a single video ad project, including all individual scenes, scripts, and generation metadata. Use this endpoint to track project progress or retrieve the final video and scene-level information.
Authentication
This endpoint requires a valid API key with theprojects:read permission.
Authorization: Bearer nvl_xxxx
Path Parameters
string
required
The unique identifier (UUID) of the project to retrieve. Must be a valid UUID belonging to the authenticated user.
Response
object
Complete project details including all configuration and output URLs.
Show Project properties
Show Project properties
string
Unique project identifier (UUID format).
string
Human-readable project title.
string | null
AI-generated description of the video ad concept. May be
null for projects still in generation.string
Current project status. One of:
generating, post_processing, stitching, completed, failed, partial.string | null
HTTPS URL to the final stitched video (MP4) with voiceover, lip sync, and captions. Only available when status is
completed.string | null
Video aspect ratio. Common values:
"9:16" (vertical/mobile), "1:1" (square), "16:9" (horizontal/desktop).integer | null
Target video duration in seconds. Actual duration may vary by ±1 second.
string | null
Original product URL provided when creating the project.
string | null
Video quality setting used for generation. Values:
"standard", "high".string | null
Ad tone/personality. Examples:
"professional", "conversational", "energetic".string | null
Ad energy level. Values:
"low", "moderate", "high".string | null
Ad pacing style. Values:
"slow", "moderate", "fast".string | null
Caption styling preset applied to the final video.
string | null
Content generation mode used. Values:
"auto", "custom".string
ISO 8601 timestamp when the project was created.
string
ISO 8601 timestamp when the project was last modified. Updates frequently during generation.
array
Ordered list of individual scenes that make up the final video. Scenes are returned in playback order.
Show Scene object properties
Show Scene object properties
string
Unique scene identifier (UUID format).
integer
Zero-indexed position of this scene in the final video. Scene 0 plays first.
string | null
Visual description prompt used to generate this scene’s video. May be
null if generation hasn’t started.string | null
Voiceover script spoken during this scene. Used for text-to-speech and caption generation.
string | null
HTTPS URL to the individual scene video clip (before final stitching). Only available when scene generation is complete.
string
Scene-level generation status. Common values:
"pending", "generating", "completed", "failed".string | null
Identifier of the actor/avatar used in this scene. Examples:
"max", "iris".number | null
Actual duration of this scene in seconds. May differ slightly from target.
string
ISO 8601 timestamp when the scene was created.
string
ISO 8601 timestamp when the scene was last modified.
Example Requests
curl https://app.nouvel.ai/api/v1/projects/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer nvl_xxxx"
const projectId = '550e8400-e29b-41d4-a716-446655440000';
const response = await fetch(
`https://app.nouvel.ai/api/v1/projects/${projectId}`,
{
headers: {
'Authorization': 'Bearer nvl_xxxx'
}
}
);
const data = await response.json();
console.log(`Project: ${data.project.title}`);
console.log(`Status: ${data.project.status}`);
console.log(`Scenes: ${data.scenes.length}`);
const response = await fetch(
`https://app.nouvel.ai/api/v1/projects/${projectId}`,
{
headers: {
'Authorization': 'Bearer nvl_xxxx'
}
}
);
const data = await response.json();
// Get full voiceover script in order
const fullScript = data.scenes
.sort((a, b) => a.scene_order - b.scene_order)
.map(scene => scene.script_text)
.filter(Boolean)
.join(' ');
console.log('Full Voiceover Script:', fullScript);
const response = await fetch(
`https://app.nouvel.ai/api/v1/projects/${projectId}`,
{
headers: {
'Authorization': 'Bearer nvl_xxxx'
}
}
);
const data = await response.json();
if (data.project.status === 'completed' && data.project.final_output_url) {
// Download the final video
const videoResponse = await fetch(data.project.final_output_url);
const videoBlob = await videoResponse.blob();
// Save to file (Node.js)
const fs = require('fs');
const buffer = Buffer.from(await videoBlob.arrayBuffer());
fs.writeFileSync(`${data.project.title}.mp4`, buffer);
} else {
console.log(`Project not ready. Status: ${data.project.status}`);
}
import requests
project_id = '550e8400-e29b-41d4-a716-446655440000'
response = requests.get(
f'https://app.nouvel.ai/api/v1/projects/{project_id}',
headers={'Authorization': 'Bearer nvl_xxxx'}
)
data = response.json()
print(f"Project: {data['project']['title']}")
print(f"Status: {data['project']['status']}")
print(f"Scenes: {len(data['scenes'])}")
import requests
response = requests.get(
f'https://app.nouvel.ai/api/v1/projects/{project_id}',
headers={'Authorization': 'Bearer nvl_xxxx'}
)
data = response.json()
# Get full voiceover script in order
scenes = sorted(data['scenes'], key=lambda s: s['scene_order'])
full_script = ' '.join(
scene['script_text']
for scene in scenes
if scene['script_text']
)
print('Full Voiceover Script:', full_script)
import requests
response = requests.get(
f'https://app.nouvel.ai/api/v1/projects/{project_id}',
headers={'Authorization': 'Bearer nvl_xxxx'}
)
data = response.json()
project = data['project']
if project['status'] == 'completed' and project['final_output_url']:
# Download the final video
video_response = requests.get(project['final_output_url'])
filename = f"{project['title']}.mp4"
with open(filename, 'wb') as f:
f.write(video_response.content)
print(f"Downloaded: {filename}")
else:
print(f"Project not ready. Status: {project['status']}")
Example Response
{
"project": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "Premium Collagen Ad - Vertical",
"description": "A compelling 15-second ad showcasing the benefits of marine collagen supplement with professional spokesperson and product demo.",
"status": "completed",
"final_output_url": "https://app.nouvel.ai/api/cdn/videos/final-550e8400.mp4",
"aspect_ratio": "9:16",
"target_duration_seconds": 15,
"product_url": "https://example.com/products/collagen",
"quality": "high",
"tone": "professional",
"energy": "moderate",
"pacing": "moderate",
"caption_style": "modern",
"content_mode": "auto",
"created_at": "2026-03-15T14:32:10.000Z",
"updated_at": "2026-03-15T14:48:22.000Z"
},
"scenes": [
{
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"scene_order": 0,
"prompt": "Professional female spokesperson in modern bright studio, upright posture, direct eye contact with camera, composed confident expression, medium shot showing full shoulders, neutral warm lighting",
"script_text": "Your skin loses collagen every single day. But what if you could restore it naturally?",
"output_url": "https://app.nouvel.ai/api/cdn/videos/scene-7c9e6679.mp4",
"status": "completed",
"avatar_id": "iris",
"duration_seconds": 5.2,
"created_at": "2026-03-15T14:32:11.000Z",
"updated_at": "2026-03-15T14:38:45.000Z"
},
{
"id": "8d1f7789-8536-51ef-a55c-f18gd2g01bf8",
"scene_order": 1,
"prompt": "Close-up of hands pouring marine collagen powder into clear glass of water on modern kitchen counter, natural morning light, product packaging visible in background",
"script_text": "Our marine collagen is clinically proven to boost skin elasticity by up to 40% in just 8 weeks.",
"output_url": "https://app.nouvel.ai/api/cdn/videos/scene-8d1f7789.mp4",
"status": "completed",
"avatar_id": null,
"duration_seconds": 4.8,
"created_at": "2026-03-15T14:32:11.000Z",
"updated_at": "2026-03-15T14:39:12.000Z"
},
{
"id": "9e2g8899-9647-62fg-b66d-g29he3h12cg9",
"scene_order": 2,
"prompt": "Professional female spokesperson in modern bright studio, upright posture, direct eye contact with camera, warm engaged expression, medium shot showing full shoulders",
"script_text": "Join thousands of women who've transformed their skin. Try it risk-free today.",
"output_url": "https://app.nouvel.ai/api/cdn/videos/scene-9e2g8899.mp4",
"status": "completed",
"avatar_id": "iris",
"duration_seconds": 5.0,
"created_at": "2026-03-15T14:32:11.000Z",
"updated_at": "2026-03-15T14:40:03.000Z"
}
]
}
Error Responses
object
Returned when the project doesn’t exist or doesn’t belong to the authenticated user.
{
"error": "Project not found"
}
object
Returned when the API key is missing, invalid, or lacks the required permissions.
{
"error": "Unauthorized"
}
Notes
Security: Projects can only be accessed by their owner. API keys are scoped to individual user accounts, and attempting to access another user’s project will return a 404 error.
Scene URLs vs Final URL: The individual scene
output_url values point to raw scene clips before final processing. The project-level final_output_url is the fully processed video with continuous voiceover, lip sync, and captions applied. Always use final_output_url for production purposes.Polling for Completion: When monitoring project progress, poll this endpoint every 10 seconds until the
status field becomes completed or failed. The updated_at timestamp changes whenever generation progresses.Scene Order: Scenes are always returned in playback order sorted by
scene_order. The final video plays scene 0 first, then scene 1, etc.Avatar Scenes vs Product Scenes: Scenes with an
avatar_id feature a human spokesperson. Scenes without an avatar_id (null) are typically product demonstration shots or B-roll footage.Rate Limiting: This endpoint is rate-limited to 60 requests per minute per API key. See Rate Limits for details.

