POST /v1/align takes one track and
one video and returns labeled offsets into the song, best first: the drop, the downbeats, the
phrase starts. These are the points where the music lands with what’s on screen.
Use it for a clip the track can cover on its own. When the video is longer than the song, you want
loop instead.
Align a track
200
offset_seconds is a position in the song: start the track there and it lines up with the top
of your clip. score is how strongly that point lands, 0–1. label says what the moment is
musically, and it is null on a track Cue could only align by energy.
No match call is required. track_id is the Soundstripe song id, so you can align a track you
picked yourself.
integer
default:"12"
How many sync points to return, 1–50.
A cold pairing comes back as a job
Cue serves the200 above only from cache. When nothing is cached for this pairing, the index has
to be built, and that takes longer than a request allows. So you get a 202 and a job:
202
GET /v1/align/jobs/{job_id} until status is complete. The finished job returns the same
sync_points, plus job_id, status and cached.
A video whose ingest hasn’t finished gives you a different 202, with no job_id and a
poll of /v1/videos/{video_id}. Poll the video to ready, then call /v1/align again.
Which answer you get, and what you pay
The warm read succeeds when either of these is true:- Cue has stored alignment points for this exact
(video_id, track_id)pair, from an earlier align or from a background warm. - Both halves of the pairing are profiled: the video’s sync profile, built during ingest, and the track’s audio profile, built the first time that track is indexed for sync.
cached on the response tells you which
rate you paid.
Sync points on a match instead
If you want points for a whole shortlist rather than one track, pass"include_sync": true
to /v1/match. That path is cache-only and never builds a profile,
so on a fresh video most tracks come back "sync_status": "warming" with an empty
sync_points array. /v1/align is the endpoint that actually builds the index.
sync_status is one of warm (points included), warming (this pairing isn’t indexed yet, so call
again shortly), unavailable (no points could be read) and error (something failed on our side,
and the same call may succeed on a retry). unavailable and error used to be reported
identically, which meant an outage read as a settled fact about your track.
Duck under voiceover
If the clip has narration, pass"auto_mix": true and the response carries a mix block: gains
for the two beds, plus duck.envelope_points, the automation curve telling you how far to pull
the music down at each instant. These are the same numbers the Cue app uses, so your render
matches the app. It reads a measurement taken at ingest, so it adds no charge.
When that measurement can’t be read, the 200 from POST /v1/align carries "mix": null and the
completed job simply omits the key. Auto-mix is additive and never fails the align call, so treat
null and absent the same way and check before you read into the block. The four-step render recipe and
the full field-by-field description are on
Loop. The block is identical on both endpoints.
What it costs
0.001 for a warm one.auto_mix adds nothing, and a call that
errors bills nothing. Sync points embedded in a match are included in the match price. See
Metering & pricing.
Over MCP the same thing is the get_sync_points tool.