> ## Documentation Index
> Fetch the complete documentation index at: https://docs.postbreeze.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Pinterest

> Schedule Pinterest pins — single image, carousel pin, with board selection, titles, and outbound links.

## 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

<Warning>
  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.
</Warning>

See [Platform settings → Pinterest](/concepts/platform-settings#pinterest) for the full schema and [Media uploads](/concepts/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](#full-control-nested-shape) when you want
a different stylistic layout for the same request.

<CodeGroup>
  ```js Node.js theme={null}
  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",
        },
      },
    ],
  });
  ```

  ```python Python theme={null}
  import os
  from datetime import datetime, timedelta, timezone
  import requests

  API = "https://api.postbreeze.ai/api/v1"

  res = requests.post(
      f"{API}/posts",
      headers={"Authorization": f"Bearer {os.environ['POSTBREEZE_API_KEY']}"},
      json={
          "content": "20 minimalist home office setups for 2026",
          "scheduledFor": (datetime.now(timezone.utc) + timedelta(seconds=30)).isoformat(),
          "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",
                  },
              }
          ],
      },
  )
  res.raise_for_status()
  ```

  ```bash curl theme={null}
  curl -X POST https://api.postbreeze.ai/api/v1/posts \
    -H "Authorization: Bearer $POSTBREEZE_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "content": "20 minimalist home office setups for 2026",
      "scheduledFor": "2026-06-02T12:00:30Z",
      "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"
        }
      }]
    }'
  ```
</CodeGroup>

## Content types

### Single-image pin

Default. Pass exactly one image — either inline via `mediaItems` or by reference via `mediaIds` (see [Media uploads](/concepts/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.

<CodeGroup>
  ```js Node.js theme={null}
  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",
        },
      },
    ],
  });
  ```

  ```python Python theme={null}
  res = requests.post(
      f"{API}/posts",
      headers={"Authorization": f"Bearer {os.environ['POSTBREEZE_API_KEY']}"},
      json={
          "content": "Bathroom renovation — before & after",
          "scheduledFor": (datetime.now(timezone.utc) + timedelta(seconds=30)).isoformat(),
          "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",
                  },
              }
          ],
      },
  )
  res.raise_for_status()
  ```

  ```bash curl theme={null}
  curl -X POST https://api.postbreeze.ai/api/v1/posts \
    -H "Authorization: Bearer $POSTBREEZE_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "content": "Bathroom renovation — before & after",
      "scheduledFor": "2026-06-02T12:00:30Z",
      "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"
        }
      }]
    }'
  ```
</CodeGroup>

## Media requirements

### 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.

## Platform-specific fields

See the full schema at [Platform settings → Pinterest](/concepts/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`.

```js Node.js theme={null}
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",
      },
    },
  ],
});
```
