pinterest-mcp
A Pinterest API v5 MCP server exposing 35 typed tools for managing boards, pins, analytics, search, media uploads, and OAuth for an authenticated Pinterest account.
Read the authenticated profile, followers/following, claimed websites, and account analytics.
Manage boards and board sections: list, get, create, update, delete, and list pins within them.
Manage pins: list, get, create, update, delete, and save/repin pins.
Register and track video/media uploads for video pin creation.
Query account analytics, top pins, top video pins, and single/multi-pin analytics over up to 90 days.
Search your own pins and boards (not Pinterest globally).
Build OAuth consent URLs, exchange codes for tokens, and refresh expired access tokens.
Call any Pinterest v5 endpoint via the
pinterest_api_requestescape hatch.Run as a local stdio MCP server or hosted HTTP/remote MCP server, including OAuth 2.1 for multi-user deployments.
Provides 35 typed tools over the Pinterest API v5 for managing the authenticated account: reading the profile/followers/claimed websites; listing, creating, updating and deleting boards and board sections; listing, getting, creating, updating, deleting and saving (repinning) pins, including video upload registration; pulling account-level daily analytics and top pin/video analytics; full-text search over the account's own pins and boards; OAuth flow helpers (consent URL, code exchange, token refresh); and a generic request tool for any other v5 endpoint.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@pinterest-mcp create a pin on my Recipes board linking to my new blog post"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
pinterest-mcp
A Model Context Protocol server for the Pinterest API v5. It gives an AI assistant 35 typed tools over boards, pins, analytics, search and OAuth — runnable as a local stdio subprocess or as a hosted HTTP endpoint.
What it can do
Area | Tools |
Account | Read the authenticated profile, followers, following, claimed websites |
Boards | List, get, create, update, delete; sections; pins within a board or section |
Pins | List, get, create, update, delete, save (repin); video upload registration |
Analytics | Account-level daily metrics, top pins, top video pins, single- and multi-pin analytics |
Search | Full-text search over the account's own pins and boards |
OAuth | Build a consent URL, exchange the code, refresh an expired token |
Escape hatch |
|
What the Pinterest API cannot do
Worth knowing before you plan a workflow around it:
No public discovery search. Everything is scoped to the authenticated account.
pinterest_search_my_pinssearches your pins, not Pinterest at large.Analytics reach back 90 days. Longer windows are rejected by Pinterest, not by this server.
Video pins take three steps — register the upload, PUT the file, then create the pin referencing the media id.
Trial access is limited. A new Pinterest app can only act on its own account until it is approved for Standard access. Everything here works under Trial for your own account; letting other people connect needs Standard.
Related MCP server: pinterest-mcp
Install
npx pinterest-mcpOr from source:
git clone https://github.com/aymandakirgh/pinterest-mcp.git
cd pinterest-mcp
npm install
npm run build # compiles to dist/
npm testGetting a token
Fastest: a test token from the dashboard
Once your app is approved for Trial access, you can generate a token straight from the Pinterest dashboard and skip OAuth entirely — the quickest way to get this server working against your own account:
developers.pinterest.com/apps → Manage → Configure → Generate Access Token → pick Production (or Sandbox) → Generate
claude mcp add pinterest --env PINTEREST_ACCESS_TOKEN=your_token -- npx -y pinterest-mcpThen ask the assistant to call pinterest_get_user_account to confirm the token and its scopes.
In the browser (easiest)
Run the HTTP transport with your app credentials set and open /auth:
PINTEREST_APP_ID=... PINTEREST_APP_SECRET=... npm run start:http
# then open http://localhost:3000/authClick through, approve on Pinterest, and the callback page hands you the access token, the refresh token and the exact claude mcp add command to paste. Nothing is stored server-side — the tokens are shown once and never written to disk.
Register <your-host>/auth/callback as a redirect URI on the Pinterest app first; it must match character for character.
Pinterest is the only sign-in option for its API — there is no "connect with Google" for third-party apps. If your Pinterest account itself uses Google or Facebook, pick that on Pinterest's own consent screen.
From the tools
If you would rather stay in the assistant:
Create an app at developers.pinterest.com/apps and note the app id, secret and a registered redirect URI.
Set
PINTEREST_APP_ID,PINTEREST_APP_SECRETandPINTEREST_REDIRECT_URI.Ask the assistant to call
pinterest_build_oauth_url, open the URL, approve.Copy the
codefrom the redirect and callpinterest_exchange_oauth_code.Put the returned
access_tokeninPINTEREST_ACCESS_TOKEN.
Access tokens last roughly 30 days; refresh tokens roughly a year. When calls start returning 401, use pinterest_refresh_access_token.
Scopes
Request only what you need — Pinterest shows the list on the consent screen.
Scope | Needed for |
| Profile, followers, following, account analytics |
| Reading / modifying boards and sections |
| The same, for secret boards |
| Reading / modifying pins, and pin analytics |
| The same, for pins on secret boards |
The OAuth tool defaults to user_accounts:read, boards:read, boards:write, pins:read, pins:write.
Use it with Claude Code
claude mcp add pinterest --env PINTEREST_ACCESS_TOKEN=your_token -- npx -y pinterest-mcpUse it with Claude Desktop
Add to claude_desktop_config.json:
{
"mcpServers": {
"pinterest": {
"command": "npx",
"args": ["-y", "pinterest-mcp"],
"env": {
"PINTEREST_ACCESS_TOKEN": "your_token_here"
}
}
}
}Let anyone connect their own account (OAuth)
Run with app credentials and the server becomes a remote MCP server with OAuth 2.1. A user adds the URL in their client, clicks Connect, approves on Pinterest, and is done — no token ever passes through their hands.
PINTEREST_APP_ID=... PINTEREST_APP_SECRET=... \
MCP_AUTH_SECRET=$(openssl rand -hex 32) \
PUBLIC_BASE_URL=https://your-host \
npm run start:httpRegister https://your-host/oauth/pinterest/callback as a redirect URI on the Pinterest app, then point a client at https://your-host/mcp.
The client never receives a Pinterest token. It gets one minted here, with the Pinterest credential sealed inside it, so a single deployment can serve many people's accounts without any of them seeing another's.
Endpoint | Purpose |
| RFC 9728 — names the authorization server |
| RFC 8414 — endpoints, scopes, PKCE support |
| RFC 7591 dynamic client registration |
| Starts the flow; hands off to Pinterest |
| Where Pinterest returns |
|
|
PKCE (S256) is mandatory, plain is refused. Authorization codes are single-use and live ten minutes. The iss parameter is returned per RFC 9207 so clients can detect an authorization-server mix-up. An unauthenticated POST /mcp answers 401 with a WWW-Authenticate header pointing at the resource metadata, which is what triggers a client to begin the flow.
Every token — codes, access, refresh, even client ids — is an AES-256-GCM envelope carrying its own payload and expiry, so there is no session store or database. The tradeoff: revocation means rotating MCP_AUTH_SECRET, which invalidates everything at once.
This needs Pinterest Standard access. Under Trial access, Pinterest only authorizes the app owner's own account, so the flow works but nobody else can complete it. Standard access requires an approved Trial app, compliance with the Developer Guidelines, and a video of the app using the API — see access tiers.
Run it hosted
A live instance runs on Railway:
https://pinterest-mcp-http-production.up.railway.app/mcpIt holds no credentials of its own — every caller supplies their own Pinterest token per request, and GET /health reports default_token_configured: false to prove it. Treat it as a convenience for trying the server out; run your own instance for anything that matters.
The HTTP transport turns the same server into a remote MCP endpoint:
npm run start:http # POST /mcp, GET /healthIt is stateless — each request builds its own server, transport and API client, then tears them down. Callers pass their own Pinterest token per request, so one deployment can serve several accounts without a caller's credentials leaking into another's session:
curl -X POST https://your-host/mcp \
-H 'Authorization: Bearer <pinterest_access_token>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'X-Pinterest-Access-Token works too, for clients that already use Authorization for their own gateway. If neither is present the server falls back to PINTEREST_ACCESS_TOKEN, and returns 401 if that is unset as well.
Docker
docker build -t pinterest-mcp .
docker run -p 3000:3000 -e PINTEREST_ACCESS_TOKEN=your_token pinterest-mcpThe image is a two-stage build running as the unprivileged node user.
Configuration
Variable | Default | Purpose |
| — | Token the server acts with. Optional: without it only the OAuth tools work. |
| — | App id, for the OAuth tools. |
| — | App secret, for the OAuth tools. |
| — | Must match a URI registered on the app exactly. |
|
| Set to |
| production v5 | Override the API root outright. |
|
| Attempts for retryable failures (429 / 5xx). |
|
| HTTP transport only. |
| derived from the request | Absolute origin of the deployment. Used for OAuth metadata and callback URLs; set it behind a proxy. |
| random per boot | Seals the OAuth tokens this server issues. Required for a hosted deployment — without it, issued tokens die on restart. |
The server starts whether or not a token is present, so an assistant can always reach the OAuth tools to obtain one.
Design notes
Errors are tool results, not exceptions. A Pinterest failure comes back as an
isErrorresult carrying the status, Pinterest's own message, and a hint naming the likely fix — a missing scope, an expired token, a bad id. Agents can act on that; a thrown exception just aborts the turn.429s and 5xx are retried with exponential backoff, honouring
Retry-Afterup to a 30-second cap so a hostile header cannot stall a call.Destructive tools are annotated.
pinterest_delete_boardandpinterest_delete_pincarrydestructiveHint, so clients can gate them behind confirmation. Deleting a board deletes its pins too.Media shapes are a discriminated union, so the model is told which fields go together rather than discovering it from a 400.
Development
npm run build # compile
npm run dev # compile in watch mode
npm run typecheck # types only, no emit
npm test # unit tests + a real MCP handshake over stdioTests cover the client (auth, query building, retries, error shaping) and boot the actual server over stdio to assert the tool surface, schemas and annotations. No network access required.
Contributing
Issues and pull requests are welcome. Adding an endpoint is usually one function in the relevant src/tools/*.ts module.
License
Available Tools
35 toolspinterest_api_requestCall any Pinterest v5 endpointA
Escape hatch: issue an arbitrary request against the Pinterest API v5 using the configured token. Use this only for endpoints no dedicated tool covers — ad accounts, catalogs, audiences, conversion events. Prefer the specific tools when one exists: they validate input and explain failures. Path is relative to https://api.pinterest.com/v5.
Inputs: path (required), body, query, method.
pinterest_build_oauth_urlBuild an OAuth authorization URLARead-only
Build the Pinterest consent URL to open in a browser. The user approves, Pinterest redirects to your redirect_uri with a code query parameter, and you exchange that code with pinterest_exchange_oauth_code. Makes no network call.
Inputs: state, app_id, scopes, redirect_uri.
pinterest_create_boardCreate boardA
Create a new board on the authenticated account. Scope: boards:write (boards:write_secret for SECRET boards).
Inputs: name (required), privacy, description, ad_account_id.
pinterest_create_board_sectionCreate board sectionB
Add a section to a board. Scope: boards:write.
Inputs: name (required), board_id (required), ad_account_id.
pinterest_create_pinCreate pinA
Create a pin on one of the authenticated user's boards. Requires a board_id and a media_source. Scope: pins:write (pins:write_secret for secret boards).
Inputs: board_id (required), media_source (required), link, note, +6 more.
pinterest_delete_boardDelete boardADestructiveIdempotent
Permanently delete a board AND every pin saved to it. This cannot be undone. Scope: boards:write.
Inputs: board_id (required), ad_account_id.
pinterest_delete_board_sectionDelete board sectionADestructiveIdempotent
Delete a board section. Pins in the section move back to the board root rather than being deleted. Scope: boards:write.
Inputs: board_id (required), section_id (required), ad_account_id.
pinterest_delete_pinDelete pinADestructiveIdempotent
Permanently delete a pin. This cannot be undone. Scope: pins:write.
Inputs: pin_id (required), ad_account_id.
pinterest_exchange_oauth_codeExchange an OAuth code for tokensA
Exchange the code from the OAuth redirect for an access token and refresh token. Access tokens last about 30 days; refresh tokens about a year. Store both securely.
Inputs: code (required), app_id, app_secret, redirect_uri.
pinterest_get_boardGet boardARead-only
Fetch a single board by id, including its pin and follower counts. Scope: boards:read.
Inputs: board_id (required), ad_account_id.
pinterest_get_mediaGet media upload statusARead-only
Check a registered upload. Status moves registered -> processing -> succeeded (or failed). Scope: pins:read.
Inputs: media_id (required).
pinterest_get_multi_pin_analyticsGet analytics for several pinsARead-only
Analytics for up to 100 pins in one call — far cheaper than looping pinterest_get_pin_analytics. Scope: pins:read.
Inputs: pin_ids (required), end_date (required), start_date (required), app_types, +2 more.
pinterest_get_pinGet pinARead-only
Fetch a single pin by id, including its media, link and board. Scope: pins:read.
Inputs: pin_id (required), pin_metrics, ad_account_id.
pinterest_get_pin_analyticsGet analytics for one pinBRead-only
Daily analytics for a single pin over a date window of up to 90 days. Scope: pins:read.
Inputs: pin_id (required), end_date (required), start_date (required), app_types, +3 more.
pinterest_get_top_pinsGet top performing pinsBRead-only
Rank the account's pins by a metric over a date window — the tool to reach for when asked what is working. Scope: user_accounts:read and pins:read.
Inputs: end_date (required), start_date (required), sort_by, app_types, +7 more.
pinterest_get_top_video_pinsGet top performing video pinsBRead-only
Rank the account's video pins by a video metric over a date window. Scope: user_accounts:read and pins:read.
Inputs: end_date (required), start_date (required), sort_by, app_types, +7 more.
pinterest_get_user_accountGet my accountARead-only
Fetch the authenticated user's account: username, id, account type, profile image and follower counts. The fastest way to verify a token works. Scope: user_accounts:read.
Inputs: ad_account_id.
pinterest_get_user_analyticsGet account analyticsARead-only
Daily account-level analytics over a date window of up to 90 days. Scope: user_accounts:read and pins:read.
Inputs: end_date (required), start_date (required), source, app_types, +6 more.
pinterest_list_board_pinsList pins on a boardBRead-only
List the pins saved to a board. Scope: pins:read and boards:read.
Inputs: board_id (required), bookmark, page_size, pin_metrics, +2 more.
pinterest_list_boardsList boardsARead-only
List the authenticated user's boards, newest first. Scope: boards:read (plus boards:read_secret to include secret boards). Returns a bookmark cursor when more pages exist.
Inputs: privacy, bookmark, page_size, ad_account_id.
pinterest_list_board_section_pinsList pins in a board sectionARead-only
List the pins saved inside one section of a board. Scope: pins:read and boards:read.
Inputs: board_id (required), section_id (required), bookmark, page_size, +2 more.
pinterest_list_board_sectionsList board sectionsARead-only
List the sections within a board. Scope: boards:read.
Inputs: board_id (required), bookmark, page_size, ad_account_id.
pinterest_list_claimed_websitesList claimed websitesARead-only
List the websites claimed by the authenticated account, with their verification status. Scope: user_accounts:read.
Inputs: bookmark, page_size.
pinterest_list_followersList my followersBRead-only
List the accounts following the authenticated user. Scope: user_accounts:read.
Inputs: bookmark, page_size.
pinterest_list_followingList who I followARead-only
List the users and boards the authenticated user follows. Scope: user_accounts:read.
Inputs: bookmark, feed_type, page_size.
pinterest_list_mediaList media uploadsARead-only
List the account's registered media uploads and their statuses. Scope: pins:read.
Inputs: bookmark, page_size.
pinterest_list_pinsList pinsBRead-only
List the authenticated user's pins, newest first. Scope: pins:read (plus pins:read_secret for pins on secret boards).
Inputs: bookmark, pin_type, page_size, pin_filter, +4 more.
pinterest_refresh_access_tokenRefresh an access tokenA
Trade a refresh token for a fresh access token. Use this when calls start failing with 401.
Inputs: refresh_token (required), scope, app_id, app_secret.
pinterest_register_mediaRegister a video uploadA
Step 1 of video pin creation: register an upload and receive one-time S3 form fields. POST the video to upload_url with those upload_parameters as multipart form fields, poll pinterest_get_media until status is 'succeeded', then pass the media_id to pinterest_create_pin with source_type 'video_id'. Scope: pins:write.
Inputs: media_type (required).
pinterest_save_pinSave (repin) a pinA
Save an existing pin to one of the authenticated user's boards — the API equivalent of a repin. Scope: pins:write.
Inputs: pin_id (required), board_id (required), ad_account_id, board_section_id.
pinterest_search_my_boardsSearch my boardsARead-only
Full-text search across the authenticated user's own boards. Scope: boards:read.
Inputs: query (required), bookmark, page_size, ad_account_id.
pinterest_search_my_pinsSearch my pinsARead-only
Full-text search across the authenticated user's own pins. This does NOT search Pinterest globally — the v5 API exposes no public discovery search. Scope: pins:read.
Inputs: query (required), bookmark, page_size, ad_account_id.
pinterest_update_boardUpdate boardAIdempotent
Update a board's name, description or privacy. Only the fields you pass are changed. Scope: boards:write.
Inputs: board_id (required), name, privacy, description, +1 more.
pinterest_update_board_sectionRename board sectionBIdempotent
Rename an existing board section. Scope: boards:write.
Inputs: name (required), board_id (required), section_id (required), ad_account_id.
pinterest_update_pinUpdate pinAIdempotent
Update a pin's text fields, link or board placement. Media cannot be changed after creation. Scope: pins:write.
Inputs: pin_id (required), link, note, title, +5 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 35 tools
Each tool targets a distinct resource and action, with clear boundaries between boards, sections, pins, media, analytics, and OAuth flows. The few similar-sounding tools (e.g., list_board_pins vs. list_board_section_pins, get_top_pins vs. get_top_video_pins) are differentiated by precise descriptions.
All tools follow a consistent snake_case verb_noun pattern with a uniform `pinterest_` prefix. Names like `get_user_account`, `update_board`, `list_board_pins`, and `delete_pin` are predictable and readable.
With 35 tools, the surface is heavy for a single MCP server, exceeding the 25-tool threshold for being too many. While Pinterest's API is broad, many granular tools (e.g., separate board section operations) could be consolidated or left to the escape hatch `pinterest_api_request`.
The set covers full CRUD for boards, sections, and pins, plus media upload lifecycle, analytics, user account, followers, search, and OAuth. The `pinterest_api_request` escape hatch fills any remaining gaps for ad accounts or catalogs.
Maintenance
Related MCP Connectors
Pinterest pins, boards, profiles and keyword search to JSON: saves, images, links, creators.
Remote streamable-HTTP MCP server running on a single Cloudflare Worker. Your assistant gets live Airbnb, Amazon, Booking.com, Google Flights, Maps and Reddit data, social search on X, Instagram and TikTok, the Meta Ad Library, and image/video generation without any keys. Connect your own accounts to let it send WhatsApp or Telegram messages, work an IMAP inbox, manage Meta Ads campaigns and publish to X and LinkedIn. OAuth 2.1 with PKCE; stored credentials are AES-256-GCM encrypted.
Tailwind MCP server — list Pinterest accounts and create, schedule, and manage Pinterest posts.
Persistent memory and knowledge management for AI agents with semantic search and 50+ tools.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to interact with the Pinterest API, allowing management of boards and pins through natural language commands.148 npm39MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to manage Pinterest boards and pins, create and update pins, and track analytics via the Pinterest API v5.8MIT
- AlicenseAqualityCmaintenanceEnables AI agents to manage Pinterest accounts through the Pinterest API v5, including creating and editing pins, organizing boards, searching public content, and retrieving pin and account analytics.11MIT
- AlicenseBqualityCmaintenanceEnables interaction with the Pinterest API v5 through natural language, covering 263 operations including pins, boards, analytics, and advertising tools, with configurable read-only and non-destructive modes.814 npmMIT