Blur and dim a video background so captions stay readable via API

Use video-filter with a dim op and a gblur filtergraph: $0.02 per encode, a free check first, 300 s source limit. Whole-frame only. Read 2026-10-10.

4 min readSume
All posts

Send one clip to video filter with a dim op of about 0.45 and a gblur filtergraph. You get a darker, softer copy of the whole frame for $0.02 per encode job, and the check that proves the program is valid before you pay is free.

The pattern fits a common problem: text or captions sit on top of a busy clip and lose contrast. Dimming the luma and blurring the picture are the two cheapest fixes, and both are inside the allowlist that Sume compiles on its own worker. The table captions below cite the docs page as read 2026-10-10.

What the two pieces do

ops[] is the ordered, validated program. A dim op takes one number, amount, in the range greater than 0 and up to 1. At 0.45 the picture is darker, at 1 nothing changes, and 0 or anything above 1 is refused with video_filter_amount_out_of_range. Black stays black and chroma does not move, because Sume multiplies luma only.

filtergraph is a filters-only graph that Sume applies after ops[]. The allowlist in VIDEO_FILTER_GRAPH_ALLOWED_FILTERS has four blur filters: boxblur, gblur, avgblur and smartblur. Sume adds the input and output labels itself, so you send only the filter expression.

Order matters. Sume runs the ops first and the filtergraph second, so the dim lands on the sharp picture and the blur then smooths the result. Stream specifiers such as [0:v], file paths and external inputs are refused with invalid_filtergraph, and so is any filter that is not on the list, with unknown_filter naming the token and the allowlist.

Run the free check first

POST /v1/video-filter/check runs the same schema, allowlist and source-preflight checks as the encode. It does not create a job, reserve credits or start the encoder, and it needs no Idempotency-Key. A valid answer has valid, encode: "not_run", the compiled filter names and an estimate, plus a next_action of submit_video_filter or fix_program_and_recheck.

A program that passes can still fail on the worker, for example on a bad expression or memory. In that case you get a structured job error rather than a validation answer. Keep that in mind before you queue 200 clips.

curl -X POST https://api.sume.com/v1/video-filter/check \
  -H "Authorization: Bearer $SUME_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "video_url": "https://media.sume.com/artifacts/artf_demo/talk.mp4",
    "ops": [{ "op": "dim", "amount": 0.45 }],
    "filtergraph": "gblur=sigma=12"
  }'

Limits that matter for this recipe

The source must already be a Sume-hosted clip, so a clip from elsewhere has to be imported or generated on Sume first. A source longer than 300 seconds fails with output_duration_exceeded; cut it with video trim first.

Video filter limits, from the Video filter page, read 2026-10-10
ItemValue
Encode price$0.02 per job
Check priceFree
Ops per request8 at most
Filtergraph length2048 characters, 32 named filters
Source length300 s at most
Source locationmedia.sume.com clip in your workspace

What this does not do

The blur and the dim cover the entire frame. This page documents no region mask or face-tracking blur, and drawtext and subtitles are off the allowlist, so you cannot burn text in with this route. Captions are a separate job: video captions has a fixed estimate for clips of 60 seconds or less, and the filter docs point caption plates to the HyperFrames compose and caption assembler path.

If the blur is too strong, lower sigma and recheck; the check is free, so tuning costs nothing until you encode. If the clip still reads busy, raise the dim amount toward 0.35 instead of stacking more blur. The output keeps the geometry, frame rate and audio of the source unless the program changes them.

Cost for a batch

One encode is $0.02, so 50 clips cost 50 x $0.02 = $1.00 and 200 clips cost $4.00. Because there is no provider inference, the price does not rise with clip length inside the 300-second limit. Submit each paid encode with its own Idempotency-Key so a retry after a timeout does not create a second charge.

Default mode is async, so poll GET /v1/jobs/:id/status and then GET /v1/jobs/:id/result. The result has a new video_url, never the source, plus ops_applied and the compiled filters[], which lets you confirm that the dim and the blur both ran before you hand the new MP4 to the next step.

Sources

Related posts

More in Media tools

All Media tools posts

Written by Sume