saveapi.org

Download social media videos in n8n with an HTTP Request node

n8n's HTTP Request node is the whole integration: one GET with a Header Auth credential and the API returns JSON with direct media URLs. The rest of the workflow is deciding what to do with them. This guide is the five nodes, in order, with the two settings that people miss — sending the link as a query parameter, and branching on success before touching medias[].

Updated

Store the key as a Header Auth credential

In n8n go to Credentials → New → Header Auth. Name: Authorization. Value: Bearer followed by your key. The credential is encrypted at rest and never appears in the workflow JSON, which matters the moment you export or share the workflow.

The HTTP Request node

Method GET, URL https://api.saveapi.org/v1/download, Authentication → Generic Credential Type → Header Auth, pick the credential. Turn on Send Query Parameters and add one parameter, url, with an expression pointing at the incoming link. The node encodes the value for you — do not build the query string by hand.

JavaScript
// HTTP Request node
{
  "method": "GET",
  "url": "https://api.saveapi.org/v1/download",
  "authentication": "genericCredentialType",
  "genericAuthType": "httpHeaderAuth",      // Header Auth credential from step 1
  "sendQuery": true,
  "queryParameters": {
    "parameters": [{ "name": "url", "value": "={{ $json.link }}" }]
  }
}

Branch on success before you touch medias[]

The API answers 200 with success: true and medias[], or an error status with success: false and a stable error.code. A Code node turns that into n8n items: one item per media on success, one error item otherwise. Downstream nodes then see a flat list with type and url, which is the shape every other node wants.

JavaScript
// Code node — one item per media, or one error item
const data = $input.first().json;

if (!data.success) {
  return [{ json: { failed: true, code: data.error.code, message: data.error.message } }];
}

return data.medias.map((media, index) => ({
  json: {
    index,
    type: media.type,          // "video" | "image" | "audio"
    url: media.url,
    title: data.meta?.title ?? "",
  },
}));

Send the file where it needs to go

For Telegram, a Telegram node with Send Video (or Send Photo when type is image) takes the url directly — Telegram fetches it. For Drive, S3 or a database, add a second HTTP Request node with the media url, set Response Format to File under Options, and pass the binary on. The API never proxies the file, so this fetch goes straight to the platform's CDN.

Retries and rate limits belong in the node settings

Open the HTTP Request node's Settings and enable Retry On Fail with a wait of a few seconds — but only for the codes that a retry can fix. UPSTREAM_TIMEOUT and UPSTREAM_ERROR are worth a second attempt and cost nothing; PRIVATE_CONTENT and MEDIA_NOT_FOUND are final and cost 0.1 credits each time. Put an IF node on the error code before any loop, and keep the workflow's rate under the key's per-minute limit.

Questions before you build

Can I use the Webhook node to trigger this?
Yes — a Webhook node that receives a link is the usual trigger. The HTTP Request node reads it as $json.link (or whatever field your webhook body uses).
Does n8n need to download the file?
Only if you want to store it somewhere n8n writes to. For Telegram and most chat targets, passing the media url is enough and keeps the file off your n8n instance.
What does one workflow run cost?
One successful resolve is 1.5 credits (5 for an Instagram story). A private or deleted link costs 0.1; a timeout costs nothing. The Code node in step 3 exposes the error code so you can count them.

Platform references used here

Other guides

Run this guide with your own key

Key in under a minute. 1,000 free credits, no card, no expiry.

Get a key and try itFull API reference