saveapi.org

Download social media videos in Go

The Go client is a request with one header and a typed decode. What this guide adds is the discipline around it — a context deadline so a slow source cannot hang a goroutine, a struct that makes the error code a field rather than a string search, and streaming the file with io.Copy rather than reading it into memory.

Updated

Types first

A struct with Success, Medias and a pointer Error makes the error code a field rather than a string search. Keep the type local and small — a new optional field in the response cannot break a decode that ignores it.

Go
type Media struct {
	Type string `json:"type"`
	URL  string `json:"url"`
}

type Resolved struct {
	Success bool    `json:"success"`
	Medias  []Media `json:"medias"`
	Error   *struct {
		Code    string `json:"code"`
		Message string `json:"message"`
	} `json:"error"`
}

The request

http.NewRequestWithContext carries the deadline; url.QueryEscape encodes the link. One header, one decode. Nothing else is needed on your side — no cookies, no session, no browser.

Go
func resolve(ctx context.Context, link string) (*Resolved, error) {
	u := "https://api.saveapi.org/v1/download?url=" + url.QueryEscape(link)
	req, _ := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
	req.Header.Set("Authorization", "Bearer sk_live_xxxx")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	var out Resolved
	return &out, json.NewDecoder(res.Body).Decode(&out)
}

Stream the file with io.Copy

The media URL points straight at the platform's CDN. io.Copy from the response body to a file keeps memory flat whatever the file size; the API never proxies the bytes.

Go
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

data, err := resolve(ctx, "https://www.instagram.com/reel/ABC123/")
if err != nil || !data.Success {
	log.Fatal(err, data.Error.Code)
}

res, _ := http.Get(data.Medias[0].URL)   // to'g'ridan-to'g'ri CDN
defer res.Body.Close()
f, _ := os.Create("video.mp4")
defer f.Close()
io.Copy(f, res.Body)                      // oqim, xotiraga emas

Retry only what a retry can fix

UPSTREAM_TIMEOUT and UPSTREAM_ERROR deserve a second attempt and cost nothing. PRIVATE_CONTENT and MEDIA_NOT_FOUND are final and cost 0.1 credits each time — a retry loop on those is a way to spend credits on the same answer. Keep the retryable set in one place.

Read the headers you already have

Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and, on paid keys, X-Credits-Remaining. That is enough to back off before you are throttled without a separate balance call.

Questions before you build

Is there an official Go SDK?
No. The typed struct and the request in this guide are the SDK; a package would add a dependency for eight lines.
How do I handle formats[]?
Add a Formats []Format field to the struct for VK and Rutube; elsewhere the field is simply absent and stays empty.
Can I resolve many links concurrently?
Yes, within the key's per-minute limit. A semaphore sized to the limit and the X-RateLimit-Remaining header keep you from being throttled.

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