GetInSaver Developers
JavaScript code with colourful syntax highlighting on a dark screen
Guide · Node.js

Node.js Social Media Downloader API

In Node.js 18 or newer you need no SDK and no npm package: fetch the public link to POST https://developers.getinsaver.com/v1/resolve, read the items array, and stream each file to disk. The same request works for Instagram, TikTok, X, Pinterest and Telegram; only the service ID changes.

Node.js fetch call to the GetInSaver API for a TikTok link, and the JSON response with a video URL

Key facts

Endpoint
POST /v1/resolve (one endpoint for every service)
Free trial
20 result credits after email verification, no card
Price
from $19 for 7,500 results ($1.33–$2.53 per 1,000)
Billing
prepaid credits, no subscription, no expiry; failed or empty requests cost 0
Credits per result
1 (Instagram, TikTok, X, Pinterest) or 2 (Stories, Highlights, Telegram)
Limits
up to 20 items per request; 120 requests per minute per account; 2 in progress at once on the trial, 8 on paid accounts

Steps

  1. Create a free account and verify your email. You get 20 free credits, no card needed.
  2. Create an API key in Dashboard → API keys and store it as the environment variable GIS_KEY on your server.
  3. Save the script below as resolve.mjs and run GIS_KEY=gis_live_... node resolve.mjs.

Complete Node.js example

// Node.js 18+ (built-in fetch, no packages). Run: GIS_KEY=gis_live_... node resolve.mjs
import { createWriteStream } from "node:fs";
import { mkdir } from "node:fs/promises";
import { Readable } from "node:stream";
import { pipeline } from "node:stream/promises";

const API_URL = "https://developers.getinsaver.com/v1/resolve";
const EXT = { "video/mp4": ".mp4", "image/jpeg": ".jpg", "image/png": ".png", "image/webp": ".webp", "audio/mpeg": ".mp3" };
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function resolveLink(service, url, maxResults = 10, retries = 3) {
  for (let attempt = 0; ; attempt++) {
    const response = await fetch(API_URL, {
      method: "POST",
      headers: { Authorization: `Bearer ${process.env.GIS_KEY}`, "Content-Type": "application/json" },
      body: JSON.stringify({ service, url, max_results: maxResults }),
      signal: AbortSignal.timeout(130_000)
    });
    const data = await response.json();
    if (response.ok) return data;
    // 429 = rate or concurrency limit, 502/504 = platform hiccup; none of them is charged
    if ([429, 502, 504].includes(response.status) && attempt < retries) {
      await sleep(Number(response.headers.get("retry-after") || 5) * 1000);
      continue;
    }
    throw new Error(`${data.code}: ${data.message}`);
  }
}

// Media URLs expire within minutes to hours, so save the file right away.
async function download(item, name, folder = "downloads") {
  const media = await fetch(item.url, { signal: AbortSignal.timeout(120_000) });
  if (!media.ok) throw new Error(`download failed: HTTP ${media.status}`);
  await mkdir(folder, { recursive: true });
  const type = (media.headers.get("content-type") || "").split(";")[0];
  const path = `${folder}/${name}${EXT[type] || ".bin"}`;
  await pipeline(Readable.fromWeb(media.body), createWriteStream(path));
  return path;
}

const result = await resolveLink("instagram", "https://www.instagram.com/reel/SHORTCODE/");
console.log(`${result.item_count} items, ${result.credits_used} credits`);
for (const [i, item] of result.items.entries()) {
  console.log(item.type, await download(item, `${result.request_id}-${i}`));
}

Call the API from your server, a worker or a serverless function. Never from browser JavaScript: the key would be visible to every visitor. To put a download box on a website, see the downloader website guide, which includes a small backend.

What the API returns

{
  "ok": true,
  "service": "instagram",
  "source_url": "https://www.instagram.com/reel/SHORTCODE/",
  "item_count": 1,
  "items": [
    {
      "type": "video",
      "url": "https://cdn.example/media.mp4"
    }
  ],
  "metadata": {
    "title": null,
    "author": "creator",
    "caption": null
  },
  "request_id": "39a3bff5-0000-4000-8000-000000000000",
  "credits_used": 1
}

Service IDs

serviceCoversCredits / resultLink format
instagramInstagram Posts & Reels1instagram.com/p/...
instagram-storyInstagram Stories2instagram.com/stories/username/...
instagram-highlightInstagram Highlights2instagram.com/stories/highlights/...
tiktokTikTok1tiktok.com/@user/video/...
xX / Twitter1x.com/user/status/...
pinterestPinterest1pinterest.com/pin/...
telegramTelegram Public Media2t.me/channel/123

Errors to handle

HTTPCodeWhat to do
402INSUFFICIENT_CREDITSTop up, or lower max_results: a request reserves max_results × credits per result first.
422UPSTREAM_REJECTEDThe post is private, deleted, expired or login-only. Show the message to your user; nothing was charged.
429RATE_LIMITED / CONCURRENCY_LIMITWait for Retry-After seconds and retry. 120 requests per minute per account; 2 at once on the trial, 8 after buying credits.
502 / 504UPSTREAM_*Temporary platform failure. Safe to retry; nothing was charged.

Every error body is JSON with a stable code and a readable message. The full list is in the API reference.

FAQ

Is there an npm package?

Not needed. The API is one JSON endpoint, so built-in fetch is the whole client. The OpenAPI file at /openapi.json can generate a typed client if you want one.

Does it run on serverless platforms?

Yes, if the platform allows outbound HTTPS and a long enough timeout. Requests can take up to two minutes on slow platforms.

Can I use TypeScript?

Yes. The example is plain ES module JavaScript and type-checks with minimal changes; the response shape is documented in the API reference.

Do failed requests cost credits?

No. Credits are reserved while a request runs and refunded in full when it fails or finds no media. You only pay for items that come back.

Updated 2026-10-30. The code on this page is executed by our test suite against the API server before every release.

Make your first call in five minutes.

20 free credits when you verify your email. No card needed.

Create a free account