Pins API
GET /v1/pins
List Pins
List pins for the workspace, newest first by default, with optional filters.
The X-Total-Count response header carries the number of pins matching the
filters and search, so clients can page with limit and offset.
Auth: ApiKeyAuth, BearerAuth
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
limit |
query | no | integer | |
offset |
query | no | integer | |
q |
query | no | string? | Case-insensitive search across title, description and link URL |
sort |
query | no | enum(created_at_desc, created_at_asc, published_at_desc, published_at_asc, title_asc, title_desc, status_asc, status_desc) | Sort order |
account_id |
query | no | string? | Only pins for this Pinterest account |
board_id |
query | no | string? | Only pins targeting this board |
status |
query | no | PinStatus? | Only pins in this status |
error_code |
query | no | string? | Only failed pins with this error code |
removed |
query | no | boolean? | true: only published pins that no longer exist on Pinterest; false: exclude them |
since |
query | no | string? | Only pins created at or after this ISO 8601 timestamp |
until |
query | no | string? | Only pins created before this ISO 8601 timestamp |
Response 200
curl -X GET "https://api.pinbridge.io/v1/pins" \
-H "X-API-Key: $PINBRIDGE_API_KEY"
POST /v1/pins
Create Pin
Create and publish a pin.
The pin is queued for publishing and processed asynchronously.
Use idempotency_key to prevent duplicate pins.
Before the pin is accepted PinBridge checks that the connected account can
publish to the board. Failures return 422 with a stable error.code
(board_not_found, board_not_owned, board_deleted, scope_missing,
token_expired, token_revoked, board_access_denied, …) and a
remediation hint.
Every response carries X-PinBridge-Quota-* and X-Pinterest-RateLimit-*
headers describing the remaining headroom. To check a request without
publishing, use POST /v1/pins/validate.
Auth: ApiKeyAuth, BearerAuth
Request body (PinCreate)
| Field | Type | Required | Description |
|---|---|---|---|
account_id |
string | yes | Pinterest account ID to publish with |
board_id |
string | yes | Pinterest board ID |
title |
string | yes | Pin title (max 100) |
description |
string? | no | Pin description |
alt_text |
string? | no | Alternative text used for accessibility |
dominant_color |
string? | no | Dominant hex color of the pin media (for example #6E7874) |
cover_image_url |
string? | no | Optional public cover image URL for video pins |
cover_image_asset_id |
string? | no | Optional uploaded image asset ID used as a video cover image |
link_url |
string? | no | Destination URL for the pin |
image_url |
string? | no | Public URL of the image to pin |
asset_id |
string? | no | Uploaded PinBridge asset ID to use for the pin image or video |
idempotency_key |
string | yes | Idempotency key to prevent duplicate pins (max 255) |
Response 201 (PinResponse)
curl -X POST "https://api.pinbridge.io/v1/pins" \
-H "X-API-Key: $PINBRIDGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"account_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "board_id": "987654321098765432", "title": "Autumn table setting", "idempotency_key": "pin-2026-001", "image_url": "https://example.com/image.jpg"}'
POST /v1/pins/batch
Create Pins Batch
Create and publish several pins in one call.
Each entry is processed independently and reported with its own outcome
(created, existing for an idempotency-key hit, or failed with an error
envelope). Requires the bulk publishing plan feature and enough monthly quota
for every entry that would be created. Quota headers describe the workspace;
Pinterest rate headers are only included when every entry targets one account.
Auth: ApiKeyAuth, BearerAuth
Request body (PinBatchRequest)
| Field | Type | Required | Description |
|---|---|---|---|
pins |
array |
yes | Pins to create, in order |
Response 200 (PinBatchResponse)
curl -X POST "https://api.pinbridge.io/v1/pins/batch" \
-H "X-API-Key: $PINBRIDGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"pins": [{"account_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "board_id": "987654321098765432", "title": "Autumn table setting", "idempotency_key": "pin-2026-001", "image_url": "https://example.com/image.jpg"}]}'
POST /v1/pins/bulk-delete
Bulk Delete Pins
Soft-delete multiple pins in one call (or hard-delete in sandbox).
Per-item outcomes:
* succeeded — pin was deleted.
* failed (error_code=not_found) — pin not found in workspace or already deleted.
Auth: ApiKeyAuth, BearerAuth
Request body (BulkIdsRequest)
| Field | Type | Required | Description |
|---|---|---|---|
ids |
array |
yes | List of resource IDs to act on (up to 500 per request). |
Response 200 (BulkOperationResponse)
curl -X POST "https://api.pinbridge.io/v1/pins/bulk-delete" \
-H "X-API-Key: $PINBRIDGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ids": ["3fa85f64-5717-4562-b3fc-2c963f66afa6"]}'
POST /v1/pins/bulk-retry
Bulk Retry Pins
Retry multiple failed pins in one call.
Per-item outcomes:
* succeeded — pin was reset to QUEUED and publish task enqueued.
* skipped (error_code=ineligible_status) — pin status is not FAILED.
* failed (error_code=not_found) — pin not found in workspace or deleted.
Auth: ApiKeyAuth, BearerAuth
Request body (BulkIdsRequest)
| Field | Type | Required | Description |
|---|---|---|---|
ids |
array |
yes | List of resource IDs to act on (up to 500 per request). |
Response 200 (BulkOperationResponse)
curl -X POST "https://api.pinbridge.io/v1/pins/bulk-retry" \
-H "X-API-Key: $PINBRIDGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ids": ["3fa85f64-5717-4562-b3fc-2c963f66afa6"]}'
GET /v1/pins/imports
List Import Jobs
List bulk import jobs for a workspace.
Auth: ApiKeyAuth, BearerAuth
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
limit |
query | no | integer | |
offset |
query | no | integer | |
status |
query | no | ImportJobStatus? | Filter import jobs by processing status |
source_type |
query | no | ImportSourceType? | Filter import jobs by source type |
Response 200
curl -X GET "https://api.pinbridge.io/v1/pins/imports" \
-H "X-API-Key: $PINBRIDGE_API_KEY"
POST /v1/pins/imports/csv
Create Csv Import Job
Queue a bulk pin import from an uploaded CSV file.
Auth: ApiKeyAuth, BearerAuth
Request body: multipart/form-data with a file field.
Response 202 (ImportJobResponse)
curl -X POST "https://api.pinbridge.io/v1/pins/imports/csv" \
-H "X-API-Key: $PINBRIDGE_API_KEY" \
-F "file=@/path/to/file"
POST /v1/pins/imports/json
Create Json Import Job
Queue a bulk pin import from a JSON array.
Auth: ApiKeyAuth, BearerAuth
Request body (BulkPinImportJsonRequest)
See schema.
Response 202 (ImportJobResponse)
curl -X POST "https://api.pinbridge.io/v1/pins/imports/json" \
-H "X-API-Key: $PINBRIDGE_API_KEY" \
-H "Content-Type: application/json" \
-d '[{"account_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "board_id": "987654321098765432", "title": "Autumn table setting", "image_url": "https://example.com/image.jpg", "idempotency_key": "import-2026-001"}]'
GET /v1/pins/imports/{job_id}
Get Import Job
Get a bulk import job and row results.
Auth: ApiKeyAuth, BearerAuth
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
job_id |
path | yes | string |
Response 200 (ImportJobResponse)
curl -X GET "https://api.pinbridge.io/v1/pins/imports/$JOB_ID" \
-H "X-API-Key: $PINBRIDGE_API_KEY"
POST /v1/pins/validate
Validate Pin
Dry-run a pin request.
Runs the full acceptance path — account, media, cover image, board access,
idempotency, billing, quota and Pinterest rate headroom — and reports each
step. Returns the exact payload that would be published. Nothing is queued.
Auth: ApiKeyAuth, BearerAuth
Request body (PinCreate)
| Field | Type | Required | Description |
|---|---|---|---|
account_id |
string | yes | Pinterest account ID to publish with |
board_id |
string | yes | Pinterest board ID |
title |
string | yes | Pin title (max 100) |
description |
string? | no | Pin description |
alt_text |
string? | no | Alternative text used for accessibility |
dominant_color |
string? | no | Dominant hex color of the pin media (for example #6E7874) |
cover_image_url |
string? | no | Optional public cover image URL for video pins |
cover_image_asset_id |
string? | no | Optional uploaded image asset ID used as a video cover image |
link_url |
string? | no | Destination URL for the pin |
image_url |
string? | no | Public URL of the image to pin |
asset_id |
string? | no | Uploaded PinBridge asset ID to use for the pin image or video |
idempotency_key |
string | yes | Idempotency key to prevent duplicate pins (max 255) |
Response 200 (PinValidationResponse)
curl -X POST "https://api.pinbridge.io/v1/pins/validate" \
-H "X-API-Key: $PINBRIDGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"account_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "board_id": "987654321098765432", "title": "Autumn table setting", "idempotency_key": "pin-2026-001", "image_url": "https://example.com/image.jpg"}'
DELETE /v1/pins/{pin_id}
Delete Pin
Delete a pin.
By default this removes the PinBridge record only and returns 204. Pass
delete_from_pinterest=true to also delete the published pin on Pinterest,
the proportionate fix for one wrong pin (deleting the board is not); that
variant returns 200 with removed_from_pinterest so the outcome is visible.
Auth: ApiKeyAuth, BearerAuth
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
pin_id |
path | yes | string | |
delete_from_pinterest |
query | no | boolean | Also remove the published pin from Pinterest. Without this flag only the PinBridge record is deleted and the pin stays live on Pinterest. |
Response 204
curl -X DELETE "https://api.pinbridge.io/v1/pins/$PIN_ID" \
-H "X-API-Key: $PINBRIDGE_API_KEY"
GET /v1/pins/{pin_id}
Get Pin
Get pin status and details.
Auth: ApiKeyAuth, BearerAuth
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
pin_id |
path | yes | string |
Response 200 (PinResponse)
curl -X GET "https://api.pinbridge.io/v1/pins/$PIN_ID" \
-H "X-API-Key: $PINBRIDGE_API_KEY"
PATCH /v1/pins/{pin_id}
Update Pin
Edit a pin’s title, description, link, alt text, or board before it publishes.
Queued, deferred and failed pins are edited in place and publish with the new
values. A published pin cannot be edited: Pinterest’s API does not let
PinBridge change a pin once it is live, so the call returns
409 pin_already_published; delete the pin and publish a corrected one
instead. Pins currently publishing return 409 pin_publishing. Both refusals
are recorded in the activity log as pin.update_rejected. In a simulated
sandbox board_id cannot change.
Auth: ApiKeyAuth, BearerAuth
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
pin_id |
path | yes | string |
Request body (PinUpdate)
| Field | Type | Required | Description |
|---|---|---|---|
title |
string? | no | Pin title |
description |
string? | no | Pin description |
link_url |
string? | no | Destination URL for the pin |
alt_text |
string? | no | Alternative text used for accessibility |
board_id |
string? | no | Move the pin to another board |
Response 200 (PinResponse)
curl -X PATCH "https://api.pinbridge.io/v1/pins/$PIN_ID" \
-H "X-API-Key: $PINBRIDGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Autumn table setting", "alt_text": "A dinner table set with candles and autumn leaves"}'
GET /v1/pins/{pin_id}/analytics
Get Pin Analytics
Pin-level analytics (impressions, saves, clicks, outbound clicks) over a date range.
PinBridge copies every published pin’s analytics nightly, so most reads are
answered from that stored history (source: stored in the response, with
data_as_of) without calling Pinterest. Live ranges are capped at 90 days,
stored ranges at 366. Sandbox-simulated pins return zeroed metrics.
A pin deleted on Pinterest (removed_from_pinterest_at set) is answered
from the stored history. Without stored history it fails with 409, which is also what a live read returns when
pin_removed_on_pinterest
Pinterest no longer has the pin.
Auth: ApiKeyAuth, BearerAuth
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
pin_id |
path | yes | string | |
start_date |
query | no | string? | Inclusive start (YYYY-MM-DD); default 30 days ago |
end_date |
query | no | string? | Inclusive end (YYYY-MM-DD); default today |
metrics |
query | no | string? | Comma-separated Pinterest metric types, e.g. IMPRESSION,SAVE,PIN_CLICK,OUTBOUND_CLICK. Defaults to the organic engagement set. |
source |
query | no | enum(auto, stored, live) | auto (default): PinBridge’s stored history when it covers the range, else Pinterest. stored: stored history only (up to 366 days, beyond Pinterest’s 90). live: always ask Pinterest (counts against the read limit). |
include_daily |
query | no | boolean | false: return only the range totals, with an empty daily list. Use it when the totals are all you need, e.g. over 90 days. |
Response 200 (PinAnalyticsResponse)
curl -X GET "https://api.pinbridge.io/v1/pins/$PIN_ID/analytics" \
-H "X-API-Key: $PINBRIDGE_API_KEY"
POST /v1/pins/{pin_id}/retry
Retry Failed Pin
Retry a failed pin.
Re-queues a pin that previously failed for publishing.
Optionally override the board or Pinterest account to fix
the root cause of the failure before retrying. The target board is
preflighted the same way as on creation.
Auth: ApiKeyAuth, BearerAuth
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
pin_id |
path | yes | string |
Request body
See schema.
Response 200 (PinResponse)
curl -X POST "https://api.pinbridge.io/v1/pins/$PIN_ID/retry" \
-H "X-API-Key: $PINBRIDGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
