Quick reference
| Field | Value |
|---|
| Title limit | 100 characters |
| Description limit | 500 characters |
| Images per pin | 1 (standard) or 2–5 (carousel) |
| Videos per pin | 0 (Pinterest video pins not exposed) |
| Image formats | JPEG, PNG |
| Max image size | 20 MB |
| Aspect ratio | 2:3 recommended (long pin) |
| Min resolution | 600 × 900 |
| Carousel aspect ratio | 1:1 or 2:3 only |
| Board | Required — pick at compose time |
| Outbound link | ✅ Per-pin click destination |
| First comment | ❌ Pinterest doesn’t expose comment API |
| Post types | Pin (single), Carousel pin |
Before you start
Pinterest requires every pin to land on a specific board. You must pass the board ID in platformOptions — there’s no default board at the API layer (the compose UI pre-fills one from the connected account, but the API does not). The compose dashboard fetches the available board list at connect time and stores it on the SocialAccount; pulling the list dynamically from the public API isn’t available in v1.
See Platform settings → Pinterest for the full schema and Media uploads for how to attach images.
Required scopes (granted by the connect flow):
boards:read — list the boards you can pin to.
boards:read_secret — list secret boards.
pins:read, pins:write — read + create pins.
Pinterest carousel pins are image-only. Mixing video isn’t supported by the API at all (video pins go through a separate flow we haven’t shipped yet).
Quick start
Workspace is inferred from your API key — no workspaceId argument.
Pinterest always needs platformOptions.board because every pin
lands on a specific board — so even the flat-shape quickstart sets it.
Drop to the nested shape when you want
a different stylistic layout for the same request.
import Postbreeze from "@postbreeze/node";
const postbreeze = new Postbreeze({ apiKey: process.env.POSTBREEZE_API_KEY });
const post = await postbreeze.posts.create({
content: "20 minimalist home office setups for 2026",
scheduledFor: new Date(Date.now() + 30_000).toISOString(),
mediaItems: [{ type: "IMAGE", url: "https://cdn.example.com/office.jpg" }],
platforms: [
{
accountId: "soc_pinterest_…",
platformOptions: {
platform: "PINTEREST",
board: "pinterest_board_id_…",
title: "20 minimalist home office setups",
link: "https://example.com/blog/home-office-setups",
},
},
],
});
Content types
Single-image pin
Default. Pass exactly one image — either inline via mediaItems or by reference via mediaIds (see Media uploads).
Carousel pin (2–5 images)
Pass 2–5 image mediaIds (or mediaItems). All slides must share the same aspect ratio (1:1 OR 2:3) — Pinterest rejects mixed aspects at the API.
const post = await postbreeze.posts.create({
content: "Bathroom renovation — before & after",
scheduledFor: new Date(Date.now() + 30_000).toISOString(),
mediaIds: ["med_before_1", "med_after_1", "med_before_2", "med_after_2"],
platforms: [
{
accountId: "soc_pinterest_…",
platformOptions: {
platform: "PINTEREST",
board: "pinterest_board_id_…",
title: "Bathroom before & after",
link: "https://example.com/case-study/bath",
},
},
],
});
Images
| Property | Requirement |
|---|
| Images per pin | 1 (standard), 2–5 (carousel) |
| Formats | JPEG, PNG |
| Max file size | 20 MB |
| Min resolution | 600 × 900 |
| Aspect ratio (single) | 2:3 recommended; 1:2.1 is “long pin” cutoff |
| Aspect ratio (carousel) | 1:1 OR 2:3 (consistent across all slides) |
Pinterest down-scales anything above 1,200 × 1,800. Higher resolutions are accepted but offer no display advantage.
See the full schema at Platform settings → Pinterest.
| Field | Type | Required | Description |
|---|
platform | "PINTEREST" | Yes | Discriminator. |
board | string | Yes | Pinterest board ID where the pin lands. No default at the API layer. |
title | string | No | Pin title (≤ 100 chars). |
link | URL | No | Outbound click destination. Tracked as outbound_click_rate in analytics. |
The pin’s description comes from the post-level content field — Pinterest caps it at 500 chars.
firstComment is not supported on Pinterest — the platform doesn’t expose a commercial comment API. Passing one is rejected at validation.
Analytics
Pinterest analytics include some signature metrics not exposed elsewhere:
| Metric | Available |
|---|
| Impressions | ✅ |
| Closeups (pin detail page opens) | ✅ |
| Saves | ✅ |
| Save rate (saves / impressions) | ✅ |
| Engagement | ✅ |
| Engagement rate | ✅ |
| Outbound clicks | ✅ |
| Outbound click rate | ✅ |
| Monthly viewers (account) | ✅ |
| Video views | ❌ (video pins not yet exposed) |
Refresh cadence: every 14 days.
Common errors
| Error | Meaning | Fix |
|---|
PIN_BOARD_REQUIRED | board missing from platformOptions | Pass a board ID. |
PIN_BOARD_NOT_FOUND | The board ID isn’t owned by this Pinterest account | List boards via the API and pick a valid one. |
PIN_CAROUSEL_SIZE | Carousel with < 2 or > 5 images | Trim to 2–5. |
PIN_MIXED_ASPECT | Carousel slides have different aspect ratios | Resize so every slide is 1:1 OR 2:3. |
PIN_VIDEO_NOT_SUPPORTED | Video in mediaIds | Pinterest video pins are deferred. |
PIN_LINK_INVALID | The link URL didn’t resolve | Verify the URL works in an incognito browser. |
PIN_RATE_LIMITED | Pinterest per-account rate limit | Postbreeze retries with backoff. |
What you can’t do
- ❌ Video pins (planned for v2)
- ❌ Idea pins (deprecated by Pinterest)
- ❌ Story pins
- ❌ Schedule via Pinterest’s native scheduler
- ❌ Edit a pin after publish (Pinterest deleted that API)
- ❌ Add to multiple boards at once (one pin = one board; create multiple posts)
- ❌ Section pins (sub-boards) at the API
- ❌ Tag products (catalog feature, separate API)
- ❌ First comment (no commercial comment API)
Full control: nested shape
The nested shape (caption + targets + scheduledAt + socialAccountId) is functionally identical to the flat shape — pick whichever reads better in your codebase. Every Pinterest target still requires platformOptions.board.
import Postbreeze from "@postbreeze/node";
const postbreeze = new Postbreeze({ apiKey: process.env.POSTBREEZE_API_KEY });
const post = await postbreeze.posts.create({
caption: "20 minimalist home office setups for 2026",
scheduledAt: new Date(Date.now() + 30_000).toISOString(),
mediaIds: ["med_image_…"],
targets: [
{
socialAccountId: "soc_pinterest_…",
platformOptions: {
platform: "PINTEREST",
board: "pinterest_board_id_…",
title: "20 minimalist home office setups",
link: "https://example.com/blog/home-office-setups",
},
},
],
});