saveapi.org

Download social media videos in PHP

PHP's curl extension is the whole client. The parts worth writing down are the ones that differ from a browser fetch: encoding the link into the query string, timing out a slow source, streaming a large file to disk instead of into a string, and retrying only what a retry can fix.

Updated

One function

curl with http_build_query for the link — the value must be URL-encoded, and a link containing & will break a hand-built query string. CURLOPT_TIMEOUT keeps a slow source from holding the request; thirty seconds is above the API's own upstream budget.

PHP
<?php
function resolve(string $link): array {
    $ch = curl_init("https://api.saveapi.org/v1/download?" . http_build_query(["url" => $link]));
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 30,
        CURLOPT_HTTPHEADER     => ["Authorization: Bearer sk_live_xxxx"],
    ]);
    $body = curl_exec($ch);
    curl_close($ch);
    return json_decode($body, true) ?? ["success" => false, "error" => ["code" => "UPSTREAM_ERROR"]];
}

Read medias[]

Every response carries the same array, one entry per file in post order, each with a type. Branch on type rather than assuming a video: a TikTok slideshow and an Instagram carousel come back as image entries.

PHP
$data = resolve("https://www.instagram.com/reel/ABC123/");

if (!$data["success"]) {
    throw new RuntimeException($data["error"]["code"]);
}

foreach ($data["medias"] as $media) {
    echo $media["type"], " ", $media["url"], PHP_EOL;   // video | image | audio
}

Stream the file to disk

CURLOPT_FILE writes the download straight into a handle. Loading a 4K reel into a PHP string is the fastest way to hit memory_limit; a stream never does.

PHP
// Faylni diskka oqizish — xotiraga yuklamasdan
$out = fopen("video.mp4", "wb");
$ch  = curl_init($data["medias"][0]["url"]);
curl_setopt_array($ch, [CURLOPT_FILE => $out, CURLOPT_FOLLOWLOCATION => true, CURLOPT_TIMEOUT => 120]);
curl_exec($ch);
curl_close($ch);
fclose($out);

Retry only what a retry can fix

UPSTREAM_TIMEOUT and UPSTREAM_ERROR are worth one more attempt and cost nothing. PRIVATE_CONTENT, MEDIA_NOT_FOUND and UNSUPPORTED_PLATFORM are final — the second call gives the same answer and, for the billed ones, spends another 0.1 credits. Act on the code, never on the message text.

A single retry, in code

One retry, after a short sleep, only for the two retryable codes. Wrap this in your own queue if you resolve in bulk; the per-minute limit is in the response headers as X-RateLimit-Remaining.

PHP
$retryable = ["UPSTREAM_TIMEOUT", "UPSTREAM_ERROR"];

$data = resolve($link);
if (!$data["success"] && in_array($data["error"]["code"], $retryable, true)) {
    sleep(2);
    $data = resolve($link);      // faqat bir marta, faqat shu kodlar uchun
}

Questions before you build

Is there a Composer package?
No, and one endpoint does not need one — the whole client is the function in step one.
Does the file pass through the API?
No. You receive a CDN URL and fetch it directly; download speed depends on that CDN, not on the API.
Can I call this from a shared host?
Yes — it is plain curl over HTTPS. What matters is that your host allows outbound requests, which almost all do.

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