Stager AI API · v1

Put your real estate photo editing on autopilot

Connect your own app, CRM or photography platform. Stager AI merges brackets, fixes lenses and verticals, stages rooms and builds listing videos, then hands the results back to you.

Start in 4 steps
Base URL
api.stagerai.com
Auth header
stager-api-key
Format
JSON over HTTPS
Price
$0.25 per photo
Quickstart

Your first edit in 4 steps

Every request goes to https://api.stagerai.com with your key in the stager-api-key header. That is all the setup there is.

  1. 01Get your keyAsk us in the chat. Send it as the stager-api-key header on every request.
  2. 02Create a projectOne project per listing keeps everything together.
  3. 03Upload your photosAsk for a signed URL per photo and upload straight to storage.
  4. 04Get the resultsPoll the status endpoint, or let a webhook tell you when each photo is ready.
Pricing

Simple, per-photo pricing

You need an active subscription to use the API. Pick any plan: we set your API quota to the standard rate of $0.25 per photo for every feature, so the API costs the same as the web app.

Unused monthly credits roll over to the next month, so you can save up for big projects or busy seasons. Credits are only deducted when processing finishes successfully.

HDR multi-bracket merge$0.25 per final photo

A bracket group of up to 7 exposures counts as 1 final photo. Upload 5 brackets, get 1 merged HDR image, pay $0.25.

Example: Processing 10 bracket groups (50 input photos) gives 10 final images = $2.50.

AI staging and retouching$0.25 per photo

Virtual staging, lawn replacement, sky replacement and retouching. Through the API, the first 3 re-renders of a photo are included (the web app includes 9). After that, every 3rd render costs 1 credit ($0.25): renders 4, 7, 10, 13 and so on.

Example: Stage a photo ($0.25), re-renders 2, 3, 5 and 6 are included, re-render 4 ($0.25) and re-render 7 ($0.25) = $0.75 for 7 renders.

Video generation6 credits per scene

Each scene is 10–15 seconds of animation made from 1 to 5 photos. The order of the photos sets the order of the animation.

Example: A video with 4 scenes (living room, kitchen, bedroom, exterior) = 4 × 6 = 24 credits, about 50 seconds long.

Authentication

Get your API key

API access is open to business accounts and platforms. Send us a message in the chat and we'll set up your production key. Every request needs it in the stager-api-key header.

Base URL https://api.stagerai.com

Check your photo quota

Shows how many photo credits you have left. Handy before you send a big batch.

FieldWhat it does
photosQuotaCredits you can spend right now. They roll over month to month, up to a cap.
GET/api/photos-quota
Header stager-api-key: your_api_key_here
Response
{
"success": true,
"data": {
"photosQuota": 128
}
}
HDR editing

Brackets in, finished photos out

Send single photos or bracketed exposures (1 to 7 per group). Stager AI aligns and blends them, then fixes the lens, the verticals and the windows for you.

HDR multi-bracket blendingCombines up to 7 exposures into one balanced image.
Lens and fish-eye correctionRemoves the distortion of wide-angle lenses.
Perspective straighteningMakes vertical lines truly vertical.
Window pullsRecovers blown-out windows so the view outside shows.

Supported files: All common raw formats: Canon (CR3, CR2), Nikon (NEF, NRW), Sony (ARW, SRF), Fujifilm (RAF), Panasonic (RW2), plus JPEG, PNG and TIFF.

What happens to every bracket group

  1. 1Upload 1 to 7 exposures
  2. 2Detect and align
  3. 3Merge and blend
  4. 4Lens, perspective, windows
  5. 5Final HDR photo

Create a project

Both HDR options start here. A project is a folder for one listing.

POST/api/projects
Header stager-api-key: your_api_key_here
Request body
{
"title": "123 Main Street Suite"
}
Response
{
"id": "project_uuid_here",
"title": "123 Main Street Suite"
}
Option 1: Batch uploadWe find the bracket groups for you.
1

Create a batch

Opens a batch for all the photos of one listing, and sets the edits to apply. We find the bracket groups for you.

FieldWhat it does
skyReplacementOptional, on by default. Puts a blue sky where the sky shows through the windows. Send false to keep the view outside as it was shot.
truefalse
lawnReplacementOptional, off by default. Replaces patchy grass with a fresh lawn on exterior photos that show one. Other photos are left as they are.
truefalse

clientReferenceId is optional. Use it to link this request to a record in your system: we send it back in this response and in every status check.

With lawnReplacement, the lawn is replaced after the HDR merge, so a group stays processing a little longer (at most 15 minutes). If the lawn step fails, you get the plain HDR photo.

POST/api/hdr-batch
Header stager-api-key: your_api_key_here
Request body
{
"projectId": "project_uuid_here",
"lensCorrection": true,
"perspectiveCorrection": true,
"photographerRemoval": true,
"fireplaceEnhancement": false,
"tvScreenReplacement": false,
"autoPrivacy": false,
"blurLicensePlates": false,
"skyReplacement": true, // optional, on by default
"lawnReplacement": false, // optional, off by default
"editingStyle": 11, // Stager Classic V2 Or leave empty to use latest model
"clientReferenceId": "your_internal_id" // optional
}
Response
{
"batchId": "batch_uuid_here",
"clientReferenceId": "your_internal_id"
}
2

Get an upload URL for each photo

Call this once per photo. You get a signed URL to upload that file to.

There is no limit on the number of photos in a batch. We group them into brackets using their EXIF data (capture time and exposure).

POST/api/hdr-batch/:batchId/url
Header stager-api-key: your_api_key_here
Request body
{
"filename": "living_room_1.jpg"
}
Response
{
"id": "image_uuid_here",
"url": "https://storage.googleapis.com/...temporary_gcloud_put_url..."
}
3

Process the batch

Once every photo is uploaded, start processing.

We read the EXIF data (time, exposure time, ISO, f-number) to find which photos belong together, then process each bracket group in the background.

POST/api/hdr-batch/:batchId/enhance
Header stager-api-key: your_api_key_here
Response
{}
4

Get the batch results

Check progress and collect the finished HDR photos, one result per bracket group.

FieldWhat it does
formatOptional query string parameter (not a body field: this is a GET request).
jpgpng
processingTrue while any group is still processing.
status (batch)"processing" or "completed" once processing has started. Before you call enhance, results is empty. If the batch itself failed it is "error", with the reason in message.
resultsOne result per detected bracket group.
groupIdUnique ID of the bracket group. It is also the imageId of the final HDR photo, to use with the AI edits.
imageUrlThe final HDR photo, once it is ready. With lawn replacement applied, this is the photo with the new lawn.
originalImageUrlThe HDR photo before lawn replacement. Same as imageUrl when no lawn was replaced.
lawnReplacementOnly when you asked for lawn replacement: "applied", "skipped" (no lawn in the photo) or "failed".
filenameOriginal filename of the first photo in the group.
status"error" if processing failed, otherwise undefined.
codeNumeric error code when status is "error". See the error codes below.
errorMessagePlain-English explanation of the error.
clientReferenceIdThe value you sent when creating the batch, if any.

Poll every 30–40 seconds. The batch is done when processing is false and every group has an imageUrl or an error status.

The groupId is also the imageId of the finished HDR photo. Pass it straight to the AI edits below (staging, declutter, lawn or sky replacement), with no need to upload the photo again.

GET/api/hdr-batch/:batchId?format=jpg
Header stager-api-key: your_api_key_here
Response
{
"processing": true,
"status": "processing",
"clientReferenceId": "your_internal_id",
"results": [
{
"groupId": "group_uuid_1",
"imageUrl": "https://storage.googleapis.com/.../hdr_output_1.jpg",
"originalImageUrl": "https://storage.googleapis.com/.../hdr_output_1.jpg",
"filename": "living_room_exposure_1.jpg",
"processing": false,
"status": undefined
},
{
"groupId": "group_uuid_2",
"imageUrl": undefined,
"filename": "kitchen_exposure_1.jpg",
"processing": true,
"status": undefined
},
{
"groupId": "group_uuid_3",
"imageUrl": undefined,
"filename": "bedroom_exposure_1.jpg",
"processing": false,
"status": "error",
"code": 1,
"message": "No credits left",
"errorMessage": "No credits left. Please purchase more credits or upgrade your plan."
}
]
}
Option 2: Single bracketYou decide which photos belong together.
1

Create an HDR group

A group holds one bracket set (up to 7 exposures). Create one per bracket set, with the edits to apply.

FieldWhat it does
skyReplacementOptional, on by default. Puts a blue sky where the sky shows through the windows. Send false to keep the view outside as it was shot.
truefalse
lawnReplacementOptional, off by default. Replaces patchy grass with a fresh lawn on exterior photos that show one. Other photos are left as they are.
truefalse

clientReferenceId is optional. Use it to link this request to a record in your system: we send it back in this response and in every status check.

With lawnReplacement, the lawn is replaced after the HDR merge, so a group stays processing a little longer (at most 15 minutes). If the lawn step fails, you get the plain HDR photo.

POST/api/hdr-image-group
Header stager-api-key: your_api_key_here
Request body
{
"projectId": "project_uuid_here",
"lensCorrection": true,
"perspectiveCorrection": true,
"photographerRemoval": true,
"fireplaceEnhancement": false,
"tvScreenReplacement": false,
"autoPrivacy": false,
"blurLicensePlates": false,
"skyReplacement": true, // optional, on by default
"lawnReplacement": false, // optional, off by default
"editingStyle": 11, // Stager Classic V2 Or leave empty to use latest model
"clientReferenceId": "your_internal_id" // optional
}
Response
{
"groupId": "group_uuid_here",
"clientReferenceId": "your_internal_id"
}
2

Get an upload URL for each exposure

Call this once per exposure in the group, then upload the file to the signed URL.

Up to 7 photos per group.

POST/api/hdr-upload-url
Header stager-api-key: your_api_key_here
Request body
{
"groupId": "group_uuid_here",
"filename": "living_room_exposure_1.jpg"
}
Response
{
"id": "image_uuid_here",
"uploadUrl": "https://storage.googleapis.com/...temporary_gcloud_put_url..."
}
3

Process the group

Merges the uploaded exposures into one HDR photo.

FieldWhat it does
tvLogoIdOptional. The id of one of your TV screen images (see Personalized TV screen below), to put on the TV in the photo. Leave it out to keep the TV as it is.
TV screen image id

With tvLogoId, the TV screen image is placed on the TV once the HDR photo is ready, so the group stays processing a little longer. Photos without a TV, and exteriors, are left as they are. If this step fails, you get the plain HDR photo.

POST/api/hdr-enhance
Header stager-api-key: your_api_key_here
Request body
{
"groupId": "group_uuid_here",
"tvLogoId": "tv_screen_image_uuid_here" // optional
}
Response
{}
4

Check the status and get the result

Check progress and collect the finished HDR photo.

Poll every 5–10 seconds. A bracket group usually takes about 1 to 5 minutes, depending on size and complexity.

clientReferenceId is the value you sent when creating the group, if any.

imageUrl is the final photo (with the new lawn, if lawn replacement was applied). originalImageUrl is the HDR photo before lawn replacement. With lawnReplacement on, the response also has lawnReplacement: "applied", "skipped" (no lawn in the photo) or "failed".

With a tvLogoId, the response also has tvLogo: "applied", "skipped" (no TV found in the photo) or "failed". The TV screen image is in both imageUrl and originalImageUrl.

The groupId is also the imageId of the finished HDR photo. Pass it straight to the AI edits below (staging, declutter, lawn or sky replacement), with no need to upload the photo again.

GET/api/hdr-image-group/:groupId?format=jpg
Header stager-api-key: your_api_key_here
Response
// If processing:
{
"status": "processing",
"clientReferenceId": "your_internal_id"
}
// On Complete:
{
"status": "completed",
"imageUrl": "https://storage.googleapis.com/stager-final-results/output.jpg",
"originalImageUrl": "https://storage.googleapis.com/stager-final-results/output.jpg",
"filename": "living_room_exposure_1.jpg",
"clientReferenceId": "your_internal_id"
}
// On Error:
{
"status": "error",
"code": 11,
"errorMessage": "Failed to align the bracketed exposures. Make sure the images were taken from the same position.",
"clientReferenceId": "your_internal_id"
}
Personalized TV screenPut your own image or logo on the TVs. Works with Option 2: send tvLogoId when you process the group.

Upload a TV screen image

Upload an image or logo to show on the TVs in your photos, as multipart form data. Upload it once and reuse its id as the tvLogoId for as many groups as you like.

FieldWhat it does
fileThe image, in the file field. JPEG, PNG, WebP or HEIC, up to 25 MB.
idSend it as tvLogoId when you process a group.
urlSigned URL of the stored image. It expires, so list the images again to get a fresh one.
thumbnailUrlSigned URL of a 400 px wide preview.

Images are stored as JPEG, up to 3840 px on the long side. Transparent areas, like the background of a PNG logo, become black, the way a TV looks around a logo.

Images belong to your account: everyone on it can list and use them.

POST/api/tv-screen-images
Header stager-api-key: your_api_key_here
Form data
// Multi-part Form Data Form
file: brokerage-logo.png / .jpg
Response
{
"status": "success",
"data": {
"id": "tv_screen_image_uuid_here",
"name": "brokerage-logo",
"url": "https://storage.googleapis.com/stager-ai-tv-screens/...signed_url...",
"thumbnailUrl": "https://storage.googleapis.com/stager-ai-tv-screens/...signed_url...",
"width": 1920,
"height": 1080,
"createdAt": "2026-11-03T10:15:30.000Z"
}
}

List your TV screen images

Every TV screen image uploaded to your account, newest first, with its id and URLs.

FieldWhat it does
offsetOptional query string parameter. Number of images to skip, 0 by default.
limitOptional query string parameter. Up to 100 images per page, 50 by default.
totalNumber of images on your account.
GET/api/tv-screen-images?offset=0&limit=50
Header stager-api-key: your_api_key_here
Response
{
"status": "success",
"data": {
"items": [
{
"id": "tv_screen_image_uuid_here",
"name": "brokerage-logo",
"url": "https://storage.googleapis.com/stager-ai-tv-screens/...signed_url...",
"thumbnailUrl": "https://storage.googleapis.com/stager-ai-tv-screens/...signed_url...",
"width": 1920,
"height": 1080,
"createdAt": "2026-11-03T10:15:30.000Z"
}
],
"total": 1
}
}

Error codes

When a bracket group fails, the status endpoints return status: "error" with a numeric code and a readable errorMessage. The hdr.image.failed webhook sends the same code as errorCode.

CodeWhat it means
1No credits left. Please purchase more credits or upgrade your plan.
2Failed to merge the bracketed exposures into an HDR image.
3Failed to apply the requested edits to the image.
4Failed to apply the selected editing style to the image.
5Processing took too long and timed out.
6No images were found for this group. Upload at least 1 image before enhancing.
7Failed to convert the uploaded image. The file may be corrupted or in an unsupported format.
8Processing failed after the maximum number of retries.
9Failed to generate the enhanced image.
10Too many images in this group. Maximum 7 brackets allowed.
11Failed to align the bracketed exposures. Make sure the images were taken from the same position.
Webhooks (optional)

Get told when photos are ready

Skip the polling: we tell your server the moment each bracket group is done (one event per bracket group, for both the batch and the single-bracket options) and the moment each AI edit (staging, declutter, lawn or sky replacement) is ready.

Add a webhook URL on your account page, under API Access. Saving it creates a webhook secret, which we use to sign every request so you can check it really came from Stager AI. Open your account page

Delivery: if your endpoint doesn't answer with a 2xx, we retry with backoff, but not forever. Treat webhooks as a faster alternative to polling, not a replacement: if no webhook arrives after a few minutes, fall back to the GET endpoints above.

There's no separate "batch completed" event yet. For a batch, listen for one hdr.image.* event per bracket group.

In hdr.image.completed, the groupId is also the imageId of the finished HDR photo, ready for the AI edits.

Events

Every webhook is a POST with a JSON body like this:

POSThdr.image.completed
Body
{
"eventId": "evt_uuid_here",
"eventType": "hdr.image.completed",
"createdAt": "2026-09-13T10:15:30.000Z",
"data": {
"groupId": "group_uuid_here",
"clientReferenceId": "your_internal_id",
"imageUrl": "https://storage.googleapis.com/stager-final-results/output.jpg",
"originalImageUrl": "https://storage.googleapis.com/stager-final-results/output.jpg",
"filename": "living_room_exposure_1.jpg"
}
}
POSThdr.image.failed
Body
{
"eventId": "evt_uuid_here",
"eventType": "hdr.image.failed",
"createdAt": "2026-09-13T10:15:30.000Z",
"data": {
"groupId": "group_uuid_here",
"clientReferenceId": "your_internal_id",
"errorCode": 2,
"message": "..."
}
}
POSTedit.completed
Body
{
"eventId": "evt_uuid_here",
"eventType": "edit.completed",
"createdAt": "2026-09-13T10:15:30.000Z",
"data": {
"generationId": "gen_uuid_here",
"imageId": "image_uuid_here",
"actionType": "skyReplace",
"resultId": "res_uuid_here",
"imageUrl": "https://storage.googleapis.com/.../result.jpg",
"width": 2958,
"height": 3978
}
}
POSTedit.failed
Body
{
"eventId": "evt_uuid_here",
"eventType": "edit.failed",
"createdAt": "2026-09-13T10:15:30.000Z",
"data": {
"generationId": "gen_uuid_here",
"imageId": "image_uuid_here",
"actionType": "lawnReplace",
"message": "..."
}
}

eventId is unique per event. Use it to ignore duplicates if the same event is ever delivered twice (standard practice for any webhook).

Verify signatures

Every request carries two headers, so you can check it came from Stager AI and wasn't changed:

X-Stager-Signature

sha256=<hex digest>

X-Stager-Timestamp

Unix time in milliseconds when the request was sent.

The signature is an HMAC-SHA256 of ${timestamp}.${rawBody}, using your webhook secret (shown next to your webhook URL under API Access). In Node.js:

Node.js
const crypto = require('crypto')
function isValidSignature(rawBody, timestamp, signatureHeader, webhookSecret) {
const expected = crypto
.createHmac('sha256', webhookSecret)
.update(`${timestamp}.${rawBody}`)
.digest('hex')
return signatureHeader === `sha256=${expected}`
}

Sign the raw request body, before parsing the JSON. Re-serializing the parsed object can produce a different string and fail the check.

AI edits

Staging, lawns, skies and decluttering

Stage empty rooms, green up lawns, swap skies and clear out clutter. Upload the photo once, then call any of these edits with its id.

1

Upload a photo

Start here. Upload the photo as multipart form data: the id you get back is the imageId for every edit below.

POST/api/image
Header stager-api-key: your_api_key_here
Form data
// Multi-part Form Data Form
File: binary_image_payload.jpg / .png
Response
{
"success": true,
"data": {
"id": "img_71283912739",
"imageUrl": "https://storage.googleapis.com/stager-resized/cache_file.jpg",
"width": 1920,
"height": 1280
}
}

Virtual staging

Furnishes an empty room in the style you choose.

FieldWhat it does
actionTypeStaging mode.
subtleStagingautoStaging
roomTypeThe type of room in the photo.
LIVING_ROOMDINING_ROOMBEDROOMKITCHENKITCHEN_LIVING_AREAHOME_OFFICETERRACEBALCONYBATHROOMEXTERIORGAME_ROOMKIDS_ROOMGARAGE
roomStyleFurniture style.
modernminimalistscandinavianindustrialrusticfarmhousekidsRoomcoastalModernluxurymidCenturybohoChicjapandi
colorTypesColor palette for the furniture.
classicWhitecharcoalGraymatteBlackbeigeespressoBrownwalnutnaturalWoodnavyBlueforestGreenrusticOakivorysteelBlueashGraymahogany

The edit runs in the background. Collect it with Get an edit result (GET /api/generations/:generationId), or get it pushed to you with the edit.completed webhook.

POST/api/images/:imageId/stage
Header stager-api-key: your_api_key_here
Request body
{
"roomType": "LIVING_ROOM",
"roomStyle": "modern",
"actionType": "subtleStaging",
"colorTypes": ["classicWhite", "naturalWood"],
"removeFurniture": false,
"extraInstructions": "Add a luxury linen sofa assembly"
}
Response
// Right away (the edit runs in the background):
{
"status": "success",
"data": {
"generationId": "gen_uuid_here",
"imageId": "image_uuid_here",
"actionType": "subtleStaging",
"status": "processing"
}
}

Lawn replacement

Replaces patchy grass, weeds and bare soil with a fresh, freshly cut lawn.

FieldWhat it does
lawnTypeHow strong the change is.
lightmediumstrong

The edit runs in the background and is blended onto your original photo, so the result comes back at the full size of the photo you uploaded.

Collect it with Get an edit result (GET /api/generations/:generationId), or get it pushed to you with the edit.completed webhook.

POST/api/images/:imageId/lawn-replace
Header stager-api-key: your_api_key_here
Request body
{
"lawnType": "medium",
"extraInstructions": "Keep the stone pathway intact"
}
Response
// Right away (the edit runs in the background):
{
"status": "success",
"data": {
"generationId": "gen_uuid_here",
"imageId": "image_uuid_here",
"actionType": "lawnReplace",
"status": "processing"
}
}

Sky replacement

Swaps a grey or blown-out sky for a blue sky or a twilight, and adjusts the light on the building to match.

FieldWhat it does
skyTypeThe new sky.
bluetwilight
skySubTypeHow strong the effect is (twilight only).
subtlemiddledramatic

The edit runs in the background and is blended onto your original photo, so the result comes back at the full size of the photo you uploaded.

Collect it with Get an edit result (GET /api/generations/:generationId), or get it pushed to you with the edit.completed webhook.

POST/api/images/:imageId/sky-replace
Header stager-api-key: your_api_key_here
Request body
{
"skyType": "twilight",
"skySubType": "dramatic"
}
Response
// Right away (the edit runs in the background):
{
"status": "success",
"data": {
"generationId": "gen_uuid_here",
"imageId": "image_uuid_here",
"actionType": "skyReplace",
"status": "processing"
}
}

Get an edit result

Check progress and collect a finished AI edit (staging, declutter, lawn or sky replacement), using the generationId you got back.

Poll every 5–10 seconds. An edit usually takes under a minute.

imageUrl is a signed link that expires: download the file rather than storing the link.

GET/api/generations/:generationId
Header stager-api-key: your_api_key_here
Response
// If processing:
{
"status": "success",
"data": {
"generationId": "gen_uuid_here",
"imageId": "image_uuid_here",
"actionType": "skyReplace",
"status": "processing"
}
}
// On Complete:
{
"status": "success",
"data": {
"generationId": "gen_uuid_here",
"imageId": "image_uuid_here",
"actionType": "skyReplace",
"status": "completed",
"resultId": "res_uuid_here",
"imageUrl": "https://storage.googleapis.com/.../result.jpg",
"width": 2958,
"height": 3978
}
}
// On Error:
{
"status": "success",
"data": {
"generationId": "gen_uuid_here",
"imageId": "image_uuid_here",
"actionType": "skyReplace",
"status": "failed"
}
}

Declutter

Removes the furniture and objects in the room, leaving it empty and ready to stage.

The edit runs in the background. Collect it with Get an edit result (GET /api/generations/:generationId), or get it pushed to you with the edit.completed webhook.

POST/api/images/:imageId/declutter
Header stager-api-key: your_api_key_here
Request body
{
"extraInstructions": "Keep the built-in shelving"
}
Response
// Right away (the edit runs in the background):
{
"status": "success",
"data": {
"generationId": "gen_uuid_here",
"imageId": "image_uuid_here",
"actionType": "declutter",
"status": "processing"
}
}
Video

Listing videos from your photos

Turn listing photos into marketing videos: walkthroughs, before-and-after staging, renovations and more.

Floor plan rotationSmooth 360° rotations with zoom.
Listing slow motionElegant pans and zooms across the photos.
Before and after stagingThe original next to the virtually staged room.
Before and after renovationShow what a property could become.
MusicRoyalty-free tracks, or upload your own.

How a video is made

  1. 11 to 5 photos per scene
  2. 2Order the frames
  3. 3Apply a style
  4. 4Add music
  5. 5Render
  6. 6Final video (10–15 s per scene)
1

Upload photos

Start here. Upload each photo as multipart form data and keep the id: you need it to build clips.

You can reuse an uploaded photo in several videos, or several times in the same video.

POST/api/image
Header stager-api-key: your_api_key_here
Form data
// Multi-part Form Data Form
File: binary_image_payload.jpg / .png
Response
{
"success": true,
"data": {
"id": "img_71283912739",
"imageUrl": "https://storage.googleapis.com/stager-resized/cache_file.jpg",
"width": 1920,
"height": 1280
}
}
2

Get the music library

Lists the background tracks you can use: our royalty-free music and any audio you uploaded.

GET/api/audio
Header stager-api-key: your_api_key_here
Response
{
"data": {
"items": [
{
"id": "audio_001",
"name": "Corporate Inspirational",
"url": "https://storage.googleapis.com/.../audio.mp3",
"isCustom": false
},
{
"id": "custom_audio_123",
"name": "My Custom Soundtrack",
"url": "https://storage.googleapis.com/.../custom.mp3",
"isCustom": true
}
]
}
}
3

Create a video

Starts a new video with its orientation and, optionally, music. You choose the animation style per clip.

FieldWhat it does
orientationVideo format.
horizontalvertical
audioIdOptional. An id from the music library. Without it, the template default is used.
POST/api/videos
Header stager-api-key: your_api_key_here
Request body
{
"title": "Luxury Home Tour - 123 Main St",
"orientation": "horizontal",
"audioId": "audio_001"
}
Response
{
"data": {
"id": "video_abc123xyz",
"title": "Luxury Home Tour - 123 Main St",
"orientation": "horizontal",
"videoType": "listing",
"videoUrl": null,
"clips": []
}
}
4

List your videos

All the videos you created, a page at a time.

GET/api/videos?offset=0&limit=50
Header stager-api-key: your_api_key_here
Response
{
"data": {
"items": [
{
"id": "video_abc123xyz",
"title": "Luxury Home Tour - 123 Main St",
"orientation": "horizontal",
"videoType": "listingSlowMotion",
"videoUrl": "https://storage.googleapis.com/.../final_video.mp4",
"createdAt": "2024-01-15T10:30:00Z"
}
],
"total": 1
}
}
5

Get a video

Everything about one video: its clips, photos and render status.

GET/api/videos/:videoId
Header stager-api-key: your_api_key_here
Response
{
"data": {
"id": "video_abc123xyz",
"title": "Luxury Home Tour - 123 Main St",
"videoUrl": "https://storage.googleapis.com/.../final_video.mp4",
"clips": [
{
"id": "clip_001",
"order": 0,
"videoUrl": "https://storage.googleapis.com/.../clip_001.mp4",
"images": [
{
"id": "img_123",
"imageId": "img_uuid",
"url": "https://storage.googleapis.com/.../thumbnail.jpg",
"isGeneration": false
}
]
}
]
}
}
6

Add a clip

Adds a clip (a scene) to a video. Each photo is a frame, in the order you set.

FieldWhat it does
effectThe animation style of the clip.
default (0)SlowMotionWalkthrough (1)SlowMotionAerial (2)FastSpeedRamps (3)SplitScreen (4)ExteriorToInterior (5)StagingFurnitureAppears (6)DawnToNoon (7)DayToNight (8)StarryNight (9)SummerToWinter (10)HouseLandsOnTerrain (11)HouseBuildsUp (12)Renovation (13)
cameraMotionOptional. Only used when effect is SlowMotionWalkthrough (1); ignored for any other effect.
default (0)pushIn (1)pushOut (2)slowMoDescending (3)slowMoElevating (4)orbitRight (5)orbitLeft (6)

The photo IDs must be uploaded first. order sets the frame order in the animation.

POST/api/videos/:videoId/clips
Header stager-api-key: your_api_key_here
Request body
{
"images": [
{ "id": "img_123", "order": 0 },
{ "id": "img_124", "order": 1 },
{ "id": "img_125", "order": 2 }
],
"customInstructions": "Add smooth transitions between rooms",
"effect": 1,
"cameraMotion": 0
}
Response
{
"data": {
"id": "clip_001"
}
}
7

Render the video

Builds the final video from all its clips. It runs in the background: track it with the progress endpoint.

A render usually takes 5–30 minutes, depending on the length and complexity of the video.

POST/api/videos/:videoId/render
Header stager-api-key: your_api_key_here
Request body
{}
Response
{
"data": {}
}
8

Check the render progress

Shows how far the render is and, when it is done, the video URL and file size.

Poll every 5–10 seconds. You get one of three states: "progress" (with a percentage), "done" (with the URL and size) or "error" (with a message).

GET/api/videos/:videoId/progress
Header stager-api-key: your_api_key_here
Response
// While processing:
{
"status": "progress",
"progress": 45
}
// When complete:
{
"status": "done",
"url": "https://storage.googleapis.com/.../final_video.mp4",
"size": 125000000
}
// On error:
{
"status": "error",
"message": "Render failed: insufficient memory"
}
9

Edit a clip

Changes a clip's photos, order, effect, camera motion or instructions. The video then needs a new render.

effect and cameraMotion are optional here: leave one out to keep its current value. The values are listed under Add a clip.

PUT/api/videos/:videoId/clips/:clipId
Header stager-api-key: your_api_key_here
Request body
{
"images": [
{ "id": "img_123", "order": 0 },
{ "id": "img_126", "order": 1 }
],
"customInstructions": "Updated smooth transitions",
"effect": 1,
"cameraMotion": 4
}
Response
{
"data": {
"success": true
}
}
10

Delete a clip

Removes a clip from the video. Render the video again afterwards.

DELETE/api/videos/:videoId/clips/:clipId
Header stager-api-key: your_api_key_here
Response
{
"data": {
"success": true
}
}

Ready to connect?

Tell us what you're building and we'll set up your key. We can also tune an editing style to match your look.