Developer Guide
Branding

Branded Listing Reels: Assembly, Pacing and White-Label Output

Anyone can turn twelve photos into twelve clips. The difference between output that looks generated and output an agent is proud to post is clip ordering, effect pacing, aspect ratio discipline, and branding that gets applied without the agent thinking about it.

February 13, 2026
14 min read
PhotoAIVideo Team
2–20
Clips Per Reel
merged in one call
1 credit
Merge Cost
regardless of clip count
8
Camera Effects
to vary pacing
20–45s
Ideal Reel Length
for social distribution

Clips Are Raw Material, Reels Are the Product

The PhotoAIVideo API separates these two steps for a reason. POST /api/v1/videos turns one photo into one cinematic clip. POST /api/v1/reels merges two to twenty completed clips into a single video. The merge costs one credit no matter how many clips go into it.

That split is the leverage point. The clips are commodity raw material — every product built on this API gets the same quality of motion out of the same photo. What differentiates your product is everything you decide about assembly: which clips, in what order, with which effects, at what aspect ratio, wrapped in whose brand. That is a product decision, and it is where you should spend your engineering effort.

This guide covers each of those decisions concretely. For the mechanics of waiting on the underlying jobs, see our async jobs and polling guide, and check the API reference for the current set of parameters each endpoint accepts.

Assigning Effects by Room Type

The single highest-leverage improvement you can make to automated output is to stop using one effect everywhere. Each camera move communicates something different, and matching the move to the subject is what makes a reel feel edited by a person.

orbit_right / orbit_left

The workhorse. Reveals depth and spatial relationship in a room. Best for living rooms, kitchens, and primary bedrooms where you want the viewer to feel the volume of the space.

zoom_in

Draws attention to a feature — a fireplace, a range, a view. Use sparingly and deliberately, because too many push-ins in sequence feel restless.

zoom_out

A natural opener or closer. Starting tight and pulling back to reveal the full room gives a reel a sense of arrival.

pan_right / pan_left

Good for wide spaces and exteriors where the subject is broader than the frame. Reads as calm and surveying rather than dramatic.

tilt_up

Best on exteriors and vaulted interiors. Rising to reveal height flatters two-story facades and tall ceilings.

tilt_down

Useful as a transition into a detail shot, or on staircases and open-plan spaces viewed from above.

In code, this becomes a lookup rather than a hardcoded constant. If your listing data already labels photos by room — and most MLS feeds and photographer deliveries do — you can assign effects automatically with no user input at all.

// Match the camera move to the subject instead of orbiting everything.
const EFFECT_BY_ROOM = {
  exterior_front: "tilt_up",     // rising reveal flatters a facade
  living_room: "orbit_right",    // show depth and volume
  kitchen: "orbit_left",         // opposite direction breaks repetition
  primary_bedroom: "orbit_right",
  bathroom: "pan_right",         // calmer move for a smaller space
  backyard: "pan_left",
  detail: "zoom_in",             // features: fireplace, range, view
  aerial: "zoom_out",
}

const FALLBACK_CYCLE = ["orbit_right", "zoom_in", "orbit_left", "pan_right"]

function effectFor(photo, index) {
  // Prefer a room-aware choice; otherwise rotate so nothing repeats back to back.
  return EFFECT_BY_ROOM[photo.roomType] ?? FALLBACK_CYCLE[index % FALLBACK_CYCLE.length]
}

The fallback cycle matters as much as the lookup. When room labels are missing, rotating through a small set of effects still guarantees that no two adjacent clips use the same move — which removes the most obvious tell of automated output at essentially zero cost.

Ordering Clips Into a Tour

Async rendering returns clips in completion order, which is effectively random. If you merge in that order, you get a slideshow that jumps from a bathroom to the backyard to a bedroom. Buyers read that as disorienting even if they cannot articulate why.

Sequencing rules worth encoding
  • Open on the exterior or the single strongest interior shot — the first two seconds decide whether anyone watches the rest.
  • Follow a walkthrough order a buyer would physically experience: entry, main living, kitchen, bedrooms, bath, outdoor.
  • Alternate effect families so two orbits never sit back to back; contrast is what creates perceived production value.
  • Put the second-strongest shot last so the reel ends on something memorable rather than a utility room.
  • Cap the reel at what holds attention — twelve good clips beat twenty mediocre ones every time.
  • Keep clip duration consistent within a reel unless you are deliberately accenting one shot.
// A walkthrough order a buyer would actually experience.
const TOUR_ORDER = [
  "exterior_front",
  "entry",
  "living_room",
  "kitchen",
  "dining",
  "primary_bedroom",
  "bedroom",
  "bathroom",
  "backyard",
  "aerial",
]

function sortForTour(photos) {
  return [...photos].sort((a, b) => {
    const ai = TOUR_ORDER.indexOf(a.roomType)
    const bi = TOUR_ORDER.indexOf(b.roomType)
    // Unlabeled photos sink to the end rather than scrambling the sequence.
    return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi)
  })
}

async function buildBrandedReel(listing, brand) {
  const ordered = sortForTour(listing.photos).slice(0, 12)  // 12 good > 20 mediocre

  // Start every clip concurrently, but remember the intended position.
  const jobs = await Promise.all(
    ordered.map((photo, i) =>
      fetch(API + "/videos", {
        method: "POST",
        headers: { ...HEADERS, "Content-Type": "application/json" },
        body: JSON.stringify({
          imageUrl: photo.url,
          effect: effectFor(photo, i),
          duration: 5,
        }),
      })
        .then((r) => r.json())
        .then((job) => ({ ...job, position: i })),
    ),
  )

  await Promise.all(jobs.map((j) => waitForVideo(j.jobId)))

  // Re-sort by intended position — completion order is not tour order.
  const inOrder = [...jobs].sort((a, b) => a.position - b.position)

  const reel = await fetch(API + "/reels", {
    method: "POST",
    headers: { ...HEADERS, "Content-Type": "application/json" },
    body: JSON.stringify({ videoJobIds: inOrder.map((j) => j.jobId) }),
  }).then((r) => r.json())

  return waitForReel(reel.jobId)
}

The position field carried through the async boundary is the important detail. Without it, there is no way to recover the intended sequence after twelve concurrent renders finish at unpredictable times.

Aspect Ratios Are Not Interchangeable

Agents publish to Instagram Reels, TikTok, YouTube Shorts, Facebook, their listing page, and the MLS. Those are not the same shape, and cropping between them destroys composition. A horizontal kitchen shot center-cropped to vertical routinely cuts out the island the photo was framed around.

RatioWhere it goesNotes
9:16Instagram Reels, TikTok, YouTube Shorts, StoriesHighest-distribution format. Keep captions clear of the lower third where platform UI sits.
1:1Feed posts, some ad placementsSafe middle ground when one asset must serve several feed surfaces.
16:9Listing pages, YouTube, MLS media, emailThe format that looks right embedded above a property description.

Practically, this means your product should treat output format as a variant of one listing rather than as a separate job the agent has to think about. Check the API reference for the aspect ratio and duration parameters currently supported, then expose a single choice — "where are you posting this?" — and derive the rest.

Default to vertical

If you only build one format first, build 9:16. That is where listing videos actually get watched and shared, and it is the format agents are most often unable to produce themselves. A horizontal variant for the listing page is a valuable second, not a first.

Branding Without Asking the Agent

Branding fails when it depends on the agent remembering to apply it. Because you are calling the API server-to-server, you already know who the agent is, which brokerage they belong to, and what their brand rules are. Apply them automatically.

Per-agent identity

Store each agent's logo, headshot, colors, and contact line in your own database keyed to their user record. When you assemble their reel, you already know which brand to apply — the agent never picks it manually.

Brokerage-level defaults

Brokerages care about consistency more than individual expression. Store brand rules at the brokerage level and inherit down to agents, allowing overrides only where the brokerage permits them.

Your own product identity

Because generation happens server-to-server, nothing in the output has to reference PhotoAIVideo. The videos your customers download belong to your product and your customers' brands.

Per-platform variants

The same clip set should produce a vertical cut for Reels and Shorts, and a horizontal cut for a listing page or YouTube. Treat aspect ratio as an output variant, not a separate project.

// Brand resolution: agent overrides brokerage, brokerage overrides platform default.
async function resolveBrand(agentId) {
  const agent = await db.agents.findById(agentId)
  const brokerage = await db.brokerages.findById(agent.brokerageId)

  return {
    logoUrl: agent.logoUrl ?? brokerage.logoUrl,
    primaryColor: agent.primaryColor ?? brokerage.primaryColor,
    contactLine: agent.phone ?? brokerage.phone,
    // Brokerages can lock branding so agents cannot drift off-template.
    locked: brokerage.enforceBranding === true,
  }
}

// The agent's entire interaction is one click; branding is never a question.
async function generateForAgent(agentId, listingId) {
  const brand = await resolveBrand(agentId)
  const listing = await db.listings.findById(listingId)

  return buildBrandedReel(listing, brand)
}

The locked flag is what brokerages actually buy. Marketing directors care less about producing video than about every agent producing video that looks like it came from the same firm. If your product can guarantee that, you are selling brand control, not rendering.

Cache Clips, Re-Merge Cheaply

Clips cost one credit each; a merge costs one credit total. That asymmetry has a direct architectural consequence: download and keep every finished clip in your own storage. When an agent wants a shorter cut, a different order, or an updated brand, you re-merge the clips you already own instead of re-rendering them.

For a twelve-photo listing, that is the difference between thirteen credits and one. Teams that skip clip caching end up paying full price every time a customer wants a small revision — and small revisions are most of what customers want.

Five Mistakes That Make Output Look Automated

Merging clips in whatever order the photos came back in
Order is the difference between a tour and a slideshow. Sort clips into a deliberate walkthrough sequence before calling the merge, using your own ordering field rather than array position from an async batch.
Using the same camera effect on every clip
Twelve identical orbits reads as automated. Assign effects by room type so the pacing varies naturally and the reel feels edited rather than generated.
Producing one horizontal video and cropping it for social
Generate the aspect ratio you intend to publish. A center-crop of a horizontal room shot routinely cuts out the exact feature the shot was framed around.
Letting agents assemble reels manually every time
Encode branding and ordering as defaults in your product. The reason to build on an API is that the agent clicks once and gets something on-brand without making six decisions.
Rebuilding the whole reel to change the branding
Keep the finished clips in your own storage. Re-merging or re-wrapping a cached clip set is far cheaper than re-rendering every clip because a logo changed.
Start Building

Ship branded listing reels from your own product

Eight camera effects, up to twenty clips per reel, and one credit per merge. Generation is entirely server-to-server, so the output carries your customers' branding and never ours. Plans start at $99/month for 250 credits.

Frequently Asked Questions

How many clips should a reel have?

Eight to twelve is the sweet spot for social distribution, landing around twenty to forty-five seconds. The API allows up to twenty, but longer reels lose retention. Selecting the best clips beats including every room.

Does a longer reel cost more to merge?

No. The merge is one credit whether you combine two clips or twenty. Your cost scales with the number of clips you render, not with reel length.

Can I produce vertical and horizontal from one shoot?

Yes, and you should. Treat aspect ratio as an output variant of the same listing. Consult the API reference for the currently supported ratio parameters, and always prefer generating the target format over cropping.

Will the output mention PhotoAIVideo?

No. API generation is server-to-server and the resulting videos are yours to brand and distribute. Your customers see your product and their own brand.

How do I add music or captions?

Check the current API documentation for the reel parameters available on your plan. Many teams also apply their own captions and audio during a post-processing step on the merged output, which keeps full control of licensing and typography inside their product.

What if an agent wants a different clip order?

Because you cached the clips, re-ordering costs one credit for a fresh merge rather than a full re-render. This is why clip caching is worth building before you build a reorder UI.

Related Articles

Rolling Out AI Video Across a Brokerage

Brand enforcement, agent adoption, and recruiting leverage for brokerages.

Bulk Listing Video Generation at Scale

Credit budgeting, concurrency caps, and resumable batch imports.

PhotoAIVideo Developer API

Endpoint reference, camera effects, credit costs, and plan tiers.