Announcing CIMD support for MCP Client registration
Learn more

MCP vs API for Integrating Pinterest to AI Agents

Pinterest has announced a hosted MCP server that is still in a partner-only alpha, community MCP servers each cover a narrow slice, and the Pinterest API v5 is the surface you can ship a product on today. The API brings scoped OAuth, a refresh token that dies after 60 days without use, and access tiers that decide whether published Pins are even visible. This guide compares the options for an agency agent and shows how to run per-client grants in production.

TL;DR

  • Pinterest has announced an official Model Context Protocol (MCP) server at https://api.pinterest.com/mcp, but as of November 2026 it is not generally available. Pinterest unveiled it on June 17, 2026 as an alpha with agency and ad-tech partners such as PMG, Dentsu, Havas, and Pacvue, and its developer page still asks you to sign up to be notified "when it launches." No tool list is published.
  • The Pinterest API v5 at https://api.pinterest.com/v5 is the only surface you can build a product on today. It uses OAuth 2.0 with granular scopes such as boards:read, pins:write, ads:read, and user_accounts:read, 30-day access tokens, and a continuous refresh token that expires after 60 days unless you refresh it, which you can do indefinitely.
  • Two access rules shape every Pinterest agent. New apps start on Trial access: 1,000 requests per day, and every board and Pin they create is visible only to the creator. And apps registered on or after September 14, 2026 get 403 PINNER_DATA_ACCESS_DENIED when they read boards or Pins for personal (non-business) accounts.
  • The MCP path, official or community, is a reasonable fit for one marketer exploring their own account from Claude or Cursor. The API is the path for an agency agent that manages boards, publishes Pins, and reports analytics for many client brands, each with its own consent and revocation.
  • Scalekit's pinterest connector exposes 72 prebuilt tools over the Pinterest API (50 read, 19 write, 3 destructive) with OAuth 2.0 through your own Pinterest app. Scalekit stores and refreshes each user's token in the token vault, so the MCP vs API decision doesn't change your auth infrastructure.

The decision your Pinterest agent forces first

Your agent works for a social media agency. Every Monday it needs to tell each client which Pins drove outbound clicks last week, pull the trending keywords for their category before the content team plans the next batch, check which boards are thin on fresh Pins, and publish the approved creatives to the right board sections. You go looking for a Pinterest MCP server and find two very different things: an official one Pinterest announced at Cannes Lions that you can't sign up for yet, and a scatter of community servers ranging from an Ads API wrapper to a headless-browser scraper. Pinterest also ships a mature v5 REST API with scoped OAuth. That changes the shape of the decision: the official MCP option is real but gated, the community options each solve a narrow slice, and the first-party option you can actually ship on is the API you wire yourself. Here's how to pick.

What Pinterest MCP and Pinterest API actually are

Pinterest MCP

Pinterest's official server was announced on June 17, 2026, alongside the Ask Pinterest app, ahead of Cannes Lions. PPC Land's coverage describes it as an alpha that lets external AI tools reach campaign performance data, analytics, and keyword insights, built with partners including PMG, Pacvue, Dentsu, Havas, Innovid by Mediaocean, and Omnicom's Jump450. Pinterest followed with a community announcement on September 22, 2026 that frames it around "AI assistants, copilots, and agentic workflows."

The developer page shows what it will look like. It is hosted, not local: the sample client config points an HTTP transport at https://api.pinterest.com/mcp with a bearer token in the Authorization header. The page names Claude, Cursor, and VS Code as clients, mentions OAuth 2.0 authorization and granular permissions, and lists capability areas: searching Pins, exploring and managing boards, trend discovery, audience and keyword insights, and creating, updating, and optimizing ads content. It names no tools, lists no scopes, and gives no launch date. Until it ships, you can't evaluate its tool surface, only its intent.

Community servers fill the gap, each covering a narrow slice:

  • cesteral/mcp-open-advertising publishes @cesteral/pinterest-mcp with 23 tools for the Pinterest Ads API: campaign CRUD, targeting, budgets, async performance reports, and audience size estimates. It runs locally over stdio or as a self-hosted Streamable HTTP endpoint, authenticates with a bearer token, and says it is not suited to organic Pin scheduling.
  • terryso/mcp-pinterest (MIT) exposes pinterest_search, pinterest_get_image_info, and pinterest_search_and_download. Its README calls it an "unofficial scraper" that reads public Pinterest pages with a headless browser and does not use the official API. No credentials, no account data.
  • collactivelabs/pinterest-mcp-server wraps seven API calls (user info, list/create/get boards, list/create/get Pins), reads PINTEREST_APP_ID and PINTEREST_APP_SECRET from .env, and runs a browser OAuth flow against a localhost:3052 redirect. One token, one user, one laptop.

Pinterest API

The Pinterest API v5 is REST at https://api.pinterest.com/v5, published as an OpenAPI spec (version 5.28.0 at the time of writing). It covers user accounts and their analytics, boards and board sections, Pins and media uploads, Pin and board search, ad accounts, campaigns, ad groups, ads, audiences, ads reporting, catalogs and product groups, trends and keyword insights, and the Conversions API at POST /v5/ad_accounts/{ad_account_id}/events. Lists paginate with a bookmark cursor and page_size.

Auth is OAuth 2.0 in three grants, per Pinterest's authentication docs:

  • Authorization Code: send the user to https://www.pinterest.com/oauth/ with client_id, redirect_uri, response_type=code, scope, and state, then exchange the code at POST https://api.pinterest.com/v5/oauth/token, authenticating your app with HTTP Basic (client_id:client_secret).
  • Refresh Token: the same endpoint with grant_type=refresh_token returns a new access token and a new continuous refresh token.
  • Client Credentials: returns a pinc-prefixed token for the app owner's own account only, with no refresh token, a 30-day lifetime, mandatory two-factor auth on the app, and some sensitive endpoints unavailable.

Many organic endpoints also accept an optional ad_account_id. That is Business Access: a token user who holds a role on someone else's ad account can act as that account's owner, for example reading Pin analytics with the Admin or Analyst role. For agencies it is the most important parameter in the API.

Comparing them where it matters for agents

What your agent can actually do

Capability
Official Pinterest MCP (alpha)
Community MCP servers
Pinterest API v5
Available to build on today
No: Partner-only alpha; sign-up waitlist
Yes: Install and run yourself
Yes: After Trial or Standard app approval
List and read boards, sections, Pins
Limited: Announced capability area; tools unpublished
Limited: Seven-tool server only; scraper reads public pages
Yes
Search boards and Pins
Limited
Limited: Scraper search over public pages, no account context
Yes
Publish Pins, including video
Limited: Announced for ads content
Limited: Image Pins in the seven-tool server
Yes
Organic analytics (account, Pin, top Pins)
Limited
No
Yes
Trends and keyword insights
Limited
No
Yes
Campaigns, ad groups, ads, reporting
Limited: Announced; the alpha's focus
Yes: 23-tool Ads server
Yes: Ads endpoints under each ad account
Catalogs, feeds, product groups
No
No
Yes
Act for client accounts via Business Access
No
No
Yes: Optional ad account on organic and ads endpoints
React to new Pins, saves, or comments
No
No
No: No webhooks; poll on a schedule
Per-user OAuth for many customers
Limited: OAuth mentioned; flow undocumented
No: One token or no auth
Yes: Authorization code flow per user with explicit scopes

The official column is mostly "announced," and that is the honest status. Pinterest's alpha targets agencies and ad-tech partners working on campaign reporting and planning, which overlaps with what an agency agent wants, but you can't see the tool names, you can't test scope behavior, and you can't put a launch date in a project plan.

The community column splits by approach. The Ads server is the most complete for paid media. The scraper is the only one that needs no Pinterest approval, which is also why it should never sit behind a product: it reads public pages, not a customer's account, and it bypasses the platform's terms rather than its API. None of them handles organic analytics or catalogs.

The API column has one empty row that surprises people. Pinterest v5 has no webhooks for organic activity. Nothing tells your agent that a Pin was saved 400 times overnight; the agent asks. The Conversions API runs the other way: you send purchase and lead events to Pinterest.

What the MCP path makes easier

"Which of the Lumen Home client's Pins published in October drove the most outbound clicks, and which board should get the next batch" on the raw API means calling GET /v5/boards with ad_account_id, walking bookmark cursors through each board's Pins, batching Pin IDs into groups of 100 for the analytics endpoint, keeping start_date within 90 days of today, and handling the fact that Pins created before March 20, 2023 only report lifetime metrics for video and Idea formats. Every intermediate response lands in the model's context.

An MCP server collapses that into named tools the model picks itself, and for the official server, Pinterest would own the schemas. That is the appeal: a marketer opens Claude, connects, and asks. Writing the schema is the hard part. Not the API call.

The tradeoff today is availability and ownership. The official server is gated. Community servers change when their maintainers push, cover narrow slices, and authenticate as whoever pasted the token. An agent tuned against one inherits all three properties.

The auth path each one puts you on

On the official MCP path, Pinterest's sample config passes a bearer token in a header, and the page mentions OAuth 2.0 with granular permissions. Until the flow is documented, you don't know whether it issues per-user tokens through a standard MCP authorization flow, which scopes it requests, or how long its tokens live. A hosted server that holds a bearer token in a client config file on a laptop is the same custody model as a pasted API key, whatever minted the token.

On the community MCP path, auth is whatever the server reads. The Ads server reads a bearer token from its environment. The seven-tool server runs its own OAuth flow against your app's credentials on localhost, then keeps the token on that machine. The scraper uses no auth at all, which means it can only see what an anonymous visitor sees.

On the direct API path, you get the most complete OAuth story of any surface here. Scopes are explicit and granular: boards:read versus boards:read_secret, pins:write versus pins:write_secret, plus ads:read, ads:write, catalogs:read, user_accounts:read, biz_access:read, and billing:read. A reporting agent can request boards:read,pins:read,user_accounts:read and never hold write access. Access tokens last 2,592,000 seconds (30 days). Pinterest now issues only the continuous refresh token: it expires 60 days after issue, each refresh returns a new one with a new refresh_token_expires_at, and if you miss the window the user must go through the Authorization Code flow again. Apps created before September 25, 2025 must send continuous_refresh=true to get this behavior.

Two quirks follow. First, POST /v5/oauth/token/revoke "only supports tokens issued for system users," per the spec, so you can't revoke an ordinary user's token through the API; you stop using it and let it age out, or the user removes the app from their Pinterest settings. Second, Business Access means a single token can reach many accounts. An agency strategist with Admin on 60 client ad accounts holds one token that, with the right ad_account_id, reads and writes boards and Pins for all 60. What the user can't do, the agent can't do. Everything the user can do across those accounts, the agent can.

Both paths still need per-user isolation

In a multi-tenant B2B agent, MCP gives you a credential per user and the direct API gives you a credential per user. In neither case does the path itself solve storage, rotation, or revocation; those are infrastructure problems regardless of which path you choose.

Pinterest sharpens this in two directions. Business Access concentrates reach: the convenient design is one agency staffer's token plus a table of client ad_account_id values, and that token's blast radius is every client the staffer can touch. The alternative is a connected account per client brand, authorized by the client's own business account, so each client can be revoked alone. And the September 14, 2026 data access rule pushes you toward business accounts: apps registered on or after that date get 403 PINNER_DATA_ACCESS_DENIED on get board, list Pins on board, list board sections, and get Pin for personal accounts. Pinterest's explainer says to treat it as an authorization state, not a temporary failure, and not to retry in a loop. Your onboarding flow has to detect a personal account and ask the client to convert it before the agent's first board read.

Recommended reading: OAuth vs API keys for AI agents

What you own in production

On the MCP path, you own the server you run, its version pinning, and its failure modes, because no community server is backed by Pinterest. The official server promises to handle authentication, rate limits, and response formatting, which is exactly what you'd want, once you can use it. Neither path learns about anything that happens in Pinterest between prompts.

On the API path, you own the token lifecycle, pagination, rate limits, and the access tier. Pinterest's rate limits differ sharply by tier:

  1. Trial access allows 1,000 requests per day per app across every user, with org_write capped at 300 per day. Boards and Pins created on Trial are sandbox entities visible only to their creator, so a Trial agency agent "publishes" Pins no client audience will ever see.
  2. Standard access allows 100 requests per second per user per app overall, with category limits per minute per user per app: org_read 1,000, org_write 100, org_analytics 60, trends_read 60, ads_analytics 300, catalogs_read 100.
  3. Getting Standard requires an upgrade request from My apps with a video demo that shows the OAuth authorization flow and a live Pinterest integration, even if you are the only user. Wireframes don't qualify. Pinterest's forums carry reports of reviews pending for weeks, so put it on the critical path.

Watch x-ratelimit-limit, x-ratelimit-remaining, and x-ratelimit-reset on responses. The 60-per-minute org_analytics limit is the one an analytics agent hits first: a Monday run across 60 clients that calls account analytics, top Pins, and per-board Pin analytics will queue behind it unless you batch Pin IDs into the 100-per-call analytics endpoint.

The token calendar is the other recurring job. Access tokens die at 30 days and continuous refresh tokens at 60, so a client who authorized in August and whose account the agent didn't touch through October needs a fresh consent. A daily job that refreshes every token older than 25 days is not optional.

When to use MCP, when to use the API

Use a Pinterest MCP server when:

  • A single marketer in Claude or Cursor wants to ask "which of my boards got the most saves this month" about their own account, and a token on their own laptop is acceptable.
  • Your agency is in Pinterest's MCP alpha and the work is campaign reporting and planning for ad accounts, which is what the alpha is built around.
  • A media buyer wants ad hoc paid-media reporting through the 23-tool community Ads server, read-only, and someone owns pinning and reviewing its code.

Use the Pinterest API when:

  • Your product serves many brands or agencies, and each client business account needs its own Authorization Code grant with explicit scopes, revocable without touching the others.
  • A scheduled, deterministic pipeline publishes approved creatives through POST /v5/pins and video uploads through POST /v5/media, then reports performance a week later.
  • The agent needs organic analytics, trends, or catalogs, none of which any available MCP server covers.
  • The agent runs headless on a schedule, polls instead of waiting for webhooks that don't exist, and must respect per-category rate limits across many client accounts.

The credential problem that exists on both paths

Both paths hand you a Pinterest credential per user. Neither hands you a vault. An agency platform serving 30 agencies with 60 client brands each holds 1,800 Pinterest grants if it isolates by client. Each one carries a 30-day access token and a 60-day continuous refresh token that must be refreshed on time or lost, stored encrypted, kept out of logs and LLM context, mapped to exactly one client at one agency, and dropped when the client leaves. Pinterest's API can't revoke ordinary user tokens for you, so "dropped" means your system stops using it and forgets it.

The MCP path doesn't reduce the count. It moves tokens into client config files, where nobody can list or refresh them centrally, and a 60-day refresh window quietly expires on every laptop that wasn't opened in time. The tempting shortcut on the API path is one strategist's token plus Business Access across every client. Fewer secrets, larger blast radius: that token reaches all 60 brands, and when the strategist leaves the agency, so does the only credential the agent was running on.

Scalekit's pinterest connector runs the OAuth flow per user, stores each token in the token vault, refreshes it before Pinterest's windows close, and resolves it at request time, so the MCP vs API decision doesn't change your auth infrastructure. Your agent passes an identifier; credentials never touch the agent runtime or the LLM context.

Recommended reading: how to handle token refresh for AI agents

How to connect a Pinterest agent with Scalekit

Scalekit's pinterest connector is an API-path connector built by Scalekit with 72 tools: 50 read-only, 19 write, and 3 destructive. It covers boards and board sections, Pins and product tags, media uploads, user account analytics and top Pins, Pin analytics, search, trends and related terms, catalogs, feeds, local stores, product groups, and catalog reports. Business Access tools accept an optional ad_account_id, and the token user must hold an appropriate role on that ad account. The core set for an agency reporting agent:

Tool
What it does
pinterest_user_account_get
Returns the connected account's own profile; call it first to confirm whose grant this is
pinterest_boards_list
Lists the account's boards plus group boards it collaborates on, filterable by privacy
pinterest_board_sections_list
Lists the sections on a board
pinterest_board_pins_list
Lists a board's Pins with bookmark paging and page_size up to 250
pinterest_pin_get
Returns one Pin by ID
pinterest_user_account_analytics_get
Returns account-level engagement totals for a window of up to 90 days
pinterest_pins_analytics_list
Returns analytics for up to 100 Pins at once (beta, not available to all Pinterest apps)
pinterest_trends_keywords_list
Lists trending search keywords for a region and trend type

The connector authenticates with OAuth 2.0 through your own Pinterest app; Scalekit stores and refreshes user tokens.

Prerequisites and the connection name

In the Scalekit dashboard, go to AgentKit > Connections, create a Pinterest connection, and copy its redirect URI. In Pinterest's developer portal, register your app under My apps and add that redirect URI. Enter the app's client ID (the App ID) and client secret on the Scalekit connection. The name you give the connection is the connection_name your code passes, and the string must match the dashboard exactly. This is the most common integration error. Copy SCALEKIT_ENVIRONMENT_URL, SCALEKIT_CLIENT_ID, and SCALEKIT_CLIENT_SECRET from Developers > API Credentials into .env. Plan for Pinterest's Standard access review before launch: on Trial, Pins your agent creates stay invisible to everyone but the creator. See the connector docs.

pip install scalekit-sdk-python python-dotenv langchain-openai

Authorize each user once

The scenario uses one connected account per client brand, authorized by the client's own Pinterest business account, so the identifier encodes both the agency and the client. get_or_create_connected_account returns that account. If it is not ACTIVE, get_authorization_link returns the Pinterest consent link the client opens once. Then pinterest_user_account_get confirms which Pinterest account the grant belongs to, so a client who approved from a personal profile instead of the brand's business account is caught before the agent reads a board and hits PINNER_DATA_ACCESS_DENIED.

import os from dotenv import load_dotenv from scalekit import ScalekitClient load_dotenv() scalekit_client = ScalekitClient( env_url=os.environ["SCALEKIT_ENVIRONMENT_URL"], client_id=os.environ["SCALEKIT_CLIENT_ID"], client_secret=os.environ["SCALEKIT_CLIENT_SECRET"], ) actions = scalekit_client.actions CONNECTION_NAME = "pinterest" # must match the connection name in AgentKit > Connections identifier = "agency_northpeak_client_lumenhome" # your app's stable ID for this client brand account = actions.get_or_create_connected_account( connection_name=CONNECTION_NAME, identifier=identifier, ) if account.connected_account.status != "ACTIVE": link = actions.get_authorization_link( connection_name=CONNECTION_NAME, identifier=identifier, ) print("Authorize Pinterest:", link.link) input("Press Enter after approving access in Pinterest...") account = actions.get_or_create_connected_account( connection_name=CONNECTION_NAME, identifier=identifier, ) if account.connected_account.status != "ACTIVE": raise RuntimeError(f"Pinterest is {account.connected_account.status}, not ACTIVE") me = actions.execute_tool( tool_name="pinterest_user_account_get", tool_input={}, connection_name=CONNECTION_NAME, identifier=identifier, ) profile = me.data if isinstance(me.data, dict) else {} if profile.get("account_type") != "BUSINESS": print("Warning: not a business account; board and Pin reads may return PINNER_DATA_ACCESS_DENIED") print(me.execution_id, profile.get("username"), profile.get("account_type"))

Retrieve the Pinterest tools this user can call

This is not a catalog load. The agent asks for the tools this client's connected account is authorized to call, then narrows them to the seven a read-only reporting assistant needs. Pinterest scopes already separate pins:read from pins:write, but your connection may request both because the same grant also powers publishing. This list is where the reporting agent's reach gets smaller than the grant: pinterest_pin_create, pinterest_board_create, and pinterest_catalog_items_batch_create, which can delete catalog items in bulk, never reach the model, so it cannot publish to a client's board or wipe a product feed while answering a question about saves.

tools = actions.langchain.get_tools( identifier=identifier, connection_names=[CONNECTION_NAME], tool_names=[ "pinterest_user_account_get", "pinterest_boards_list", "pinterest_board_sections_list", "pinterest_board_pins_list", "pinterest_user_account_analytics_get", "pinterest_pins_analytics_list", "pinterest_trends_keywords_list", ], page_size=100, ) tool_map = {t.name: t for t in tools} print(sorted(tool_map))

Run the agent loop

The loop runs until the model stops requesting tools. Every call executes with this client's Pinterest grant and sees only that brand's boards and Pins. The system prompt carries the details that trip models up on this API: bookmark paging, the 90-day analytics window, the 100-Pin batch limit, and the analytics rate limit.

from datetime import date from langchain_core.messages import HumanMessage, SystemMessage, ToolMessage from langchain_openai import ChatOpenAI llm = ChatOpenAI(model="gpt-4o").bind_tools(tools) messages = [ SystemMessage( "You are a Pinterest reporting assistant for a social media agency. Today is " f"{date.today().isoformat()}. Call pinterest_boards_list to find boards and " "pinterest_board_pins_list to find Pins; when a response includes a bookmark value, " "pass it as bookmark on the next call to get the next page. Analytics dates use " "YYYY-MM-DD and start_date cannot be more than 90 days before today. Use " "pinterest_pins_analytics_list with at most 100 comma-separated pin_ids per call " "instead of one call per Pin, because analytics calls are rate limited to 60 per " "minute. Use pinterest_trends_keywords_list for trend questions. You are read-only: " "never claim you created, edited, or published anything." ), HumanMessage( "Which Pins on the Fall Living Room board drove the most outbound clicks over the " "last 30 days, and which home decor keywords are trending in the US right now that " "we should target in next week's batch?" ), ] while True: response = llm.invoke(messages) messages.append(response) if not response.tool_calls: print(response.content) break for tc in response.tool_calls: tool = tool_map.get(tc["name"]) if tool: result = tool.invoke(tc["args"]) else: result = f"Unknown tool: {tc['name']}" messages.append(ToolMessage(content=str(result), tool_call_id=tc["id"]))

Deterministic calls with execute_tool

For pipelines, skip the model. execute_tool runs one tool with a fixed input and returns data plus an execution_id you can log. This job runs every Monday at 06:00 for every client brand an agency manages. Pinterest has no webhooks, so a schedule is the trigger. It pulls last week's account totals with pinterest_user_account_analytics_get, and it treats a PINNER_DATA_ACCESS_DENIED response as an onboarding problem to surface, not an error to retry. Each client's numbers come from that client's own grant, so one revoked client fails alone.

from datetime import date, timedelta # Your app's mapping of client brands to Scalekit identifiers for one agency CLIENT_IDENTIFIERS = { "Lumen Home": "agency_northpeak_client_lumenhome", "Fernway Outdoor": "agency_northpeak_client_fernway", "Saltbox Kitchen": "agency_northpeak_client_saltbox", } METRICS = "IMPRESSION,SAVE,PIN_CLICK,OUTBOUND_CLICK,ENGAGEMENT" def weekly_window() -> tuple[str, str]: end = date.today() - timedelta(days=1) start = end - timedelta(days=6) return start.isoformat(), end.isoformat() def client_weekly_summary(client: str, client_identifier: str) -> dict: start_date, end_date = weekly_window() try: result = actions.execute_tool( tool_name="pinterest_user_account_analytics_get", tool_input={ "start_date": start_date, "end_date": end_date, "metric_types": METRICS, }, connection_name=CONNECTION_NAME, identifier=client_identifier, ) except Exception as exc: # surface auth states; do not retry them in a loop message = str(exc) if "PINNER_DATA_ACCESS_DENIED" in message: return {"client": client, "status": "needs_business_account"} return {"client": client, "status": "error", "detail": message} data = result.data if isinstance(result.data, dict) else {} totals = data.get("all", {}).get("summary_metrics", {}) print(result.execution_id, client, start_date, end_date) return { "client": client, "status": "ok", "window": f"{start_date} to {end_date}", "impressions": totals.get("IMPRESSION", 0), "saves": totals.get("SAVE", 0), "pin_clicks": totals.get("PIN_CLICK", 0), "outbound_clicks": totals.get("OUTBOUND_CLICK", 0), } def run_weekly_report() -> list[dict]: rows = [client_weekly_summary(c, i) for c, i in CLIENT_IDENTIFIERS.items()] for row in rows: if row["status"] != "ok": print(f"{row['client']}: {row['status']}") return rows if __name__ == "__main__": for row in run_weekly_report(): print(row)

Board-level detail needs every Pin ID on the client's active boards, which means walking bookmark cursors. The function below collects them with pinterest_board_pins_list at the maximum page size of 250, so a board with 1,200 Pins costs five calls against the 1,000-per-minute org_read limit instead of 48 at the default of 25.

def board_pin_ids(board_id: str, client_identifier: str) -> list[str]: pin_ids: list[str] = [] bookmark = None while True: tool_input = {"board_id": board_id, "page_size": 250} if bookmark: tool_input["bookmark"] = bookmark page = actions.execute_tool( tool_name="pinterest_board_pins_list", tool_input=tool_input, connection_name=CONNECTION_NAME, identifier=client_identifier, ) data = page.data if isinstance(page.data, dict) else {} pin_ids.extend(item["id"] for item in data.get("items", []) if "id" in item) bookmark = data.get("bookmark") if not bookmark: break print(f"{len(pin_ids)} Pins on board {board_id}") return pin_ids

Feed those IDs to pinterest_pins_analytics_list in groups of 100, and a 1,200-Pin board needs 12 analytics calls, well inside the 60-per-minute org_analytics limit.

Reaching the Pinterest REST API

Publishing is where an agency pipeline most often needs exact control: a human approves the creative, then code publishes it, no model in between. Here it is against the API directly, with the refresh you own on this path. The token refresh uses HTTP Basic with your app credentials and returns a new continuous refresh token that must replace the old one. Scalekit custom connectors cover authenticated REST calls the prebuilt tools don't.

import os import requests PINTEREST_API = "https://api.pinterest.com/v5" APP_ID = os.environ["PINTEREST_APP_ID"] APP_SECRET = os.environ["PINTEREST_APP_SECRET"] def refresh_tokens(refresh_token: str) -> dict: resp = requests.post( f"{PINTEREST_API}/oauth/token", auth=(APP_ID, APP_SECRET), # HTTP Basic: client_id:client_secret data={"grant_type": "refresh_token", "refresh_token": refresh_token}, timeout=30, ) resp.raise_for_status() tokens = resp.json() # Persist BOTH values: the old refresh token stops being the one that extends the 60-day window return { "access_token": tokens["access_token"], "refresh_token": tokens.get("refresh_token", refresh_token), "refresh_token_expires_at": tokens.get("refresh_token_expires_at"), } def publish_image_pin(access_token: str, board_id: str, image_url: str, title: str, description: str, link: str, ad_account_id: str | None = None) -> dict: params = {"ad_account_id": ad_account_id} if ad_account_id else {} resp = requests.post( f"{PINTEREST_API}/pins", headers={"Authorization": f"Bearer {access_token}"}, params=params, json={ "board_id": board_id, "title": title[:100], "description": description[:800], "link": link, "alt_text": title[:500], "media_source": {"source_type": "image_url", "url": image_url}, }, timeout=30, ) if resp.status_code == 429: reset = resp.headers.get("x-ratelimit-reset", "unknown") raise RuntimeError(f"Pinterest rate limit hit; resets in {reset}s") if resp.status_code == 403 and "PINNER_DATA_ACCESS_DENIED" in resp.text: raise PermissionError("Account needs business access approval; do not retry") resp.raise_for_status() return resp.json() tokens = refresh_tokens(os.environ["PINTEREST_REFRESH_TOKEN"]) pin = publish_image_pin( access_token=tokens["access_token"], board_id="1234567890123456789", image_url="https://cdn.example.com/creatives/lumen-fall-sofa-01.jpg", title="Warm neutrals for a fall living room", description="Layer oatmeal throws and walnut accents for a cozy fall refresh.", link="https://lumenhome.example.com/fall-living-room", ) print(pin["id"], tokens["refresh_token_expires_at"])

Every value in that snippet is something Scalekit otherwise holds for you: the app secret, the refresh token, the new refresh token that must be written back atomically, and the 60-day deadline. Lose a refresh write during a deploy and the client re-consents.

Multi-tool, multi-tenant Pinterest agents on a virtual MCP server

An agency reporting agent rarely stops at Pinterest. It reads the numbers, then posts a digest to the client's Slack channel. Load all 72 pinterest tools and the model holds pinterest_pin_create, pinterest_board_create, and pinterest_catalog_items_batch_create while answering "how many saves did we get last week." At roughly 200 tokens each, 72 tools is about 14,400 tokens before the agent does any work, and the model is choosing from a decision space it doesn't need. The fix is not better prompting. It is surface reduction. A Scalekit virtual MCP server declares exactly which tools an agent role can see across connections, and session tokens keep each run bound to one user's connected accounts. It is also the hosted Pinterest MCP endpoint you can use today, while Pinterest's own server is in alpha. Docs: set up a virtual MCP server.

Create the server once per agent role

from scalekit.actions.models.mcp_config import McpConfigConnectionToolMapping vmcp = actions.mcp.create_config( name="pinterest-weekly-digest", description="Reads Pinterest account and Pin analytics and posts a weekly Slack digest per client", connection_tool_mappings=[ McpConfigConnectionToolMapping( connection_name="pinterest", tools=[ "pinterest_boards_list", "pinterest_board_pins_list", "pinterest_user_account_analytics_get", "pinterest_pins_analytics_list", ], ), McpConfigConnectionToolMapping( connection_name="slack", tools=["slack_send_message"], ), ], ) print(vmcp.config.id, vmcp.config.mcp_server_url)

Mint a session token per user, per run

One server definition serves every client brand at every agency. Before each run, check that the client's accounts are active and mint a short-lived session token scoped to them. Any MCP client that speaks Streamable HTTP can connect; this example uses langchain-mcp-adapters (pip install langchain-mcp-adapters) and passes the token as a Bearer header.

import asyncio from datetime import timedelta from langchain_mcp_adapters.client import MultiServerMCPClient config_id = vmcp.config.id mcp_server_url = vmcp.config.mcp_server_url user_id = "agency_northpeak_client_lumenhome" accounts = actions.mcp.list_mcp_connected_accounts( config_id=config_id, identifier=user_id, include_auth_link=True, ) not_ready = [ a for a in accounts.connected_accounts if str(a.connected_account_status).upper() != "ACTIVE" ] for a in not_ready: print(f"{a.connection_name} is {a.connected_account_status}: {a.authentication_link}") if not_ready: raise RuntimeError("Connect the accounts above before starting this run") session_token = actions.mcp.create_session_token( mcp_config_id=config_id, identifier=user_id, expiry=timedelta(minutes=30), ).token async def load_tools(): client = MultiServerMCPClient( { "scalekit": { "transport": "streamable_http", "url": mcp_server_url, "headers": {"Authorization": f"Bearer {session_token}"}, } } ) return await client.get_tools() mcp_tools = asyncio.run(load_tools()) print([t.name for t in mcp_tools])

The agent sees five tools instead of 72 plus everything Slack exposes. The endpoint is static; the identity is per-user. A strategist can point Claude or Cursor at the same server URL, and each run acts as one client brand through that client's own OAuth grant instead of a bearer token pasted into a config file. When Pinterest's official server reaches general availability, this pattern doesn't change: a vendor MCP server becomes another connection, and the session token still decides whose credentials a run uses.

Observability: every Pinterest action tied to a user

Every execute_tool call returns an execution_id, and every execution is attributed to a connected account, which maps to one client brand at one agency. When a client asks which run published a Pin to the wrong board at 03:00 or why last Tuesday's report showed zero saves, the answer is a lookup, not an investigation.

The shared-token designs are weaker by construction. A community MCP server reads one token, so every action looks, to Pinterest, like the token's owner did it by hand. Business Access compounds that: if five strategists drive one agent through one senior strategist's token, Pinterest's records show the senior strategist acting on 60 client accounts, and nothing anywhere says which colleague asked or which client's request it served. With no webhooks, there isn't even an inbound event stream to reconcile against; your own logs are the only record of who did what. Shared credentials are a single-user solution. They do not survive a second user.

Recommended reading: audit trails for agent auth

Which one to build against

If your agency is in Pinterest's MCP alpha and the work is campaign reporting from Claude, use it and watch for the launch details, because a Pinterest-maintained server will eventually be the lowest-maintenance way to ask questions about ad accounts. If your agent is one marketer's assistant for their own account, a community server works for the slice it covers, as long as someone pins and reads its code. If you are building that assistant into a product for many brands or agencies, build on the Pinterest API v5 with per-client OAuth, Standard access, and a scheduler, because it is the only surface today with scoped consent, organic analytics, publishing, and catalogs. Either way, you hold a Pinterest grant per client whose refresh token dies at 60 days and whose reach can extend across every account Business Access touches, and that's what needs production-grade infrastructure.

FAQs

Does Pinterest have an official MCP server?

Pinterest has announced one, hosted at its API domain, but as of November 2026 it is an alpha with agency and ad-tech partners such as PMG, Dentsu, Havas and Pacvue. Its developer page asks you to sign up to be notified at launch and publishes no tool list, scopes or launch date.

What community Pinterest MCP servers exist?

Three cover narrow slices: a 23-tool server for the Pinterest Ads API, an unofficial scraper that reads public pages with a headless browser and uses no account data, and a seven-tool server for boards and Pins that runs OAuth against a localhost redirect for one user. None covers organic analytics or catalogs.

How do Pinterest API tokens work?

Access tokens last 30 days. Pinterest issues a continuous refresh token that expires 60 days after issue, and each refresh returns a new one, so a grant used regularly lasts indefinitely but one left alone for 60 days needs new consent. The revoke endpoint only supports system user tokens.

What are Pinterest's Trial and Standard access tiers?

New apps start on Trial access with 1,000 requests per day, and boards and Pins they create are visible only to the creator. Standard access raises limits to per-user, per-category budgets and requires a video demo of a working OAuth integration, which can take weeks to review.

What is Business Access on the Pinterest API?

Many endpoints accept an optional ad account, so a token user with a role on someone else's ad account can act for that account's owner. It lets one agency strategist's token reach every client they hold a role on, which is convenient and concentrates the blast radius in one credential.

How does Scalekit connect an AI agent to Pinterest?

Scalekit's Pinterest connector runs the OAuth flow through your own Pinterest app, stores each grant in a vault and refreshes it before Pinterest's windows close. It exposes 72 tools, 50 read, 19 write and 3 destructive, and works well with one connected account per client brand, served through a virtual MCP server.

Build your Pinterest agent with Scalekit

Browse the Scalekit Pinterest connector and the Pinterest connector docs with the full tool list. See Scalekit pricing, or talk to us.

No items found.
On this page
Share this article
Scalekit
Find us on ProductHunt

Acquire enterprise customers with
‍zero upfront cost.

Every feature unlocked. No hidden fees.