API

The Pinterest keyword API

Competing pins, average saves, related keywords and volume history — over HTTPS, in JSON. 15 lookups a month free, for as long as you have an account. No card, and no trial clock.

Use ChatGPT, Claude or Cursor? Connect the same key with MCP and ask in the chat.

Authentication

Every request carries your key in an Authorization header. Create one in the API tab of your dashboard — it is shown once.

Authorization: Bearer pp_live_YOUR_KEY_HERE

Base URL: https://api.pinpinterest.com/api/v1

Calling from a server, a Cloudflare Worker, or a ChatGPT / Claude connector? Use this base — it answers your code directly. The old base, https://pinpinterest.com/api/v1, keeps working, but it sits behind our bot protection, which can refuse requests from cloud hosts with an HTML 403.

What a call costs

Endpoints are either reads or lookups. Reads — /me, /check, /niches — are unlimited and never counted. Lookups come out of a monthly allowance that resets on the 1st.

  • A lookup that finds nothing is free. If we have no data for a keyword you get tracked: false, a list of near matches, and no charge.
  • …and on a paid plan it starts the crawl. That answer carries a crawl block: we queue the keyword, tell you when to ask again (retry_after_minutes) and e-mail you once it is in. Queueing costs no lookup.
  • Errors are free. A 4xx or 5xx never spends anything.
  • Batch charges per keyword resolved, not per request.
  • Read X-Quota-Remaining on any response to see where you stand without spending a call.

Plans

PlanLookups / monthPer minuteEndpoints
Free15105 of 12
Starter5,000609 of 12
Pro50,00012012 of 12
Studio200,00024012 of 12

The Free plan is not a trial. It stays at 15 lookups a month for as long as you have an account, whether or not you ever subscribe.

Endpoints

GET/v1/me

Your plan, quota and reset date. free to call

curl "https://api.pinpinterest.com/api/v1/me" \
  -H "Authorization: Bearer pp_live_YOUR_KEY_HERE"
Example response
{
  "account": { "email": "[email protected]", "plan": "free", "plan_label": "Free" },
  "quota": {
    "limit": 15,
    "used": 3,
    "remaining": 12,
    "resets_at": "2026-09-01T00:00:00.000Z",
    "resets_label": "1 Sep",
    "note": "A lookup that finds no data does not count against this."
  },
  "rate_limit": { "per_minute": 10, "remaining": 10 },
  "fields": ["term","tracked","pinsAnalysed","pinCount","niche","competition","avgSaves","maxSaves","totalSaves","estClicks","opportunity"]
}

GET/v1/check

Verify a key works. Costs no quota. free to call

curl "https://api.pinpinterest.com/api/v1/check" \
  -H "Authorization: Bearer pp_live_YOUR_KEY_HERE"
Example response
{ "ok": true, "account": { "email": "[email protected]", "plan": "free", "plan_label": "Free" } }

GET/v1/niches

The niche taxonomy we classify keywords into. free to call

curl "https://api.pinpinterest.com/api/v1/niches" \
  -H "Authorization: Bearer pp_live_YOUR_KEY_HERE"
Example response
{
  "niches": [ { "slug": "food-drink", "name": "Food & Drink", "children": [ { "slug": "air-fryer", "name": "Air Fryer" } ] } ],
  "count": 1
}

POST/v1/keywords/batch

Up to 50 keywords in one call. uses a lookupstarter+

ParameterTypeDefaultNotes
termsrequiredbodystring[]—Up to 50 keywords. Each one that returns data spends one lookup; misses are free.
curl -X POST "https://api.pinpinterest.com/api/v1/keywords/batch" \
  -H "Authorization: Bearer pp_live_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{"terms":["air fryer recipes","meal prep"]}'
Example response
{
  "results": [
    { "term": "air fryer recipes", "tracked": true, "pins_analysed": 213, "avg_saves": 310 },
    { "term": "made up term", "tracked": false, "crawl": { "request_id": 139, "status": "queued", "retry_after_minutes": 10 } }
  ],
  "requested": 2,
  "found": 1,
  "charged": 1,
  "truncated": false
}

POST/v1/keywords/request

Ask us to crawl keywords we do not have yet. We e-mail you once when they are in. free to callstarter+

ParameterTypeDefaultNotes
termsrequiredbodystring[]—Up to 25 keywords to crawl. Ones we already hold come back ready; each new one spends one of the 25 a day your account may start. Uses no lookup.
curl -X POST "https://api.pinpinterest.com/api/v1/keywords/request" \
  -H "Authorization: Bearer pp_live_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{"terms":["air fryer recipes","meal prep"]}'
Example response
{
  "requested": 2,
  "accepted": 2,
  "queued": 1,
  "message": "1 queued to crawl, 1 already in. We e-mail you once when they are in; or ask GET /v1/keywords/request with the request_ids.",
  "results": [
    { "term": "dog deshedding", "tracked": false, "queued_now": true,
      "crawl": { "request_id": 137, "status": "queued", "retry_after_minutes": 10, "queue_ahead": 0, "message": "We are crawling Pinterest for this keyword now. …" } },
    { "term": "air fryer recipes", "tracked": true,
      "crawl": { "status": "ready", "message": "We already have this keyword — look it up now." } }
  ],
  "cap": { "limit": 25, "used": 1, "remaining": 24, "window": "last 24 hours" }
}

GET/v1/keywords/request

Where your crawl requests stand: queued, crawling, ready or failed. free to callstarter+

ParameterTypeDefaultNotes
idint—One or more request_id values from a crawl block, comma-separated (up to 25).
termstring—Or the keyword itself, comma-separated for several. Only your own requests are ever returned.
curl "https://api.pinpinterest.com/api/v1/keywords/request?id=137,138" \
  -H "Authorization: Bearer pp_live_YOUR_KEY_HERE"
Example response
{
  "requests": [
    { "term": "dog deshedding", "tracked": true,
      "crawl": { "request_id": 137, "status": "ready", "message": "Crawled — look it up again now; the data is in." } },
    { "term": "husky shedding", "tracked": false,
      "crawl": { "request_id": 138, "status": "crawling", "retry_after_minutes": 5, "message": "We are crawling Pinterest for this keyword now. …" } }
  ],
  "cap": { "limit": 25, "used": 2, "remaining": 23, "window": "last 24 hours" }
}

GET/v1/keywords/top-pins

The best-performing pins for a keyword. uses a lookuppro+

ParameterTypeDefaultNotes
termrequiredstring—The keyword whose ranking pins you want.
limitint10How many pins to return, 1-50, best first.
curl "https://api.pinpinterest.com/api/v1/keywords/top-pins?term=air+fryer+recipes" \
  -H "Authorization: Bearer pp_live_YOUR_KEY_HERE"
Example response
{
  "term": "air fryer recipes",
  "tracked": true,
  "pins": [
    {
      "pin_id": "1234567890",
      "title": "15 Easy Air Fryer Dinners",
      "domain": "example.com",
      "saves": 8400,
      "score": 412.5,
      "rank": 1
    }
  ]
}

GET/v1/keywords/trend

Daily demand history — the saves on the pins ranking for a term, day by day. uses a lookuppro+

ParameterTypeDefaultNotes
termrequiredstring—The keyword to chart.
daysint90How far back to go.
curl "https://api.pinpinterest.com/api/v1/keywords/trend?term=air+fryer+recipes" \
  -H "Authorization: Bearer pp_live_YOUR_KEY_HERE"
Example response
{
  "term": "air fryer recipes",
  "tracked": true,
  "days": 90,
  "points": [ { "day": "2026-06-01", "volume": 88000, "estimated": false } ]
}

GET/v1/niches/top-pins

The best-performing pins in a niche, sorted and paged. Off-topic subjects are filtered out. uses a lookuppro+

ParameterTypeDefaultNotes
nicherequiredslug—ONE slug from /v1/niches. Selecting a parent category includes every subniche beneath it.
sortenumsavessaves, clicks, engagement, velocity, reactions, recent. There is deliberately NO impressions option: Pinterest does not publish impressions for a pin you do not own, so we will not invent one. Each pin's `comments` field is always null: Pinterest does not publish a usable comment count, so we do not print one.
limitint24How many pins per page, 1-50. Use `cursor` for the next page.
cursorstring—The next_cursor from the previous page. Paging is by POSITION, not page number, so a pin whose saves changed between two requests cannot repeat or vanish. Treat it as opaque.
min_savesint—Only pins at or above this many saves.
video0 | 1—1 for video pins only, 0 for images only. Omit for both.
max_age_daysint—Only pins Pinterest says were created within this many days. Pins with no known creation date are excluded when this is set.
curl "https://api.pinpinterest.com/api/v1/niches/top-pins?niche=travel&sort=saves&limit=24" \
  -H "Authorization: Bearer pp_live_YOUR_KEY_HERE"
Example response
{
  "niche": "travel",
  "niche_name": "Travel",
  "sort": "saves",
  "count": 24,
  "pins": [
    {
      "pin_id": "1055599428648737000",
      "title": "17 things to pack for a week in Lisbon",
      "link": "https://example.com/lisbon-packing-list/",
      "domain": "example.com",
      "image_url": "https://i.pinimg.com/originals/ab/cd/ef.jpg",
      "saves": 48210,
      "score": 91.4,
      "is_video": false,
      "reactions": 312,
      "comments": null,
      "est_clicks": 9640,
      "velocity": 62.1,
      "created_at_pin": "2026-04-02T00:00:00.000Z",
      "account": { "username": "someblogger", "followers": 88400 },
      "niche": { "slug": "travel", "name": "Travel" }
    }
  ],
  "filtered": { "removed": 9, "subjects": { "nails": 9 }, "note": "9 nail art & manicures pins filtered out of this niche." },
  "next_cursor": "48210:1055599428648737000",
  "has_more": true
}

Keywords we do not have yet

Every keyword endpoint answers a term we have not crawled with tracked: false and a crawl block. On Starter, Pro and Studio (and a pass or Lifetime) the crawl has already been queued: status is queued or crawling, then ready — or failed, which means Pinterest has very little for that term, itself a finding. Ask again after retry_after_minutes; asking again is free and is the same request, never a second one. A crawl usually lands in 10–40 minutes, and we send one e-mail for a list that finishes within the hour, not one per keyword.

For a list, POST /v1/keywords/request takes up to 25 terms in one call, and GET /v1/keywords/request?id=… reports on them. An account may start 25 new crawls a day through the API. A free key’s miss says available_on_paid_plans and queues nothing.

Errors

Every error has the same shape, so you can switch on error.code:

{ "error": { "code": "quota_exhausted", "message": "…" }, "docs": "…" }
StatusCodeMeaning
400bad_requestThe request could not be parsed.
400validation_failedA required parameter is missing or unusable.
401invalid_api_keyThe key is malformed, unknown, or has been revoked.
401unauthorizedNo key was sent. Use the Authorization header.
402plan_requiredYour plan does not include this endpoint. The body names the plan that does.
402quota_exhaustedYou have used this month’s lookups. The body carries the reset date. Retrying will not help until then.
403forbiddenThis key may not do that.
404not_foundNo such endpoint.
405method_not_allowedWrong HTTP verb for this endpoint.
429rate_limitedToo many requests this minute. Back off and retry — this did not use a lookup.
500server_errorOur fault. Safe to retry.
503not_configuredThe service is temporarily unavailable.

Note that quota_exhausted is 402, not 429. Retrying does not help until the month turns over, so it is deliberately not a code that tells your client to back off and try again.

Get a key

Create an account, open the API tab, press Create key. The free allowance never expires.

Every plan includes an API allowance — see what each one gets · Questions? Email [email protected]