JSON prompting for video and image APIs: what it changes and what it does not
JSON prompting means writing your prompt as a JSON object instead of a paragraph:
{
"subject": "a lone cyclist on a coastal road",
"action": "pedalling steadily into a headwind",
"camera_motion": "slow tracking shot alongside the subject",
"setting": "cliffside road at sunrise, sea below",
"lighting": "low golden sun, long shadows",
"style": "documentary, 35mm film"
}
Before the technique: the single most useful fact about it.
The API does not parse your JSON prompt
On this API the prompt field is typed as a plain string — in our Veo 3
adapter it is literally z.string().min(1) — and it is forwarded upstream as a
string too. Nothing validates the keys, nothing reads them as parameters,
nothing errors if you invent a key that means nothing.
JSON prompting works because models are good at reading labelled structure, not because there is a schema behind it. The object above is sent as text that happens to look like JSON.
That has one consequence worth internalising, because it is the mistake that shows up most often in JSON prompt examples you will find elsewhere:
{
"subject": "a lone cyclist",
"duration": 10,
"aspect_ratio": "9:16",
"resolution": "1080p"
}
Those last three keys do nothing. Duration, aspect ratio and resolution are
separate fields in the request body, and the request body is the only part that
is validated. Writing "duration": 10 inside the prompt gets you a clip of
whatever length the real duration_sec field says — usually the model’s
default, because you never set it.
Here is what the same intent looks like when the parameters are where they belong:
{
"model": "kling-2.0-standard",
"prompt": "{\"subject\":\"a lone cyclist\",\"action\":\"pedalling into a headwind\"}",
"duration_sec": 10,
"aspect_ratio": "9:16"
}
The JSON prompt is a string inside the request. The parameters sit beside it.
What JSON prompting is actually good for
Two things, and both are about your workflow rather than the model’s output.
It is diffable. When a generation comes out wrong, you change one key and re-run. With a paragraph you rewrite the whole thing and cannot tell which clause moved the result. This matters most when you are iterating on camera work, where a single phrase changes everything.
It is programmatic. An agent assembling a prompt can set camera_motion
directly instead of splicing English. If you are calling this API from code —
which, if you are reading this, you probably are — building an object and
JSON.stringify-ing it is simply less error-prone than template strings.
What it is not reliably good for is raw quality. There is no published evidence that JSON prompts beat well-written prose on the same content, and we have not run that comparison ourselves, so we are not going to claim it. Treat structure as an authoring convenience with a possible clarity bonus, not as a quality setting.
Keys that earn their place
Use keys the model can act on. A reasonable set for video:
| Key | What it controls |
|---|---|
subject | who or what is in frame |
action | what the subject does — the one thing image prompts never need |
camera_motion | how the camera moves, separate from subject motion |
setting | location and time of day |
lighting | light quality and direction |
style | medium, film stock, rendering |
mood | emotional register |
For images, drop action and camera_motion and add composition.
Leave a key out rather than setting it to "". An empty string is still a
value and models sometimes read it as a constraint — “no lighting” is not what
you meant.
Duration is where JSON prompts get punished
Because duration lives in the request body, not the prompt, you have to respect each model’s real constraint:
| Model | model | duration_sec |
|---|---|---|
| Seedance 2.0 Fast | seedance-2.0-fast | any integer 3–15 |
| Kling 2.0 Standard | kling-2.0-standard | 5 or 10 only |
| Veo 3 | veo3 | 8, and nothing else |
Kling’s durationSec is a union of exactly 5 and 10. Asking for 7 is a
400 INVALID_PARAM, not a clip rounded up to 10. Veo 3’s is a literal 8 —
the upstream model has no other length, so it is not a parameter you can tune.
If a prompt-building agent picks durations freely, that is where it will break first. Clamp to the model’s allowed set before you send.
Generating both halves
The JSON Prompt Generator builds the JSON prompt and the request body that carries it, with the parameter options restricted per model to the values the API accepts. For what the duration limits do to cost — Veo 3 billing 8 seconds when you wanted 5 is a real overpayment — see the AI API Cost Comparison.
Models covered on this page
Where these facts come from
- codebase: src/ai/providers/veo3.ts — paramsSchema, prompt is z.string()
- codebase: src/ai/providers/kling.ts — durationSec is a 5 | 10 union
- codebase: src/ai/providers/seedance.ts — durationSec is an int range 3–15
- codebase: src/app/api/v1/video/generations/route.ts — snake_case request body