# Pinterest

Create image, carousel and video Pins on your Pinterest boards with the Pinterest API v5.


## Overview

The `pinterest` platform creates Pins with the Pinterest API v5. You can create:

- Image Pins (1 image)
- Carousel Pins (2–5 images)
- Video Pins (1 video, with a cover image or a cover frame)

Every Pin is saved to a board (`options.boardId` is required). The post `content` becomes the Pin description; `options.title` sets the Pin title and `options.link` the destination link.

## Connect the account

In **[Integrations](https://social-publish-kappa.vercel.app/dashboard/integrations)**, choose **Pinterest** and approve access.

### App setup (self-hosted)

1. At developers.pinterest.com, open **My apps → Connect app** (you need a Pinterest business account).
2. Add the redirect URI `https://social-publish-kappa.vercel.app/api/integrations/pinterest/callback` (override with `PINTEREST_REDIRECT_URI`).
3. Set `PINTEREST_APP_ID` and `PINTEREST_APP_SECRET`.

Scopes requested: `boards:read`, `boards:write`, `pins:read`, `pins:write`, `user_accounts:read`. Tokens are refreshed automatically.

> [!WARNING]
> New Pinterest apps get **Trial access**, which can't create Pins on production. Until Pinterest grants Standard access, set `PINTEREST_API_BASE=https://api-sandbox.pinterest.com`. Sandbox boards and Pins are separate from your real ones, visible only to you, and video Pins are not supported there.

## Post types & media rules

| Rule | Limit |
| --- | --- |
| Media | Required: 1 image, 2–5 images (carousel), or exactly 1 video |
| Video | 1 video and no images; up to 1 GB |
| Video cover | `coverImageUrl` or `coverKeyFrameSeconds` is required for video Pins |
| Description (`content`) | 800 characters |
| Title | 100 characters |
| Alt text | 500 characters (`options.altText`, or the first image's `altText`) |
| Destination link | Up to 2,048 characters |
| Board | `boardId` is required (numeric board ID) |

Images are fetched by Pinterest from their URLs, so they must be publicly reachable. Carousel items share the Pin's title, description and link.

## Options

| Option | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `boardId` | string (per account) | Yes |  | Board. Every Pin must be saved to one of your boards. |
| `title` | string | No |  | Pin title. Optional, max 100 characters. The post text becomes the Pin description (max 800). |
| `link` | string (URL) | No |  | Destination link. Where the Pin leads when clicked. |
| `altText` | string | No |  | Alt text. Describes the image for screen readers (max 500). Defaults to the image's own alt text. |
| `coverImageUrl` | string (URL) | No |  | Video cover image URL. Video Pins only. Required unless a cover key frame is set. |
| `coverKeyFrameSeconds` | number | No |  | Video cover frame (seconds). Video Pins only. Use the frame at this many seconds as the cover instead of a cover image URL. |
`boardId` is a per-account value. The easiest way to get it is to pick the board in the dashboard **Playground** and copy the generated request; the board list there comes from your Pinterest account (up to 250 boards).

## Examples

### Image Pin

```bash tab="cURL"
curl -X POST https://social-publish-api.onrender.com/v1/posts \
  -H "x-access-key: $RELAYCAST_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "content": "A 20-minute weeknight pasta with roasted tomatoes.",
    "media": [{ "url": "https://cdn.example.com/pasta.jpg", "type": "image" }],
    "targets": [{
      "accountId": "4f5e6d7c-8b9a-4c0d-8e1f-2a3b4c5d6e7f",
      "options": {
        "boardId": "1084523960912345678",
        "title": "Roasted tomato pasta",
        "link": "https://example.com/recipes/roasted-tomato-pasta",
        "altText": "Bowl of pasta with roasted cherry tomatoes and basil"
      }
    }],
    "publishNow": true
  }'
```

```ts tab="Node.js"
const res = await fetch("https://social-publish-api.onrender.com/v1/posts", {
  method: "POST",
  headers: {
    "x-access-key": process.env.RELAYCAST_API_KEY!,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    content: "A 20-minute weeknight pasta with roasted tomatoes.",
    media: [{ url: "https://cdn.example.com/pasta.jpg", type: "image" }],
    targets: [
      {
        accountId: "4f5e6d7c-8b9a-4c0d-8e1f-2a3b4c5d6e7f",
        options: {
          boardId: "1084523960912345678",
          title: "Roasted tomato pasta",
          link: "https://example.com/recipes/roasted-tomato-pasta",
          altText: "Bowl of pasta with roasted cherry tomatoes and basil",
        },
      },
    ],
    publishNow: true,
  }),
});
const { data } = await res.json();
```

```python tab="Python"
import os, requests

res = requests.post(
    "https://social-publish-api.onrender.com/v1/posts",
    headers={"x-access-key": os.environ["RELAYCAST_API_KEY"]},
    json={
        "content": "A 20-minute weeknight pasta with roasted tomatoes.",
        "media": [{"url": "https://cdn.example.com/pasta.jpg", "type": "image"}],
        "targets": [{
            "accountId": "4f5e6d7c-8b9a-4c0d-8e1f-2a3b4c5d6e7f",
            "options": {
                "boardId": "1084523960912345678",
                "title": "Roasted tomato pasta",
                "link": "https://example.com/recipes/roasted-tomato-pasta",
                "altText": "Bowl of pasta with roasted cherry tomatoes and basil",
            },
        }],
        "publishNow": True,
    },
)
data = res.json()["data"]
```

### Video Pin with a cover frame

```json title="Request body"
{
  "content": "How to fold a fitted sheet in 30 seconds",
  "media": [{ "url": "https://cdn.example.com/fold.mp4", "type": "video" }],
  "targets": [{
    "accountId": "4f5e6d7c-8b9a-4c0d-8e1f-2a3b4c5d6e7f",
    "options": { "boardId": "1084523960912345678", "title": "Fold a fitted sheet", "coverKeyFrameSeconds": 3 }
  }],
  "scheduledFor": "2026-11-09T14:00:00Z"
}
```

## Analytics & comments

Analytics show impressions, saves, clicks (Pin clicks + outbound clicks), reactions and comments. Pinterest only reports analytics for the last 90 days, and none in the sandbox.

Comments and messaging are not supported for Pinterest.

## Limitations & common errors

| Error | Cause |
| --- | --- |
| `Choose a board to pin to` | `boardId` is missing or not numeric. |
| `Pinterest Pins need an image or a video` | No media. |
| `A Pinterest video Pin takes exactly one video and no images` | Video combined with other media. |
| `Pinterest carousels allow 2-5 images` | More than 5 images. |
| `Video Pins need a cover image URL or a cover key frame time` | Set `coverImageUrl` or `coverKeyFrameSeconds`. |
| `Video Pins cannot be created in the Pinterest API sandbox` | Video with `PINTEREST_API_BASE` pointing at the sandbox. |
| `Pinterest descriptions allow 800 characters` | `content` is too long. |
| Target fails with a trial-access message | The app only has Trial access; use the sandbox or request Standard access. |
| Account becomes `reconnect_required` | The refresh token expired or was revoked. |
