Upload media (images and videos)
Host your pin media on PinBridge, then publish pins that reference it by asset_id. This is the alternative to pointing a pin at a public image_url, and it’s the only way to publish a video pin.
Prerequisites
- A paid plan. Uploads are a paid feature: the Playground (Free) plan cannot upload assets (you’ll get a
403). On Free, publish with a publicimage_urlinstead (see Publish pins). - An API key, sent in the
X-API-Keyheader. See Authentication. - Your files must be one of the supported formats below.
When to upload vs. use a public URL
You have two ways to give a pin its media:
- Public
image_url— PinBridge fetches the image from a URL you host. Works on every plan, images only. - Uploaded
asset_id— you upload the file to PinBridge first, then reference the returned asset. Required for video pins, and useful when you don’t have a public URL. Paid plans only.
Assets have no expiry. They persist until you delete them.
Supported formats
| Media | Content types |
|---|---|
| Image | image/gif, image/jpeg, image/png, image/webp |
| Video | video/mp4, video/quicktime (.mov) |
Any other content type is rejected with 400 Unsupported image content type (or ... video content type).
Size limits
Per-file limits are set by your plan:
| Plan | Image per file | Video per file |
|---|---|---|
| Playground (Free) | Uploads blocked | Uploads blocked |
| Starter | 10 MiB | 250 MiB |
| Growth | 50 MiB | 500 MiB |
| Agency | 100 MiB | 1 GiB |
| Enterprise | 100 MiB | 1 GiB |
Files over the limit return 400 file_too_large.
Your organization also has a total disk quota across all uploaded assets:
| Plan | Storage quota |
|---|---|
| Playground (Free) | 0 (uploads blocked) |
| Starter | 2 GiB |
| Growth | 7 GiB |
| Agency | 20 GiB |
| Enterprise | 20 GiB |
When an upload would exceed the org quota, it returns 402 storage_quota_exceeded. Delete unused assets or upgrade to free space.
Upload an image
POST /v1/assets/images is a multipart/form-data request with a single file field.
curl
curl -X POST https://api.pinbridge.io/v1/assets/images \
-H "X-API-Key: $PINBRIDGE_API_KEY" \
-F "file=@/path/to/pin.png;type=image/png"
Response (201, trimmed):
{
"id": "e3a7c5f9-1b4d-4c8e-a2f6-7d9b3e5a1c80",
"asset_type": "image",
"original_filename": "pin.png",
"content_type": "image/png",
"file_size_bytes": 148213,
"file_size_display": "144.7 KiB",
"referenced_by_pin_count": 0,
"public_url": "https://api.pinbridge.io/v1/assets/e3a7c5f9-1b4d-4c8e-a2f6-7d9b3e5a1c80/content"
}
Keep the id. That’s the asset_id you’ll pass when publishing.
Python SDK
from pinbridge_sdk import PinbridgeClient
with PinbridgeClient(api_key="$PINBRIDGE_API_KEY") as client:
asset = client.assets.upload_image("/path/to/pin.png")
print(asset.id, asset.asset_type, asset.public_url)
upload_image also accepts raw bytes or a file object; pass filename= / content_type= if you do. The SDK does not read environment variables, so pass api_key= explicitly.
n8n
Use the PinBridge node with Resource: Asset, Operation: Upload Image. The file comes from the item’s binary data: set Binary Property to the property that holds the file (default data). Chain it after a node that produces binary data (for example an HTTP Request or Read Binary File node).
Upload a video
Same shape, at POST /v1/assets/videos.
curl -X POST https://api.pinbridge.io/v1/assets/videos \
-H "X-API-Key: $PINBRIDGE_API_KEY" \
-F "file=@/path/to/clip.mp4;type=video/mp4"
The response is the same AssetResponse shape, with "asset_type": "video". In n8n, use Operation: Upload Video; in the SDK, client.assets.upload_video(...).
Publish a pin from an asset
Reference the asset by asset_id when you create the pin. Provide exactly one of image_url or asset_id, never both.
curl -X POST https://api.pinbridge.io/v1/pins \
-H "X-API-Key: $PINBRIDGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"account_id": "6f1c2e4a-3b7d-4c9e-8a21-5d0f9b3e7c41",
"board_id": "987654321098765432",
"title": "My pin",
"asset_id": "e3a7c5f9-1b4d-4c8e-a2f6-7d9b3e5a1c80",
"idempotency_key": "upload-demo-001"
}'
The pin’s media type is derived from the asset: an image asset produces an image pin, a video asset produces a video pin. There is no separate video_url field. For the full list of pin fields, see Publish pins.
Video cover images
The cover image for a video pin is a field on the pin, not on the asset. On PinCreate (and ScheduleCreate), set one of:
cover_image_url— a public image URL, orcover_image_asset_id— an uploaded image asset’s id.
Rules:
- Cover fields are only accepted for video pins. Setting a cover on an image pin returns
400 cover_image_url is only supported for video pins. cover_image_asset_idmust reference an image asset (not a video), else400.- Provide at most one of the two cover fields, else
422.
Verify it worked
- The upload response returns
201with the assetidandpublic_url. - Fetch the asset later with
GET /v1/assets/{asset_id}(SDK:client.assets.get(asset_id)). - After you publish,
referenced_by_pin_countreflects how many pins use the asset.
Common errors
| Status | Code / message | Cause |
|---|---|---|
| 403 | feature_unavailable |
Uploads are not available on the Free plan. Use a public image_url, or upgrade. |
| 400 | Unsupported image content type / ... video content type |
File type is not in the supported list. |
| 400 | file_too_large |
File exceeds your plan’s per-file limit. |
| 400 | Uploaded file is empty |
The file part had no bytes. |
| 402 | storage_quota_exceeded |
Org disk quota is full. Delete assets or upgrade. |
Next steps
- Publish pins — all pin fields and media options.
- Schedule pins — publish an asset-backed pin at a future time.
- Rate limits and quotas — how storage and publishing quotas work per plan.
- Docs home — quickstart and the rest of the API.
