Developer guide

Pinterest API Access, Pins, video uploads and limits in v5

Checked against Pinterest’s developer documentation in September 2026

Short answer: Pinterest API v5 is a free REST API at api.pinterest.com/v5. Your app starts on Trial access, where Pins you create are visible only to you and calls are capped at 1,000 a day. Standard access, granted after a video demo, makes Pins public. A Pin is one POST /v5/pins; video needs an upload step first.

What the Pinterest API covers

Pinterest’s current API is v5, described in full in an OpenAPI file on GitHub (version 5.28.0 as of this writing). One API covers both organic and paid use:

  • Organic content: boards and board sections, Pins (image, video, carousel), user account data and Pin and account analytics.
  • Ads: ad accounts, campaigns, audiences and conversion events.
  • Catalogs and trends: product catalogs for shopping, and trend data.

This guide is about the organic part: getting access, authenticating and publishing Pins. One boundary from Pinterest’s own reference: pins/create “is intended solely for publishing new content created by the user.” For saving other people’s content to your boards, Pinterest points you to its Save button instead.

Trial vs Standard access

Every app has an access tier, and the tier changes both who sees your Pins and how fast you can call the API:

Trial accessStandard access
How you get itDefault tier once your app is approvedUpgrade request with a video demo of your app
Pins and boards you createVisible only to the creator, as Sandbox entitiesPublic, like any other Pin
Rate limitsPer day, per app: 1,000 requests for all callsPer minute, per user, per app: 100 requests per second overall
EndpointsSame as StandardSame as Trial
Right forBuilding and testingAnything real users rely on

Sources: access tiers and rate limits. Read 29 September 2026.

The Trial trap. Your code can be completely correct on Trial access and still look broken: the API returns a Pin ID, and nobody but you can see the Pin. That is by design. To go public, your app must already be approved for Trial access and follow the Developer Guidelines; you then record a video of your app completing an action through the API (Pinterest wants to see the OAuth flow and the live integration) and click Upgrade on the app card in My apps. Pinterest reviews requests “regularly” and emails you the decision. There is no published timeline.

OAuth 2.0: 30-day tokens, 60-day refresh

Send the user to Pinterest’s authorization page with the scopes you need:

https://www.pinterest.com/oauth/
  ?client_id=YOUR_APP_ID
  &redirect_uri=https://yourapp.com/callback
  &response_type=code
  &scope=boards:read,boards:write,pins:read,pins:write
  &state=RANDOM_STRING

Then exchange the code at https://api.pinterest.com/v5/oauth/token. Unlike most APIs, the app credentials go in an HTTP Basic Authorization header, not the body (authentication docs):

# Code -> tokens (HTTP Basic auth with app ID and secret)
curl -X POST https://api.pinterest.com/v5/oauth/token \
  -u "YOUR_APP_ID:YOUR_APP_SECRET" \
  -d grant_type=authorization_code \
  -d code=AUTH_CODE \
  -d redirect_uri=https://yourapp.com/callback
# -> access_token (30 days) + refresh_token (60 days, refreshable)
# Apps created before 25 Sep 2025: add -d continuous_refresh=true

# Later: refresh before the access token expires
curl -X POST https://api.pinterest.com/v5/oauth/token \
  -u "YOUR_APP_ID:YOUR_APP_SECRET" \
  -d grant_type=refresh_token \
  -d refresh_token=REFRESH_TOKEN

Access tokens last 30 days (2,592,000 seconds). Refresh tokens are the “continuous” kind: they last 60 days and can be refreshed indefinitely, so a scheduled refresh keeps a connection alive without the user logging in again. Pinterest no longer supports the old 365-day refresh tokens. Apps created on or after 25 September 2025 get continuous tokens automatically; older apps must send continuous_refresh=true in the code exchange.

Pinterest API scopes

ScopeAllows
boards:readSee public boards, including group boards the user joined
boards:writeCreate, update or delete public boards
pins:readSee public Pins
pins:writeCreate, update or delete public Pins
*_secret variantsboards:read_secret, boards:write_secret, pins:read_secret, pins:write_secret for secret boards and Pins
user_accounts:readSee the user account and followers
ads:*, catalogs:*, billing:*, biz_access:*Ads, catalogs, billing and business access, read and write

Source: Pinterest API v5 OpenAPI description, security schemes. Read 29 September 2026.

Creating a Pin requires boards:read, boards:write, pins:read and pins:write.

How to create a Pin with the API

Find the board ID

curl https://api.pinterest.com/v5/boards \
  -H "Authorization: Bearer ACCESS_TOKEN"
# -> {"items": [{"id": "987654321", "name": "Weeknight dinners", ...}], "bookmark": ...}

Image Pin

curl -X POST https://api.pinterest.com/v5/pins \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "board_id": "987654321",
    "title": "15-minute miso noodles",
    "description": "Weeknight dinner with five ingredients.",
    "link": "https://example.com/miso-noodles",
    "alt_text": "Bowl of noodles in miso broth with scallions",
    "media_source": {
      "source_type": "image_url",
      "url": "https://cdn.example.com/miso-noodles.jpg"
    }
  }'

The response is the new Pin with its id. Instead of image_url you can send the file itself with source_type: "image_base64", content_type and data. Add board_section_id to drop the Pin into a section.

Carousel Pin

Swap the media source for multiple_image_urls (or multiple_image_base64) with 2 to 5 items. Each slide can carry its own title, description and link:

"media_source": {
  "source_type": "multiple_image_urls",
  "items": [
    {"url": "https://cdn.example.com/step-1.jpg", "title": "Step 1", "link": "https://example.com/r#1"},
    {"url": "https://cdn.example.com/step-2.jpg", "title": "Step 2", "link": "https://example.com/r#2"},
    {"url": "https://cdn.example.com/step-3.jpg", "title": "Step 3", "link": "https://example.com/r#3"}
  ]
}

Video Pin

Video takes an upload step. You register the upload, post the file to a pre-signed storage URL with the parameters Pinterest gives you, wait for processing, then create the Pin with the media_id (create boards and Pins):

# 1. Register the upload
curl -X POST https://api.pinterest.com/v5/media \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"media_type": "video"}'
# -> {"media_id": "12345", "upload_url": "https://pinterest-media-upload.s3-accelerate.amazonaws.com/",
#     "upload_parameters": {"key": "...", "policy": "...", "x-amz-signature": "...", ...}}

# 2. Upload the file with every upload_parameter as a form field
curl -X POST "UPLOAD_URL" \
  -F "key=..." -F "policy=..." -F "x-amz-algorithm=..." -F "x-amz-credential=..." \
  -F "x-amz-date=..." -F "x-amz-security-token=..." -F "x-amz-signature=..." \
  -F "Content-Type=multipart/form-data" \
  -F "[email protected]"

# 3. Wait for status "succeeded"
curl https://api.pinterest.com/v5/media/12345 \
  -H "Authorization: Bearer ACCESS_TOKEN"

# 4. Create the Pin
#    "media_source": {"source_type": "video_id", "media_id": "12345",
#                     "cover_image_key_frame_time": 3}

The media status moves through registered, processing and succeeded or failed. Only create the Pin after succeeded. For the cover you can pass cover_image_url, a base64 image, or cover_image_key_frame_time in seconds; if that time is past the end of the video, Pinterest uses the last frame.

Test in the Sandbox

Pinterest runs a separate environment at https://api-sandbox.pinterest.com/v5 that does not touch production data. You can generate a 30-day Sandbox token from your app page or get one through OAuth using https://api-sandbox.pinterest.com/v5/oauth/token. Sandbox boards and Pins appear on your own profile only and cannot be moved to production. The main gap: you cannot create video Pins in Sandbox (Sandbox docs).

Pinterest API in Python

You do not need a client library. This script creates an image Pin and a video Pin, including the upload and status loop, with requests:

import time
import requests

API = "https://api.pinterest.com/v5"   # Sandbox: https://api-sandbox.pinterest.com/v5
H = {"Authorization": "Bearer ACCESS_TOKEN"}


def create_pin(board_id, media_source, **fields):
    r = requests.post(f"{API}/pins", headers=H,
                      json={"board_id": board_id, "media_source": media_source, **fields})
    if r.status_code == 429:
        reset = r.headers.get("x-ratelimit-reset")
        raise RuntimeError(f"Rate limited, retry in {reset} s")
    r.raise_for_status()
    return r.json()["id"]


def upload_video(path):
    reg = requests.post(f"{API}/media", headers=H, json={"media_type": "video"}).json()
    with open(path, "rb") as f:
        up = requests.post(reg["upload_url"], data=reg["upload_parameters"],
                           files={"file": f})
    up.raise_for_status()
    while True:
        status = requests.get(f"{API}/media/{reg['media_id']}", headers=H).json()["status"]
        if status == "succeeded":
            return reg["media_id"]
        if status == "failed":
            raise RuntimeError("Pinterest could not process the video")
        time.sleep(5)


BOARD = "987654321"

# Image Pin
create_pin(BOARD, {"source_type": "image_url", "url": "https://cdn.example.com/a.jpg"},
           title="Before and after", link="https://example.com/renovation")

# Video Pin (not available in Sandbox)
media_id = upload_video("demo.mp4")
create_pin(BOARD, {"source_type": "video_id", "media_id": media_id,
                   "cover_image_key_frame_time": 2},
           title="Kitchen renovation in 30 seconds")

For a real integration, add the token refresh job and store the media_id with each queued post, so a restart does not upload the same video twice.

Pinterest API limits

LimitTrial accessStandard access
All requests1,000 per day100 per second per user per app
org_write (create Pins, boards)300 per day100 per minute
org_read1,000 per day1,000 per minute
org_analytics1,000 per day60 per minute

Source: Pinterest rate limits. Read 29 September 2026.

Field or mediaLimit
Title100 characters (the first 40 show in feeds)
Description800 characters
Link2,048 characters
Alt text500 characters
ImagePNG or JPEG; up to 20 MB on desktop, 32 MB in the app; 2:3 recommended (1000 × 1500 px)
VideoMP4, MOV or M4V; up to 2 GB; 4 seconds to 15 minutes; 1:1, 2:3, 4:5 or 9:16
Carousel2 to 5 images, 20 MB each

Sources: OpenAPI Pin schema and Pinterest product specs. Read 29 September 2026.

Every response carries x-ratelimit-limit, x-ratelimit-remaining and x-ratelimit-reset (seconds until the window resets). Read them instead of guessing, especially on Trial access where 300 writes a day go fast in testing.

Common Pinterest API errors

Errors come back as JSON with a numeric code and a message. What each HTTP status usually means when creating Pins:

StatusUsual causeFix
400Invalid body: title over 100, description over 800, a carousel with 1 or 6 images, a bad source_typeValidate against the limits above before sending
401Access token expired (30 days) or revokedRefresh with the refresh token; re-run OAuth if that expired too
403Token lacks a scope, or the board belongs to someone elseRequest boards:write and pins:write; use a board the user owns
404Wrong board_id, or a production ID used against Sandbox (or the reverse)List boards with the same host and token you post with
429Daily (Trial) or per-minute (Standard) limit reachedWait for x-ratelimit-reset; upgrade to Standard
Media failedVideo outside the specs, or the upload did not include every upload_parameters fieldCheck format, size and duration; re-register and upload again
200, but invisibleApp is on Trial accessRequest Standard access

The shortcut: publish Pins without the Standard access review

If Pinterest is one of several channels, the hard part is not the POST. It is getting Standard access, the token refresh, the S3-style video upload and the status loop. Upload-Post’s Pinterest API runs an approved app, so Pins are public from the first post. You connect the account once and send the file:

# A video Pin to a board section, with cover frame and AI disclosure
curl -X POST https://api.upload-post.com/api/upload \
  -H "Authorization: Apikey YOUR_KEY" \
  -F "user=my-brand" \
  -F "platform[]=pinterest" \
  -F "platform[]=tiktok" \
  -F "platform[]=instagram" \
  -F "[email protected]" \
  -F "title=Kitchen renovation in 30 seconds" \
  -F "pinterest_board_id=987654321" \
  -F "pinterest_board_section_id=555" \
  -F "pinterest_link=https://example.com/kitchen" \
  -F "pinterest_cover_image_key_frame_time=3" \
  -F "pinterest_ai_disclosures=AI_MODIFIED" \
  -F "add_to_queue=true"

That queues the video for Pinterest, TikTok and Instagram in the next free slot, with a board section, a destination link, the cover taken from second 3 and an AI disclosure. The board IDs come from GET /api/uploadposts/pinterest/boards, which also lists secret boards and can create boards and sections. Other options in the docs:

  • Carousels: send 2 to 5 images to /api/upload_photos with per-slide pinterest_carousel_titles[], pinterest_carousel_descriptions[] and pinterest_carousel_links[].
  • Pin fields: pinterest_title, pinterest_description, pinterest_alt_text, pinterest_link.
  • Covers: pinterest_cover_image_url, base64 cover data or a key frame time.
  • Scheduling: scheduled_date with a timezone, up to 365 days ahead, or add_to_queue=true.
  • Saving Pins: repin an existing Pin to one of your boards with POST /api/uploadposts/posts/save.
  • Analytics: Pinterest metrics come back from the same analytics endpoint as the other platforms.

Working from an AI assistant? The Pinterest MCP server gives Claude, ChatGPT and Cursor the same tools, including board lookup.

The Free plan is free forever with no card: 10 uploads a month and 2 profiles, with Pinterest and API access included. Paid plans with unlimited uploads start at $24 a month ($16 billed annually). Compare options in the social media API overview.

Frequently asked questions

Is the Pinterest API free?

Pinterest does not charge for API access. What limits you is the access tier: Trial access is capped at 1,000 requests a day and everything you create is private to you, and Standard access, which lifts both, requires a review with a video demo of your app.

Why can nobody see the Pins I created with the API?

Because your app is on Trial access. Pinterest’s docs say all Pins and boards created with Trial access are only visible to their creator as Sandbox entities. Upgrade to Standard access for Pins that other people can see.

How do I get Standard access to the Pinterest API?

Your app must already be approved for Trial access and follow the Developer Guidelines. Record a video of your app completing an action through the API, showing the OAuth flow and the live Pinterest integration, then choose Upgrade on the app card in My apps. Pinterest emails you when it decides; it does not publish a timeline.

Which OAuth scopes do I need to create a Pin?

The pins/create endpoint lists boards:read, boards:write, pins:read and pins:write. For secret boards and Pins add boards:read_secret, boards:write_secret, pins:read_secret or pins:write_secret. user_accounts:read covers the account profile and follower data.

How long do Pinterest access tokens last?

Access tokens last 30 days (2,592,000 seconds). Continuous refresh tokens last 60 days and can be refreshed indefinitely, so a daily or weekly refresh job keeps a connection alive without sending the user back through OAuth.

Can I create video Pins with the API?

Yes, in three steps: register the upload with POST /v5/media, upload the file to the returned upload_url with the upload_parameters, then create the Pin with media_source.source_type set to video_id and a cover image once the media status is succeeded. Video Pins cannot be created in the Sandbox environment.

How many images can a Pinterest carousel have?

Two to five. The API accepts multiple_image_urls or multiple_image_base64 with 2 to 5 items, each with its own optional title, description and link. Pinterest’s specs allow up to 20 MB per image.

What are the Pinterest API rate limits?

On Trial access, 1,000 requests a day for all calls, with lower category limits such as 300 org_write calls a day. On Standard access, 100 requests per second per user per app, with per-minute category limits such as 100 org_write and 1,000 org_read. Responses include x-ratelimit-limit, x-ratelimit-remaining and x-ratelimit-reset headers.

Can I repin other people’s content with pins/create?

Pinterest says pins/create is intended solely for publishing new content created by the user, and points to its Save button for curated content. Use it for your own images and videos.

Is there an official Pinterest API for Python?

Pinterest publishes an OpenAPI description of API v5 on GitHub, which you can use to generate a client, but the API is plain HTTPS and JSON. The Python example on this page creates image, carousel and video Pins with nothing but requests.

Public Pins from the first request

Skip Trial access, the video upload dance and token refresh. Publish image, video and carousel Pins, and send the same content to TikTok and Instagram in one call.

Free forever plan. No credit card. 10 uploads a month.