Pinterest 视觉研究 API 教程 | SandBase

搭一套 Pinterest 视觉研究工作流:列出品牌的 board、读一个 board 的 pin、再用搜索扩大范围——一个 SandBase 密钥,无需 Pinterest 登录。

深色电影质感画面:Pinterest board 化为一个 board 的 pin 和一张搜索网格,汇入 Agent 内核

如果你在 Pinterest 上做视觉趋势研究——一个品牌如何策展、一个 board 收集了什么、某个主题下正在浮现什么——你想要一个可复用的循环:列出一个 handle 的 board、打开重要的那些、再用关键词搜索把网撒大。这篇 Pinterest 视觉研究 API 教程用三个 SandBase 端点把这个循环串起来,让 Agent 能端到端跑完。它建立在 Pinterest 公开数据 API 汇总页之上;建议先读那篇了解全局。

这里的一切都是公开、只读数据。不需要登录 Pinterest、不需要 SDK——但仍需要一个 SandBase API 密钥来鉴权。端点 API 参考是参数和响应信封的权威来源;下面的载荷字段名来自我实际跑的调用(测试于 2026-09-27,UTC),仅供观测——并非文档保证——请以真实响应为准核对,因为负载会随时间变化。

先说结论

  • 三个端点构成循环:user-boards → board → search。
  • 每次调用都是 POST /v1/api/pinterest/<path>,只传该端点的参数,一个 SANDBASE_API_KEY。
  • 信封可能不同:参考记录的是 outputs[0].data,但在我的调用里负载常出现在一个顶层 output 对象下,含一个 success 标记、一个结果列表和一个 cursor——两种结构都读。
  • 以自然输入链式串联:一个 handle 列出 board,每个 board 的 url 打开它的 pin,一个 query 扩大集合。

工作流一览

  1. 用 pinterest/user-boards 为一个 handle 列出 board。
  2. 用第 1 步里的一个 board url 调用 pinterest/board 打开一个 board。
  3. 用一个 query 调用 pinterest/search 用搜索扩大范围,抓到该品牌之外的 pin。

每个端点返回相同信封——一个 id、一个 status、model。参考记录负载在 outputs[0].data 下;在我的调用里它常出现在一个顶层 output 对象下,承载 success、一个结果列表和一个 cursor。failed 或 timeout 的运行带 error。两种结构都读(优先 output,回退 outputs[0].data),把 status 检查写一次。

本教程使用每个端点专属的 Model API 参考及其 POST https://api.sandbase.ai/v1/api/pinterest/... 路径。Pinterest 目录卡片暴露的是另一个 GET /apis/v1/pinterest/... 面——不要用目录路径替代。下面每一步都链接它的确切参考。

SandBase vs. Pinterest 官方平台

你的需求用
公开、只读的视觉研究SandBase Pinterest 公开数据 API
发帖、保存 pin,或以会员身份操作Pinterest 官方平台
私有或仅账号可见的数据两种公开方案都不适用

SandBase Pinterest API 页面,含本工作流用到的端点 SandBase 上的 Pinterest 端点——user-boards、board 和 search 驱动这个循环。

第 0 步:一个辅助函数管所有调用

import os
import requests

API = "https://api.sandbase.ai/v1/api"
HEADERS = {
    "Authorization": f"Bearer {os.environ['SANDBASE_API_KEY']}",
    "Content-Type": "application/json",
}

def call(path: str, payload: dict) -> dict:
    resp = requests.post(f"{API}/{path}", headers=HEADERS, json=payload, timeout=70)
    resp.raise_for_status()
    body = resp.json()
    if body.get("status") != "completed":
        error = body.get("error", {})
        raise RuntimeError(error.get("message", f"{path} 未完成"))
    # 信封可能不同:优先读顶层 output,否则 outputs[0].data。
    output = body.get("output")
    if output is None and body.get("outputs"):
        output = body["outputs"][0].get("data", {})
    return output or {}

下面每一步都用 .get() 防御式读取有名字的字段。

第 1 步:列出一个 handle 的 board

out = call("pinterest/user-boards", {"handle": "nike"})
boards = out.get("boards", []) if out.get("success") else []
print(len(boards), "个 board")

在我抓到的响应里(user-boards run id 962cce4c-cfc8-4fb1-8ed4-943109e4dd97,测试于 2026-09-27,UTC),output 带一个 success 标记、一个 boards 列表和一个 cursor。每个 board 带一个 name、一个 url、一个 pin_count 和一个 description。字段名会随时间变化——把它们当作观测到的,并对照真实响应核对。

first = next((b for b in boards if isinstance(b, dict) and b.get("url")), None)
board_url = first.get("url") if first else None

第 2 步:打开一个 board 的 pin

out = call("pinterest/board", {"url": board_url})
pins = out.get("pins", []) if out.get("success") else []
cursor = out.get("cursor")
print(len(pins), "个 pin,还有更多:", bool(cursor))

在我这次运行里(board run id 74fa4c3d-6e7d-4c0b-9d7a-61787d564a65),负载带一个 success 标记、一个 pins 列表和一个 cursor。翻页时,用返回的 cursor 重发 board——确切参数名请对照 board API 参考核对。

Pinterest board 端点的 SandBase API 参考 board 参考——url 参数和响应路径的事实来源。

第 3 步:用搜索扩大范围

out = call("pinterest/search", {"query": "minimalist kitchen"})
pins = out.get("pins", []) if out.get("success") else []
print(len(pins), "个搜索 pin")

search 端点接受一个 query,返回 pins 加一个 cursor(我这次运行的搜索 run id 7298c08a-09be-471b-978a-b155a323d9e3)。这把你的集合从一个品牌的 board 扩大到某个主题下正在浮现的一切。确切参数见 search API 参考。

Pinterest search 端点的 SandBase API 参考 search 参考——传一个 query;用返回的 cursor 翻页。

串起来

def research(handle: str, query: str):
    boards_out = call("pinterest/user-boards", {"handle": handle})
    boards = boards_out.get("boards", []) if boards_out.get("success") else []

    collected = []
    for b in boards:
        if not isinstance(b, dict) or not b.get("url"):
            continue
        board_out = call("pinterest/board", {"url": b["url"]})
        collected.append({
            "board": b.get("name"),
            "pins": board_out.get("pins", []) if board_out.get("success") else [],
        })

    search_out = call("pinterest/search", {"query": query})
    return {
        "boards": collected,
        "search": search_out.get("pins", []) if search_out.get("success") else [],
    }

因为三个端点共享同一个信封,循环保持扁平:call(...) 里一次 status 检查、一次 output.success 兜底、处处同一套 .get() 模式。标识从一步流向下一步——一个 handle 产出 board,每个 board 的 url 产出 pin。

实操要点

  • 信封可能不同。 参考记录的是 outputs[0].data;在我的调用里负载常出现在一个顶层 output 对象下、带一个 success 标记。两种结构都读(优先 output,回退 outputs[0].data)并按 status 分支。
  • board 需要一个有效的 url。 从一次 user-boards 结果里取,而不是自己拼。
  • 用 cursor 翻页。 board 和 search 都返回一个 cursor;重发它取下一页。参数名对照参考核对。
  • 业务字段仅供观测。 参考保证的是信封;把 boards、pins、pin_count 之类当作观测到的、以真实响应核对。
  • 仅公开、只读数据。 不发帖、不保存、不涉及私有或仅账号可见数据。用 SandBase API 密钥鉴权。
  • 做个好客户端。 遇到 HTTP 429 等瞬时错误按退避重试;用游标翻页而不是猛打。

常见问题

我需要 Pinterest 登录或 SDK 吗? 不需要。你用自己的 SANDBASE_API_KEY 向 SandBase 鉴权。这些读取端点不需要你这边有 Pinterest 账号或 OAuth。

为什么这个面返回 output 而不是 outputs? 参考记录负载在 outputs[0].data 下;在我的调用里它常出现在一个顶层 output 对象下,含一个 success 标记、一个结果列表和一个 cursor。两种结构都读(优先 output,回退 outputs[0].data),再检查 success、防御式读列表。

我怎么拿到 board url? 从 user-boards。output.boards 里每个 board 带一个 url,直接传给 pinterest/board。

怎么翻更多 pin? board 和 search 都在 output 里返回一个 cursor。用游标重发同一个调用取下一页。确切参数名对照参考核对。

小结

三个端点、一个 output 信封、标识在步骤间流动——这就是整个视觉研究循环。完整端点目录和响应细节见 Pinterest 公开数据 API 汇总页。准备好后: