The Gemini Video File Limit Isn't One Number. It's Four.

You uploaded a 90-minute screen recording to the Gemini API and got back a 400 Bad Request with nothing useful in it. The file was well under 2 GB, so size wasn't the problem, and now you're guessing which limit you crossed. There are four of them, and each maps to a different FFmpeg flag.
Quick answer: The Gemini video file limit is 100 MB for an inline request, 2 GB per file through the Files API (with 20 GB of project storage, deleted after 48 hours), and a hard 400 Bad Request once a video runs past roughly one hour at default media resolution. Clear all of them in one pre-upload pass with FFmpeg: ffmpeg -i in.mp4 -vf "scale=960:-2,fps=1" -c:v libx264 -crf 30 -ac 1 -ar 16000 -b:a 24k out.mp4, then split anything still over an hour into five-minute segments on forced keyframes. If you'd rather not install FFmpeg or babysit a two-hour encode, send the same prep job to FFmpeg Micro as one API call and get the downsampled file and chunk offsets back.Which Gemini limit your 400 actually came from
Four separate constraints produce rejections, and the error body doesn't say which one fired. Google's video understanding docs and the Files API reference list them in different places.
| Limit | Value | Where it applies |
|---|---|---|
| Inline request payload | under 100 MB (raised from 20 MB in January 2026) | base64 in the request, or a GCS object / signed URL |
| Files API, per file | 2 GB | `files.upload`, then reference the file URI |
| Files API, per project | 20 GB total, files deleted after 48 hours | your whole upload bucket |
| Duration | 400 Bad Request past about 1 hour at default media resolution | every path, including a signed URL |
| Videos per request | 10 | Gemini 2.5 and later |
Most walkthroughs still get the January 2026 change wrong. The Gemini API file limits announcement moved the inline path to 100 MB and added Cloud Storage objects and HTTP or signed URLs as inputs. Any guide citing 20 MB predates it.
The 48-hour expiry changes your retry logic: if a job fails Tuesday and your queue retries Friday, the file URI is gone. Keep the source bytes, not just the URI.
The one-hour limit is a token budget, not a duration rule
Gemini's hour ceiling isn't a policy number, it's arithmetic on the context window, which is why it moves. Per Google's media resolution docs, a sampled frame costs 258 tokens at default media resolution and 66 at low, and audio costs 32 tokens per second. Gemini samples at 1 frame per second by default, so one second of video is about 290 tokens at default resolution, 98 at low.
Multiply out: 3,600 seconds at 290 tokens is roughly 1.04 million, just past a 1M context window. That's the hour. Drop to mediaResolution: MEDIA_RESOLUTION_LOW and the same budget buys about three hours, which matches the per-model table on the Interactions API video page.
The encode and the request parameter solve different problems, and people conflate them constantly. Downsampling fixes the size limits and the upload time. It doesn't reduce your token bill, because tokens are set by sampling rate and media resolution, not the bitrate you shipped. Lowering mediaResolution or a custom fps in videoMetadata is what cuts tokens. Do one and you still 400; do both and you don't.
Encode to what Gemini actually keeps
Gemini stores video at 1 frame per second and, through the Files API, keeps audio at 1 kbps single channel. Shipping a 4K 60 fps master uploads roughly 59 of every 60 frames for the service to discard. An open Gemini forum thread asking for optimal pre-processing fps and resolution has gone unanswered by Google, so this is reasoning, not a blessed number.
Frame tokens are flat per frame, so resolution above what the model's tiling keeps earns nothing. 960 wide is a safe target that still reads on-screen text in a screen recording. The size budget is bitrate math: 100 MB across an hour is 222 kbps total, video plus audio. At 1 fps with CRF 30, a talking-head or screen-capture source lands well inside that.
Probe first so you know which limit you're against:
ffprobe -v error -show_entries format=duration,size,bit_rate \
-of default=noprint_wrappers=1 long-recording.mp4
Then the prep pass:
ffmpeg -i long-recording.mp4 \
-vf "scale=960:-2,fps=1" \
-c:v libx264 -preset veryfast -crf 30 -g 10 -pix_fmt yuv420p \
-c:a aac -ac 1 -ar 16000 -b:a 24k \
-movflags +faststart gemini-ready.mp4
scale=960:-2 keeps the height even, which avoids libx264's divisible-by-two complaint. -g 10 puts a keyframe every ten seconds at 1 fps, which matters for the split below. Mono 16 kHz at 24 kbps already beats the 1 kbps mono Gemini stores, so going higher gains nothing. Pick a target and encode to it once, the same discipline as encoding one file at spec instead of a bitrate ladder.
The same job as one API call, if you don't want FFmpeg on the box running your agent:
{
"inputs": [{ "url": "gs://<YOUR_BUCKET>/1234567890-long-recording.mp4" }],
"outputFormat": "mp4",
"options": [
{ "option": "-vf", "argument": "scale=960:-2,fps=1" },
{ "option": "-c:v", "argument": "libx264" },
{ "option": "-crf", "argument": "30" },
{ "option": "-g", "argument": "10" },
{ "option": "-ac", "argument": "1" },
{ "option": "-ar", "argument": "16000" },
{ "option": "-b:a", "argument": "24k" }
]
}
That's POST /v1/transcodes on FFmpeg Micro: presign, upload, confirm, submit, poll, download. The output is a gs:// object with a signed download URL, which Gemini's inline path accepts since the January change, so it goes straight into the model call. Billing is on input duration, per second with no rounding up, and failed jobs aren't billed, so a two-hour recording costs 720 tokens whether our workers take four minutes or forty. The quickstart runs the whole flow on a temporary key.
Split long recordings without losing your offsets
Five minutes per chunk is where the Gemini forum lands when developers ask how to analyze videos over an hour: short enough that a chunk's answer stays specific, long enough that boundary-spanning events are rare.
The widely copied recipe is -c copy -f segment -segment_time 600, and stream copy only cuts on keyframes, so segments land on the nearest keyframe, not at 600 seconds exactly. A two-hour file with a 4-second GOP can drift a few seconds per cut. Compute offsets as index × 600 and every timestamp after the first chunk is wrong, worse down the file.
Two fixes: force keyframes at the boundaries during the prep encode, or read the real boundaries from the segment muxer:
ffmpeg -i gemini-ready.mp4 -c copy -map 0 \
-f segment -segment_time 300 -reset_timestamps 1 \
-segment_list segments.csv -segment_list_type csv \
chunk-%03d.mp4
segments.csv gives you one line per chunk with the actual start and end in source time: chunk-000.mp4,0.000000,300.000000. Use that as the source of truth for offsets instead of your own multiplication. Add five to ten seconds of overlap between chunks if the model is finding events, then dedupe by timestamp proximity on the way back.
Stitch chunk answers back to source timecode
Converting a chunk-relative answer to a source timestamp is one addition: source_seconds = chunk_start + model_reported_seconds, with chunk_start read from the segment list. Gemini answers in MM:SS relative to the file it was given, the chunk, not your recording.
One case needs multiplication instead. If you used the flipbook trick (extract one frame every N seconds and reassemble them at 1 fps, which the GDELT project used to push a single prompt to 2.5 hours), you compressed the timeline by N, so source time is chunk_start + reported × N. That's the same offset arithmetic that bites when turning a chunked transcription API response into SRT.
When not to split
Splitting is the wrong move when you only care about part of the recording. Gemini's videoMetadata takes startOffset and endOffset, so you can scope a request to minutes 40 through 50 of a two-hour file without cutting. Tokens are charged on the window you asked for, not the whole upload.
Where a real cut still wins: the file is over 2 GB, you want parallel calls across chunks instead of ten sequential windowed requests, or you're keeping the prepped chunks as artifacts for a repurposing pipeline. If the chunks become clips rather than prompts, that's long-form to shorts repurposing and the cut is the point.
Common pitfalls
A few Gemini upload mistakes cost more debugging time than the rest, in rough order of frequency:
- Base64 inflates payloads by about a third, so a 90 MB file can push an inline request past 100 MB. Use the Files API or a signed URL above roughly 70 MB.
- Re-encoding to 1 fps is irreversible. If you later want
fps: 2invideoMetadata, the frames aren't in the file. Keep the original. -anlooks like free savings at 32 tokens per second, but if the model needs to timestamp spoken moments, you removed the signal it uses.- Ten videos per request is the ceiling on Gemini 2.5 and later, so a 12-chunk recording needs two calls regardless of context room.
- An n8n Google Gemini node returning 400 on video input is usually the duration or payload limit, not the model or MIME type. Builders have burned threads swapping models when the fix was upstream, in the file. More than 300 builders run their FFmpeg step from Make or n8n over HTTP, since those environments have no FFmpeg binary.
One published workaround uploads your video to YouTube as Unlisted and passes the URL. That puts a client's private recording on a third-party platform to dodge a 40-second encode. Don't.
FAQ
Do the 5-minute and 1-hour Gemini app limits apply to my API key?
The consumer Gemini app caps (around 5 minutes of video on the free tier, longer on Pro and Ultra) don't apply to Gemini API requests. The API limits are the inline 100 MB payload, 2 GB per Files API upload, 20 GB of project storage, and the duration ceiling set by context window and media resolution.
How long should each Gemini video chunk be?
Five minutes per chunk is a good default for the Gemini API: about 87,000 tokens at default media resolution, well inside any current context window, and short enough that answers stay specific to one part of the recording.
Does downsampling video reduce Gemini token costs?
Downsampling reduces upload time and file size, not token count. Gemini's token cost per second of video is fixed by the sampling rate and the mediaResolution setting, so cutting tokens means MEDIA_RESOLUTION_LOW (66 tokens per frame instead of 258) or a lower custom fps in videoMetadata.
Will -c copy split my video at exact times?
Stream copy with -f segment cuts at the nearest keyframe, not at the exact -segment_time value, so boundaries drift by up to one GOP length. Write a segment list with -segment_list segments.csv -segment_list_type csv and read the real start times from it.
What does prepping an hour of video cost on FFmpeg Micro?
One token covers 10 seconds of input video, so an hour-long recording is 360 tokens for the prep pass, metered per second with no rounding up to the minute. A new free account gets 200 tokens with no card, about 33 minutes of video, and Starter's 2,500 monthly tokens cover about six hour-long recordings.
If your pipeline already calls Gemini, the prep step is the only part you're still hand-building. Sign up free, run the downsample-and-segment job as one API call before the model call, and stop debugging 400s.
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.
You might also like

Speed up / slow down video via API (with pitch correction)
A speed up video API call is two filters, not one: setpts retimes frames, chained atempo stretches audio without pitch shift. Both ways shown, with costs.

Video Encoding
Video encoding for pipelines, not streaming services: the three settings that matter, the exact FFmpeg command, the one-call API version, and encode costs.

Video Transcoding Isn't a Bitrate Ladder. It's One File at Spec
Video transcoding is demux, decode, filter, re-encode, remux. The pipeline version: one file at platform spec as an FFmpeg command, a REST job, or n8n node.
Ready to process videos at scale?
Start using FFmpeg Micro's simple API today. No infrastructure required.
Get Started Free