Public REST API for AI property video generation — upload property photos, get back cinematic videos and reels.
Hypernova is liveNew
Our most powerful video model, now the default for POST /api/v1/videos. Send "model": "supernova" to use our previous model.
The PhotoAIVideo API v1 lets you turn property photos into cinematic videos programmatically, then merge them into branded reels. All endpoints are served from a single base URL.
All input image URLs must live on our storage (from /api/v1/presigned-urls) — external URLs are rejected.
https://app.photoaivideo.comAllowed durations (seconds)
456
Camera effects
dolly_zoompull_outpush_inaerial_dronelow_anglehigh_angleaerialzoom_inzoom_outtruck_lefttruck_rightpedestal_downpedestal_upcrane_downcrane_uppan_leftpan_rightorbit_leftorbit_right
Every request needs an API key, sent either way. Create keys from the API dashboard.
Authorization: Bearer <API_KEY># orx-api-key: <API_KEY>{ "error": "Invalid or missing API key" }402GET /api/v1/credits{ "error": "Not enough credits", "required": 1, "remaining": 0 }Pick the model per video with the model field of POST /api/v1/videos. Both models take the same inputs (prompt, first and optional last frame, effects, 4–6 second duration) and cost 1 credit per video. Every job shows the model that made it in its model field.
Changed on 2026-10-01: requests without model used to run on Supernova and now run on Hypernova. Send "model": "supernova" to keep the earlier behavior.
hypernovaNewdefault
Our most powerful video model
supernova
Our previous model
/api/v1/presigned-urlsGet signed upload URLs for your input images. Free — no credits.
countdefault 1number of URLs, 1–20 (query parameter)
Then upload each file with PUT <presignedUrl> and the raw image bytes as the body (Content-Type: image/jpeg or image/png). Use the matching publicUrl in the endpoints below.
{ "bucket": "api-uploads", "uploads": [ { "path": "<keyId>/<uuid>", "token": "...", "presignedUrl": "https://....supabase.co/storage/...", "publicUrl": "https://....supabase.co/storage/..." } ]}/api/v1/videosTurn one property photo into a cinematic video clip. Asynchronous — returns a jobId immediately; poll for the result below. Costs 1 credit.
promptrequireddescribes the scene/motion
imageUrlrequiredfirst frame; our storage only
endImageUrloptionaloptional last frame; our storage only
effectsdefault []camera-movement effect codes (see Getting started)
durationdefault 54, 5 or 6 seconds
modeldefault hypernovahypernova (our most powerful model) or supernova, see Models
The request below follows your choices.
Model
Length
Camera effects2 selected
{ "prompt": "smooth cinematic reveal of a modern living room", "imageUrl": "https://....supabase.co/storage/v1/object/public/api-uploads/...", "effects": ["push_in", "pan_left"], "duration": 5, "model": "hypernova"}{ "jobId": "uuid", "status": "pending", "model": "hypernova", "creditsCharged": 1 }/api/v1/videos/:jobIdvideo_url plays inline in a browser; download_url is the same file with a forced-download flag. Poll every few seconds until status is completed or failed. Failed jobs refund the credit automatically.
error_message is null unless the job failed, and then one of:
/api/v1/videos?limit=20&offset=0List this key's video jobs, newest first. Returns { jobs: [...], total, limit, offset }.
{ "job": { "id": "uuid", "model": "hypernova", "prompt": "smooth cinematic reveal of a modern living room", "image_url": "https://...", "end_image_url": null, "effects": ["push_in", "pan_left"], "duration": 5, "status": "pending | processing | completed | failed", "video_url": "https://...-or-null", "download_url": "https://...?download", "error_message": null, "credit_cost": 1, "created_at": "...", "processed_at": "..." }}/api/v1/reelsMerge 2–20 completed video jobs into one reel, in the order given, with optional background music, text overlay, ending screen and brand logo. Asynchronous — poll for the result below. Costs 1 credit.
videoJobIdsrequired2–20 completed job ids; order = clip order; duplicates allowed
orientationdefault landscapelandscape (1920x1080) | portrait (1080x1920)
musicTypedefault no_musicno_music | tropical | lofi | jazz
isFadingEnabledefault truefade in/out per clip
textOverlayoptionaltext on top of the video (see below)
endingoptionalclosing card appended after the last clip (see below)
logooptionalwatermark logo overlaid on the whole video (see below)
{ "videoJobIds": ["uuid-1", "uuid-2"], "musicType": "lofi", "orientation": "portrait", "textOverlay": { "text": "123 Main Street, Miami", "fontFamily": "Montserrat", "fontSize": 56, "bold": true, "position": { "x": 50, "y": 85 }, "duration": 5 }, "ending": { "text": "Contact us today!", "backgroundColor": "#1a1a2e", "duration": 3 }, "logo": { "url": "https://....supabase.co/storage/v1/object/public/api-uploads/.../logo.png", "scale": 15, "opacity": 90 }}{ "jobId": "uuid", "status": "pending", "creditsCharged": 1 }textOverlayobjecttextrequiredoverlay text, line breaks allowed
fontFamilydefault RobotoArial | Helvetica | Times New Roman | Roboto | Open Sans | Montserrat
fontSizedefault 4812–200 px
colordefault #fffffftext color, hex
backgroundColordefault #000000text box color, hex
backgroundOpacitydefault 400–100
bold / italicdefault falsepositiondefault {x: 50, y: 80}percent of frame — {x:0,y:0} top-left, {x:100,y:100} bottom-right
durationdefault whole videoseconds the text stays visible from the start
endingobjecttextrequiredending message
fontSizedefault 7212–200 px
colordefault #fffffftext color, hex
backgroundColordefault #000000card background, hex (ignored when backgroundImageUrl set)
backgroundImageUrloptionalcard background image; our storage only
durationdefault 31–10 seconds
logoobjecturlrequiredPNG recommended; our storage only
positiondefault {x: 95, y: 5}percent of frame (default = top-right)
scaledefault 15logo width as % of video width, 5–40
opacitydefault 1000–100
/api/v1/reels/:jobId/api/v1/reels?limit=20&offset=0List this key's reel jobs, newest first. Returns { jobs: [...], total, limit, offset }.
{ "job": { "id": "uuid", "video_job_ids": ["uuid-1", "uuid-2"], "orientation": "portrait", "music_type": "lofi", "is_fading_enable": true, "status": "pending | processing | completed | failed", "output_video_url": "https://...-or-null", "download_url": "https://...?download", "error_message": null, "credit_cost": 1, "created_at": "...", "processed_at": "..." }}/api/v1/credits{ "totalCredits": 100, "usedCredits": 12, "remainingCredits": 88}KEY="your-api-key"BASE="https://app.photoaivideo.com"# 1. get an upload URLcurl -s "$BASE/api/v1/presigned-urls?count=1" -H "Authorization: Bearer $KEY"# 2. upload the property photocurl -s -X PUT "<presignedUrl>" -H "Content-Type: image/jpeg" --data-binary @photo.jpg# 3. create the video jobcurl -s -X POST "$BASE/api/v1/videos" \ -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \ -d '{ "prompt": "cinematic reveal of a modern kitchen", "imageUrl": "<publicUrl>", "effects": ["push_in"], "duration": 5, "model": "hypernova" }'# 4. poll until completedcurl -s "$BASE/api/v1/videos/<jobId>" -H "Authorization: Bearer $KEY"# 5. merge completed clips into a reelcurl -s -X POST "$BASE/api/v1/reels" \ -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \ -d '{ "videoJobIds": ["<jobId1>", "<jobId2>"], "musicType": "lofi" }'# 6. poll the reelcurl -s "$BASE/api/v1/reels/<reelJobId>" -H "Authorization: Bearer $KEY"