How to Auto-Publish Pinterest Pins via API

Auto-pin every new blog post or piece of content to Pinterest. Real Postproxy params: board_id, destination_link, title, cover_url.

Why auto-pin

Pinterest is a search engine for content discovery — recipes, design, travel, productivity, software tutorials. A single pin with the right title and board can keep sending traffic for a year. For blogs, creator sites, and SaaS content marketing, “every new post becomes a pin” is one of the highest-leverage automations you can wire.

The friction is mechanical: pick the right hero image, write a title that reads well in the Pinterest search index, set the destination URL, drop it on the right board. Postproxy turns that into one POST.

Required parameters

Pinterest’s only required platform parameter via Postproxy is board_id. The rest worth knowing:

ParameterTypeRequiredDescription
board_idstringYesPinterest board ID
titlestringNoPin title (max 100)
destination_linkstringNoWhere the pin links to (max 2,048)
cover_urlstringNoCover image, video pins only
thumb_offsetintegerNoThumbnail offset in seconds, video only

The post body becomes the pin description (max 500 chars).

Looking up your boards

Boards are placements in Postproxy. Fetch them once:

Terminal window
curl -X GET "https://api.postproxy.dev/api/profiles/PROFILE_ID/placements" \
-H "Authorization: Bearer YOUR_API_KEY"

Response:

{
"placements": [
{ "id": "987654321", "name": "Tutorials" },
{ "id": "987654322", "name": "Behind the build" },
{ "id": "987654323", "name": "Design systems" }
]
}

Use the id as board_id.

Auto-pin a single blog post

Terminal window
curl -X POST "https://api.postproxy.dev/api/posts" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"post": {
"body": "Five ways to keep an Astro site fast as it grows: image optimisation, view transitions, font subsetting, careful islands, and edge caching."
},
"profiles": ["pinterest"],
"media": ["https://yourcdn.com/blog/astro-performance-1000x1500.jpg"],
"platforms": {
"pinterest": {
"title": "Five ways to keep your Astro site fast",
"board_id": "987654321",
"destination_link": "https://yoursite.com/blog/astro-performance"
}
}
}'

Image specs: 1000×1500 (2:3) is the canonical Pinterest aspect ratio. JPG, PNG, GIF, or WebP, max 32 MB.

From an RSS feed

A common content workflow: every new post in your RSS feed becomes a pin. The Python SDK keeps it short.

pip install postproxy-sdk feedparser

import asyncio
import os
import sqlite3
import feedparser
from postproxy import PostProxy, PlatformParams, PinterestParams
FEED_URL = "https://yoursite.com/blog/rss.xml"
BOARD_ID = "987654321"
db = sqlite3.connect("pinned.db")
db.execute("CREATE TABLE IF NOT EXISTS pinned (link TEXT PRIMARY KEY)")
async def main():
feed = feedparser.parse(FEED_URL)
async with PostProxy(os.environ["POSTPROXY_API_KEY"], profile_group_id="pg-abc") as client:
for entry in feed.entries:
if db.execute("SELECT 1 FROM pinned WHERE link = ?", (entry.link,)).fetchone():
continue
hero = next(
(m["url"] for m in entry.get("media_content", []) if m.get("medium") == "image"),
None,
)
if not hero:
continue
await client.posts.create(
entry.summary[:500],
profiles=["pinterest"],
media=[hero],
platforms=PlatformParams(
pinterest=PinterestParams(
title=entry.title[:100],
board_id=BOARD_ID,
destination_link=entry.link,
),
),
)
db.execute("INSERT INTO pinned (link) VALUES (?)", (entry.link,))
db.commit()
asyncio.run(main())

Run on a cron every 30 minutes. The local SQLite tracks what’s already pinned so re-runs don’t duplicate.

Routing per topic to its board

Most content sites have several boards: “Tutorials”, “Behind the build”, “Roundups”. Map the post category → board ID:

BOARD_BY_TAG = {
"tutorial": "987654321",
"behind": "987654322",
"roundup": "987654323",
}
# In the loop:
tag = entry.tags[0].term.lower() if entry.tags else "tutorial"
board_id = BOARD_BY_TAG.get(tag, "987654321") # default to Tutorials
await client.posts.create(
entry.summary[:500],
profiles=["pinterest"],
media=[hero],
platforms=PlatformParams(
pinterest=PinterestParams(
title=entry.title[:100],
board_id=board_id,
destination_link=entry.link,
),
),
)

Scheduling pins across the week

Pinterest’s algorithm rewards consistent posting. Use a queue:

Terminal window
curl -X POST "https://api.postproxy.dev/api/post_queues" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"profile_group_id": "pg123",
"post_queue": {
"name": "Pinterest content drip",
"timezone": "America/New_York",
"queue_timeslots_attributes": [
{ "day": 1, "time": "09:00" },
{ "day": 1, "time": "13:00" },
{ "day": 2, "time": "09:00" },
{ "day": 2, "time": "13:00" },
{ "day": 3, "time": "09:00" },
{ "day": 4, "time": "09:00" },
{ "day": 5, "time": "09:00" }
]
}
}'

Then drop each new pin into the queue with queue_id instead of scheduled_at.

Video pins

Vertical video pins follow the same MP4 shape as TikTok and Reels (9:16, ≤15 minutes, ≤2 GB). Add a cover_url for the still thumbnail:

Terminal window
curl -X POST "https://api.postproxy.dev/api/posts" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"post": { "body": "Quick walkthrough of the new dashboard." },
"profiles": ["pinterest"],
"media": ["https://yourcdn.com/dashboard-walkthrough-9x16.mp4"],
"platforms": {
"pinterest": {
"title": "New dashboard walkthrough",
"board_id": "987654321",
"destination_link": "https://yoursite.com/blog/new-dashboard",
"cover_url": "https://yourcdn.com/dashboard-cover.jpg"
}
}
}'

This is the same MP4 you’d push to TikTok or Instagram Reels — see the cross-post guide for one-call distribution to all three.

Auto-pin from a headless CMS webhook

If you publish via a headless CMS (Sanity, Contentful, Webflow, Ghost), point its post-publish webhook at a small handler:

npm install postproxy-sdk

// Cloudflare Worker / Netlify Function / Express handler
import { PostProxy } from "postproxy-sdk";
const client = new PostProxy(process.env.POSTPROXY_API_KEY!, {
profileGroupId: "pg-abc",
});
const BOARD_BY_TAG: Record<string, string> = {
tutorial: "987654321",
behind: "987654322",
roundup: "987654323",
};
export default async function handler(request: Request) {
const post = await request.json();
const tag = post.tags?.[0] ?? "tutorial";
await client.posts.create(
(post.excerpt ?? "").slice(0, 500),
["pinterest"],
{
media: [post.heroImageUrl],
platforms: {
pinterest: {
title: post.title.slice(0, 100),
board_id: BOARD_BY_TAG[tag] ?? BOARD_BY_TAG.tutorial,
destination_link: post.url,
},
},
},
);
return new Response("ok");
}

A new CMS post → a new Pinterest pin. No human in the loop.

For deeper Pinterest specifics — pin analytics, scheduling at scale, mixing image and video — see Pinterest API: automate pins and Pinterest API posting integration guide.

Ready to get started?

Start with our free plan and scale as your needs grow. No credit card required.