FastSaverAPI moved. The API and dashboard now live at api.fastsaver.iowhat changed.

Pinterest downloader API for images, videos and idea pins

Resolve any public Pinterest pin — image, video or multi-page idea pin — with one GET request to the Pinterest downloader API. You get the stored file rather than the grid thumbnail, plus dimensions, duration and the description, for 1.5 credits. Instagram, TikTok, X, Facebook and RuTube answer on the same endpoint.

Last updated

Endpoint
GET /fetch
Credits
1.5 per pin
Auth
X-Api-Key header
Media
image pins, video pins, idea pins

coverage

what a pin resolves to

Pinterest runs through the same universal endpoint as the other non-YouTube platforms. Hand it a pin URL; it works out what the pin contains and answers with JSON.

  • Image pins — the stored file plus its pixel dimensions and the pin description.
  • Video pins — an MP4 with duration, and a poster frame in thumbnail_url.
  • Idea pins and carousels — multi-page pins arrive as type: "album" with an items array, one entry per page.
  • GIF-style pins — Pinterest stores most of these as video, so expect type: "video".

Three link shapes work: the canonical /pin/<id>/ permalink, a pin.it short link from the mobile share sheet, and localised hosts such as ru.pinterest.com. Query strings are ignored, so the tracking noise the app appends is harmless.

usage

one request, one pin

A GET with the pin in the url parameter and your key in a header. Nothing else.

GET https://api.fastsaver.io/v1/fetch 1.5 credits
request
curl -G "https://api.fastsaver.io/v1/fetch" \
  --data-urlencode "url=https://pin.it/4kQvXcL9r" \
  -H "X-Api-Key: fs_sk_•••••••••••"
200 OK · response
{
  "ok": true,
  "id": "1125968838649261847",
  "source": "pinterest.com",
  "type": "video",
  "download_url": "https://v1.pinimg.com/videos/mc/720p/...",
  "thumbnail_url": "https://i.pinimg.com/originals/...",
  "width": 720,
  "height": 1280,
  "duration": 21,
  "caption": "Walnut desk build — three-hour timelapse."
}

--data-urlencode instead of gluing the link into the query string is a habit worth keeping: pin links carry their own parameters, and an unencoded & silently truncates the URL your server receives.

An image pin is the same envelope with duration null:

image pin
{
  "ok": true,
  "id": "60446345121012345",
  "source": "pinterest.com",
  "type": "image",
  "download_url": "https://i.pinimg.com/originals/2f/8c/1d/....jpg",
  "thumbnail_url": "https://i.pinimg.com/236x/2f/8c/1d/....jpg",
  "width": 1536,
  "height": 2048,
  "duration": null,
  "caption": "Muted terracotta kitchen, brass fittings."
}

A multi-page idea pin, and a carousel pin, use a different envelope. There is no top-level download_url, width or height — the pages live in an items array instead, and each entry carries its own type, download_url, thumbnail_url, width and height:

idea pin (album)
{
  "ok": true,
  "id": "3096293941226352",
  "source": "pinterest.com",
  "type": "album",
  "items": [
    {
      "type": "image",
      "download_url": "https://i.pinimg.com/originals/a1/4e/90/....jpg",
      "thumbnail_url": "https://i.pinimg.com/originals/a1/4e/90/....jpg",
      "width": null,
      "height": null
    },
    {
      "type": "video",
      "download_url": "https://v1.pinimg.com/videos/mc/720p/...",
      "thumbnail_url": "https://i.pinimg.com/originals/b7/22/05/....jpg",
      "width": 720,
      "height": 1280
    }
  ],
  "thumbnail_url": "https://i.pinimg.com/originals/a1/4e/90/....jpg",
  "duration": null,
  "caption": "Five-step gouache landscape."
}

Two details worth coding around. Image items report width and height as null — Pinterest does not publish per-page dimensions for them, so probe the file if you need them. And a one-page idea pin collapses to a plain image or video response rather than an album, so a branch that only handles "album" will miss it.

quality

the original file versus what the feed shows you

Compare the two URLs in that image response. The thumbnail sits under a 236x path segment; the download sits under originals. Pinterest stores several derivatives of every upload and the browse grid serves the small ones — which is why right-clicking a pin in a feed hands you a 236-pixel-wide JPEG that looks fine as a thumbnail and terrible anywhere else.

download_url points at the stored file, and width and height tell you what you are about to fetch. Read them: "original" means original upload, and a pin re-pinned from a compressed screenshot stays compressed. If you promise print-quality assets, gate on those dimensions.

code

saving a list of pins

Two shapes. The throwaway one — resolve, then pipe the bytes to disk:

shell + jq
curl -sG "https://api.fastsaver.io/v1/fetch" \
  --data-urlencode "url=https://www.pinterest.com/pin/1125968838649261847/" \
  -H "X-Api-Key: fs_sk_•••••••••••" \
| jq -r '.download_url' \
| xargs -r curl -sL -o pin.mp4

And the one you would deploy: a loop over a file of links, naming each output after the pin id so re-runs are idempotent and your storage keys stay stable. Note the album branch — reading download_url on an idea pin raises KeyError, because that key is only on single-media responses.

python
import pathlib, time, requests

KEY = "fs_sk_•••••••••••"
OUT = pathlib.Path("moodboard")
OUT.mkdir(exist_ok=True)

def write(name, media):
    dest = OUT / (name + (".mp4" if media["type"] == "video" else ".jpg"))
    dest.write_bytes(requests.get(media["download_url"], timeout=120).content)
    return dest

def save_pin(link):
    r = requests.get(
        "https://api.fastsaver.io/v1/fetch",
        params={"url": link},
        headers={"X-Api-Key": KEY},
        timeout=60,
    )
    data = r.json()
    if not data.get("ok"):
        print("skipped", link, data.get("detail"))
        return []
    if data["type"] == "album":                 # idea pin or carousel: one file per page
        return [write(data["id"] + "-" + str(i), item)
                for i, item in enumerate(data["items"])]
    return [write(data["id"], data)]            # image or video pin

for link in pathlib.Path("pins.txt").read_text().split():
    print(save_pin(link))
    time.sleep(1)          # stay inside your plan's per-minute limit

The sleep is not decoration. Ten requests per minute on the free tier disappears fast during a backfill, and a 429 costs a retry you could have avoided.

use cases

what people build with it

Pinterest traffic here skews towards ingest rather than one-off downloads: something else produced the link, and a job has to turn it into a file.

Moodboard tools

product

A user pastes a pin; you store the file and its dimensions and lay out a real grid instead of hotlinking someone else's CDN.

Design archives

internal

References that outlive the pin. Pins get deleted; your own copy, with the caption attached, does not.

Content pipelines

automation

A worker pulls a link off a queue, gets JSON, pushes the file to object storage. No headless browser in the loop.

honesty

what Pinterest keeps out of reach

  • Public pins only. Secret boards, and anything that asks you to log in, will not resolve.
  • One pin per call. No board, profile or search endpoint — you supply the links.
  • CDN URLs expire. Treat download_url as single-use: fetch the bytes in the same job, never store the link.
  • No dimensions inside an album. Idea-pin and carousel image items report width and height as null; only single-media responses carry real numbers.
  • Pacing is per plan. Free allows 10 requests a minute, Mega 900. A 429 says slow down, not that the pin is bad.
  • Pins disappear, and Pinterest changes its markup. We fix resolution behind the endpoint; a deleted pin nobody can fix.

One more, and it is not a technicality: almost nothing on Pinterest was made by the account that pinned it. Downloading a file grants you no licence to republish it. Attribution, permission and copyright are yours to sort out.

faq

questions about the Pinterest API

Does it work with pin.it short links?

Yes. Pass the pin.it link exactly as the share sheet gave it to you — the redirect is followed server-side. No need to resolve it first, and no need to strip the tracking parameters the app appends.

Can I download Pinterest video pins and idea pins?

Yes. A video pin returns an MP4 in download_url with duration set. A multi-page idea pin comes back as type: "album" carrying an items array — one entry per page, each with its own type, download_url, thumbnail_url, width and height. An album has no top-level download_url, so branch on type before you read one.

What image resolution does the Pinterest API return?

The stored file, not the grid thumbnail you see while browsing. Read width and height rather than assuming: the ceiling is whatever the pinner uploaded, and plenty of pins were re-pinned from an already-compressed copy.

How many credits does one Pinterest request cost?

1.5 credits, image or video, the same as TikTok, X and Facebook. The free tier's 1,000 credits cover roughly 660 pins — enough to test an ingest pipeline before you pick a plan.

Can I pass a board URL or a profile instead of a single pin?

No. The endpoint resolves one pin per call. For a board, collect its pin URLs yourself and loop, with a delay that keeps you under your plan's per-minute limit. Secret boards need a login and are out of scope.

Resolve a pin in the playground

Paste any public pin link, hit send, and read the JSON before you commit to an integration.