n8n HTTP Request node
Most n8n tutorials and shared workflows call JSON2Video with n8n's built-in HTTP Request node: one node sends the movie to POST /v2/movies, a Wait node pauses, and a second HTTP Request node checks the status until the video is ready. This page explains that setup, how to put n8n values in the JSON body, and how to fix the errors people hit most.
Use this page if:
- you are following a tutorial or imported a workflow that already has HTTP Request nodes, or
- you are on self-hosted n8n without community nodes.
Building a new workflow from scratch on n8n Cloud or a recent self-hosted version? The official JSON2Video node is simpler: the key goes in a credential, template variables show up as fields, and Render and Wait does the polling for you.
This video uses the Quick start in the dashboard; the steps are the same.
1. Paste your key in the x-api-key header
Every request to JSON2Video carries your API key in a header named x-api-key. Get the key from the API Keys page of the dashboard.
Workflows send it in one of two ways. Use whichever your workflow already has.
Option A: in the node's headers. In the HTTP Request node, turn on Send Headers. Under Header Parameters, set:
| Field | Value |
|---|---|
| Name | x-api-key |
| Value | YOUR_API_KEY |
Do this in every node that calls JSON2Video, including the one that checks the status. A key changed in the first node but not in the second is the most common reason a workflow creates the video and then fails.
Option B: in a Header Auth credential. Set up once and reused by every node:
- In the HTTP Request node, set Authentication to Generic Credential Type and Generic Auth Type to Header Auth.
- Under Header Auth, select Create new credential.
- Set Name to
x-api-keyand Value to your API key. Save. - Select the same credential in the node that checks the status.
If you imported a tutorial's workflow, the credential is already there: open it and replace its Value with your key.
No Bearer, just the key. The name must be exactly x-api-key, with no spaces around it.
For a workflow you share or run in production, create a dedicated key with the Render role, so you can revoke it without touching anything else. See API keys.
2. Create the video
Set up the first HTTP Request node:
| Field | Value |
|---|---|
| Method | POST |
| URL | https://api.json2video.com/v2/movies |
| Send Headers / Authentication | your key, as in step 1 |
| Send Body | on |
| Body Content Type | JSON |
| Specify Body | Using JSON |
| JSON | the body, below |
The body is either a template with variables or a full movie JSON.
A template with variables. This one renders a public example template, a vertical narrated short with three images, with your own first voice-over line:
{
"template": "2QzqAMLjKkS7dpgUupmI",
"variables": {
"text_1": "Hello from n8n. This is my first video."
}
}
Variables you do not send keep the template's sample values. This template also takes text_2, text_3, image_1 to image_3 (public image URLs) and voice (an Azure voice name). To use your own template, copy its ID from the Templates page of the dashboard.
A full movie JSON. The whole video is described in the body. This one makes a vertical video from an image, a title and a narration that come from earlier nodes (see step 3 for the {{ }} parts):
{
"resolution": "instagram-story",
"scenes": [
{
"elements": [
{ "type": "image", "src": "{{ $json.image_url }}", "resize": "cover" },
{ "type": "text", "text": {{ JSON.stringify($json.title) }}, "style": "002" },
{ "type": "voice", "text": {{ JSON.stringify($json.script) }} }
]
}
]
}
Click Execute step. The answer carries the project ID of the new video:
{
"success": true,
"project": "WAEE8PohgVwv2teP",
"timestamp": "2025-05-28T14:57:34.393Z"
}
The video is not ready yet: it renders in the background. Step 4 gets it when it finishes.
Every field the body accepts is in the Create movie reference and the JSON syntax reference.
3. Put n8n values in the JSON body
Switch the JSON field to Expression. n8n fills in {{ }} only when the field is in Expression mode: the Fixed / Expression switch is above the field, and in Expression mode n8n shows a preview of the result under it. In Fixed mode the {{ $json.title }} text is sent as it is, and the render fails with Expressions unsolved. A body pasted from a tutorial or from this page is the usual cause.
Strings go in quotes, numbers and true/false do not:
{
"template": "YOUR_TEMPLATE_ID",
"variables": {
"title": "{{ $json.title }}",
"price": {{ $json.price }},
"show_logo": {{ $json.show_logo }}
}
}
Text written by an AI node, or typed by a person: use JSON.stringify. If the text has a quote (") or a line break, "{{ $json.text }}" breaks the JSON and n8n refuses to send it ("JSON parameter needs to be valid JSON"). JSON.stringify escapes the text and adds the quotes, so write it without quotes around it:
{
"text": {{ JSON.stringify($json.text) }}
}
Arrays and objects work the same way, also without quotes. For example, a list of questions an AI node returned, sent to a template variable:
{
"template": "YOUR_TEMPLATE_ID",
"variables": {
"topic": {{ JSON.stringify($json.topic) }},
"questions": {{ JSON.stringify($json.questions) }}
}
}
Values from an earlier node use its name: {{ $('Google Sheets').item.json.Title }}. If you rename a node, update the expressions that use its name, or they send an empty value.
4. Wait for the video
A render takes from a few seconds to a few minutes. The usual loop is three nodes after the one that creates the video:
- Wait node: 30 seconds.
- HTTP Request node, Check status:
- Method
GET, URLhttps://api.json2video.com/v2/movies. - Turn on Send Query Parameters and add Name
project, Value{{ $('Create the video').item.json.project }}. Use the name of your own POST node. - The same key as the first node: the
x-api-keyheader, or the same credential.
- Method
- Switch node on
{{ $json.movie.status }}:done: the video is ready. Continue the workflow.error: the render failed. The reason is in{{ $json.movie.message }}.- Anything else (
pending,running): send it to a second Wait node (15 seconds) and back to Check status. In the Switch node, turn on Fallback Output for this branch.
Refer to the POST node by name, as above, rather than with {{ $json.project }}: on the second round of the loop, $json is the answer of Check status, where the ID is in movie.project.
When the status is done:
{{ $json.movie.url }}is the MP4. It is public: download it with a plain HTTP Request node, no key needed, or pass the URL to YouTube, Slack, a sheetβ¦{{ $json.movie.duration }},{{ $json.movie.size }},{{ $json.movie.width }}and{{ $json.movie.height }}describe the file.
The file is deleted 7 days after the render. Copy it to your own storage if you need it longer: see File retention.
Two more things to set up in a workflow that runs on its own:
- Cap the loop. Add an IF node before the second Wait that stops when
{{ $runIndex }}goes over, say, 40, so a stuck render can't loop forever. A movie stillrunningafter 15 minutes counts astimeout: treat it likeerror. - A wrong project ID stops the loop. Check status answers
400withMovie <project> not found. Check that the project ID was returned by POST /v2/movies with an API key of this same account.The expression points at the wrong node, or the two nodes use keys of different accounts. Retrying gives the same answer.
To avoid polling altogether, add a webhook destination to the body and catch it with a Webhook trigger in a second workflow. See Webhooks.
All the fields and statuses are in the Get movie status reference.
5. Following a tutorial?
Before you run someone else's workflow:
- Paste your key in every node that calls JSON2Video, or in the credential they share. The tutorial's key, or its placeholder, does not work for you.
- The free plan makes videos of up to 1 minute. Many tutorials make longer ones; on the free plan they fail with
β¦ is longer than your plan allowance (60s). Try with less text, or see the plans.- Replace the author's IDs with yours. An ElevenLabs
connectionID, a voice ID or a template ID in the body belong to the author's account. Use your own connection, or remove theconnectionto use JSON2Video's own voices (they cost credits). Public templates work for anyone; a private template of the author's does not.
6. Common errors
When POST /v2/movies refuses a request, n8n shows the error on the node that created the video. When a render fails later, the reason comes back in movie.message from Check status, with status: "error".
| Message | Where | Fix |
|---|---|---|
Invalid API Key |
The node, any call | The key is missing a piece, has extra text such as Bearer, or the header is not named x-api-key. Paste the key alone, in every node that calls JSON2Video. |
API key not associated with any account |
The node, any call | The key looks right but is not a key of any account: it was deleted, or it comes from the tutorial. Paste your own key. |
API Key not available / API Key not provided |
The node, any call | No key was sent. Turn on Send Headers or select the credential in that node. |
JSON parameter needs to be valid JSON |
n8n, before sending | Text from an earlier node has a quote or a line break. Use {{ JSON.stringify($json.text) }} without quotes around it. See step 3. |
Source URL is required: the image element in Scene #2, Element #1 has no "src"β¦ (code media_src_required) |
POST, the node | An image, video or audio URL arrived empty. Open the node's input and check that the mapped field shows an https:// link. Usually an earlier node returned no URL, or the field path is wrong. |
Source URL is required for image element in Scene #1, Element #2 |
movie.message |
Same as above, for a template variable: the variable that fills the src arrived empty. |
Expressions unsolved: β¦ |
movie.message |
The {{ }} arrived as text. Switch the JSON field to Expression. See step 3. |
Connection my-elevenlabs not found in your list of connections. Add your connection from the dashboard. |
movie.message |
The connection ID is the tutorial author's. Add your own in Connections and use its ID, or remove connection. |
Prompt is empty for image element. Empty string is not allowed in Scene #1, Element #2 |
movie.message |
An AI image prompt arrived empty: the node that writes the prompts returned nothing for it, or the expression points at the wrong field. |
Movie duration (95s) is longer than your plan allowance (60s). Upgrade your plan |
movie.message |
The free plan makes videos of up to 1 minute. Shorten the text or the scenes, or see the plans. |
More messages and their fixes are in Errors.
See also
- n8n end-to-end: the official JSON2Video node.
- Starter kits: example n8n workflows with sample data.
- Generic HTTP integration: the same requests with cURL.
- Create movie and Get movie status.