ffmpegapi

Speed up / slow down video via API (with pitch correction)

··10 min read
Speed up / slow down video via API (with pitch correction)

You sped a tutorial up to 2x and the narrator came out sounding like a chipmunk. Or you slowed a product shot to half speed, the voice dropped an octave, and the motion started stuttering. Both are the same defect, and it isn't in your encoder: the single speed multiplier is doing one operation where the job needs two.

Quick answer: A correct speed up video API request runs two independent filters, not one multiplier. setpts rescales the video timestamps by the reciprocal of your speed factor, and atempo time-stretches the audio at the factor itself so the pitch stays where it was. For 2x in FFmpeg that is -vf "setpts=PTS/2" -af "atempo=2.0". If you don't want to host an encoder, a queue, and a retry loop to run those two flags, FFmpeg Micro accepts the same two flags as a single job and bills on input seconds, not rendered output.

Speed changes shift pitch because resampling and time-stretching aren't the same thing

Conventional wisdom says a speed change is one number. Most video APIs are built that way, and on a silent b-roll clip the result is perfect. The problem shows up the moment there's a human voice in the track, because the naive way to make audio shorter is to play the same samples faster, and frequency scales with playback rate. Double the rate and every formant in the voice doubles too. That's the chipmunk.

Time-stretching is a different operation. FFmpeg's atempo uses a WSOLA-style approach: it cuts the waveform into overlapping windows and re-lays them closer together or further apart, so the duration changes while the frequencies stay put. The voice gets faster, not higher.

The gap is documented, not hidden. The template-editor class of API exposes one speed property on a clip, typically a decimal multiplier from 0 to 10, and their own learn pages state plainly that speeding up raises the audio pitch and slowing down lowers it. One, which models speed ramps as keyframed percentage changes from -50 to 100, recommends muting the audio track entirely. That's an honest answer to the wrong question. If your clip is a narrated UGC testimonial or a podcast cut, muting it deletes the content.

The video and audio values are reciprocals of each other

A speed change needs two values derived from the same factor, and they move in opposite directions. setpts takes a timestamp expression, so to make video play twice as fast you divide the presentation timestamps by 2. atempo takes a tempo ratio, so the same change is 2.0. Inverting one of them is the most common mistake in this job.

Target speedVideo filterAudio filter
1.5x faster`setpts=PTS/1.5``atempo=1.5`
2x faster`setpts=PTS/2``atempo=2.0`
4x (timelapse)`setpts=PTS/4``atempo=2.0,atempo=2.0`
0.5x (slow motion)`setpts=2.0*PTS``atempo=0.5`
0.25x`setpts=4.0*PTS``atempo=0.5,atempo=0.5`

Run locally, a 2x speed-up is this:

ffmpeg -i input.mp4 -vf "setpts=PTS/2" -af "atempo=2.0" -c:v libx264 -crf 23 output.mp4

Both of those are single-input, single-output filterchains. setpts reads one video stream, atempo reads one audio stream, and a comma-chained atempo=2.0,atempo=2.0 is still one chain on one stream.

atempo stopped being capped at 2.0 in FFmpeg 4.1, and you should still chain it

Half the guides for this job repeat that atempo only accepts 0.5 to 2.0. That ceiling belonged to FFmpeg 4.0 and earlier. The current filter documentation gives the range as 0.5 to 100, so a single atempo=4.0 runs fine on any build from the last several years.

Chain it anyway, because of what happens above 2.0. Past that ratio the filter skips samples instead of blending overlapping windows, which is cheap and audible: transients smear and speech picks up a metallic edge. Two stages of 2.0 stay inside the blending range, so a 4x timelapse with narration sounds like a person talking fast, not a robot. The 0.5 floor is still a real floor. For anything slower than half speed you have to chain, which is why 0.25x is atempo=0.5,atempo=0.5.

One API call, with no filter_complex involved

Because setpts and atempo are both single-input filters, a speed change is one of the few composite video jobs that runs cleanly on an args-based API. Nearly every tutorial and every competing API template wraps this job in -filter_complex "[0:v]setpts=PTS/1.25[v];[0:a]atempo=1.25[a]", which teaches people that a sandboxed API can't do retiming at all.

FFmpeg Micro does not accept `-filter_complex`, on purpose: a filter graph can read arbitrary files, invoke commands, and build arbitrarily expensive graphs. -vf and -af are allowed, and they're all this job needs. A 2x speed-up on an uploaded file:

curl -X POST https://api.ffmpeg-micro.com/v1/transcodes \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inputs": [{ "url": "gs://<YOUR_BUCKET>/1234567890-interview.mp4" }],
    "outputFormat": "mp4",
    "options": [
      { "option": "-vf", "argument": "setpts=PTS/2" },
      { "option": "-af", "argument": "atempo=2.0" },
      { "option": "-c:v", "argument": "libx264" },
      { "option": "-crf", "argument": "23" }
    ]
  }'

The response comes back immediately with a job id and "status": "pending". You poll GET /v1/transcodes/:id until it reads completed, then GET /v1/transcodes/:id/download for a signed URL. Status checks, listing jobs, and downloads are all free, so a tight polling loop costs nothing.

For a 4x timelapse, change two arguments: setpts=PTS/4 and atempo=2.0,atempo=2.0. Nothing else about the request moves.

Slow motion is billed on the source, not the stretched output

Retiming is where billing models diverge hardest, so do the arithmetic first. FFmpeg Micro meters on input duration, per second, with no rounding up to the minute, and failed jobs are never billed. One token buys 10 seconds of input.

Take a 30-second clip to 0.5x. The rendered file is 60 seconds long. You're billed for the 30 seconds you supplied: 3 tokens. A credit API that charges per minute of rendered video bills you for the 60 seconds it produced, so the slower you go, the wider the gap gets.

The honest flip side is a timelapse. A one-hour source compressed to 4x produces a 15-minute file, and you're billed for the hour: 3,600 seconds, so 360 tokens. The free tier's 200 tokens, about 33 minutes of video, won't cover it, and a 500-token top-up is $5. Trim first if you only need part of the recording: pull a 30-second window out of a one-hour file and you pay for 30 seconds, not the hour.

Long sources also land in a different queue. Inputs over two minutes route to FFmpeg Micro's long lane on its own server, so a one-hour encode can't hold up the 15-second clips behind it. The status payload returns started_at, lane, and queue_position, and started_at minus created_at is an upper bound on how long you waited. That's also the number you want for building progress reporting around these jobs.

Running it from n8n, Make, or Zapier

None of the no-code platforms can shell out to FFmpeg, and n8n's Execute Command path has been closing off for a year, so a speed change there is an HTTP Request node with the JSON body above, a Wait node, and a second request to poll. More than 300 builders run FFmpeg Micro from Make or n8n, split 151 Make and 100 n8n among tagged accounts, plus older accounts identified by API key names. The same pattern as FFmpeg in n8n without touching your Docker image applies here: submit, poll, download.

Common pitfalls

Most failures in this job come from treating the two filters as optional halves of each other.

  • Passing -vf setpts with no -af. The audio keeps its original length, the muxed output runs as long as the longer stream, and your voice drifts further out of sync with every second. If you want the track gone, say so with -an rather than leaving it unretimed. Swapping it for a different track is a separate job.
  • Retiming a variable-frame-rate source. Screen recordings from OBS and most phone captures are VFR, and setpts on uneven timestamps gives uneven motion. Add -r 30 to normalize the output to constant frame rate.
  • Expecting smooth slow motion from 30fps footage. At 0.5x you have 15 real frames per second of output and the rest are duplicates, so it judders. Shoot 60fps or 120fps if slow motion is the point. Interpolation filters that invent the missing frames are slow and aren't present in every FFmpeg build.
  • Forgetting the subtitle timings. Caption cues are absolute timestamps, so a sped-up video needs every cue rescaled by the same factor or the captions land in the wrong place. The timing math behind SRT is the same arithmetic as setpts, run on text.

When a speed API is the wrong fit

A fixed-factor retime covers most of what pipelines need, not all of it. A keyframed speed curve, 1x easing down to 0.25x over 30 frames and back, isn't one filter expression. You get it by cutting the clip into segments, rendering each at its own factor, and concatenating the results: several jobs instead of one. If your product needs an artist dragging ease handles on a timeline, that's a timeline editor, not an API, and FFmpeg Micro is an API. Playback speed that the viewer controls is a player feature and shouldn't be encoded at all.

If what you want is 0.5x, 1.5x, 2x, or 4x with the audio pitch intact, the Speed Changer blueprint is this whole post as a form: upload, pick the dial, download. It runs on the free tier's 200 tokens with no card at sign-up, which is enough to retime a few dozen clips and hear whether the chained atempo holds up on your footage.

FAQ

Does speeding up a video always change the audio pitch?

Speeding up a video only changes the audio pitch if the audio is resampled instead of time-stretched. FFmpeg's atempo filter changes duration while holding frequency constant, so a 2x speed-up with -af "atempo=2.0" keeps the speaker's voice at its normal pitch. APIs that expose a single speed multiplier usually resample, which is why their docs describe pitch shift as expected behavior.

Is the atempo filter limited to 0.5 to 2.0?

The atempo filter accepts 0.5 to 100 in FFmpeg 4.1 and later. The 0.5 to 2.0 range people still quote was the limit in FFmpeg 4.0 and older. Above a tempo of 2.0 the filter skips samples rather than blending overlapping windows, so chaining two 2.0 stages for a 4x change sounds noticeably better than one 4.0 stage, but it isn't required.

Do I need setpts, or will -r on its own change the speed?

You need setpts for a reliable speed change. Setting -r alone tells FFmpeg a different output frame rate, and depending on the source it may duplicate or drop frames rather than retime the stream. Use setpts to retime and -r afterward only to pin the output to a constant frame rate.

Am I charged for the slowed-down length or the original clip?

FFmpeg Micro charges on input duration, so a 30-second clip slowed to 0.5x costs 30 seconds of metering even though the output file is 60 seconds long. Billing is per second with no rounding up to a minute, and failed jobs are never billed. For a timelapse the trade runs the other way: a one-hour source is billed as one hour regardless of how short the output is.

Can I change video speed in n8n without installing FFmpeg?

You can change video speed in n8n with an HTTP Request node and no FFmpeg binary in your image. Post the setpts and atempo options to /v1/transcodes, wait, then poll the job id until the status reads completed. The same three-step pattern works in Make, Zapier, or any tool that can call a REST endpoint.

About Javid Jamae

Founder & CEO at FFmpeg Micro

Javid is a software engineer, author, and entrepreneur with over 25 years of professional software development experience across enterprise, startup, and consulting environments. He founded FFmpeg Micro to make video processing accessible to developers through a simple, automation-first REST API.

Software EngineeringVideo ProcessingFFmpegCloud ArchitectureAPI DesignAutomation

Skip the command line

The Speed Changer blueprint retimes your video with pitch-corrected audio: upload, pick the speed, done.

Run it (free)