Media API
The /v1/media endpoints move files into your media library: either by
URL ingest (post.buzz fetches the file server-side) or by direct
multipart upload. Uploaded media is referenced by ID when creating posts.
Requires authentication.
Ingest media from a URL
POST /api/v1/media
post.buzz fetches the URL server-side, runs the size/type gate, stores the file in object storage (R2), and queues analysis.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
url | string | yes | HTTP(S) URL of the file to ingest |
mediaType | string | no | image, video, or document |
teamId | string | no | Brand that owns the media; defaults to the workspace's first brand |
Example
curl -X POST https://<your-api-origin>/api/v1/media \
-H "Authorization: Bearer sx_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "url": "https://cdn.example.com/launch-teaser.mp4", "mediaType": "video" }'
Response — 200 OK
{
"data": {
"id": "med_01",
"mediaType": "video",
"url": "https://cdn.example.com/launch-teaser.mp4",
"status": "processing"
}
}
Errors
| Status | Code | When |
|---|---|---|
400 | validation_error | Invalid body or non-URL input |
403 | forbidden | Request not permitted |
413 | too_large | File exceeds the effective size ceiling |
502 | unreachable / server_error | Fetch failed or remote errored |
503 | server_error | Object storage unavailable |
Upload a file directly
POST /api/v1/media/upload
A multipart/form-data upload for files you have locally. The same size/type
gates apply.
| Form field | Type | Required | Description |
|---|---|---|---|
file | file | yes | The file to upload |
mediaType | string | no | image, video, or document |
teamId | string | no | Brand that owns the media; defaults to the workspace's first brand |
Example
curl -X POST https://<your-api-origin>/api/v1/media/upload \
-H "Authorization: Bearer sx_live_YOUR_API_KEY" \
-F "file=@./cover.png" \
-F "mediaType=image"
Response — 200 OK
Same media shape as URL ingest. Errors likewise,
plus 400 validation_error (Provide a \file` form field`) when the form
field is missing.
List media
GET /api/v1/media?type=image|video&teamId=<brand>&limit=24&offset=0
Query parameters
| Parameter | Type | Default | Range |
|---|---|---|---|
type | string | — | image, video |
teamId | string | — | Brand ID; omit for all brands |
limit | integer | 24 | 1–100 |
offset | integer | 0 | ≥ 0 |
Response — 200 OK
{
"data": {
"items": [
{
"id": "med_01",
"mediaType": "image",
"url": "https://...",
"status": "ready"
}
],
"total": 42
}
}
Get / delete media
GET /api/v1/media/{id}
DELETE /api/v1/media/{id}
GET returns the media record inside data; DELETE removes it and returns
{ "data": { "success": true } }. Both return 404 not_found for unknown or
out-of-organization IDs.
Upload ceilings
The absolute ceilings the API will ever accept, regardless of plan:
| Type | Absolute max |
|---|---|
| Image | 25 MB |
| Video | 100 MB |
| Document | 100 MB |
Your plan may impose a lower maxFileSize; the effective
limit is the smaller of the plan ceiling and the absolute ceiling — see
Limits. Oversized uploads return
413 { "error": { "code": "too_large", ... } }.
Don't confuse upload ceilings with publish limits. Some platforms accept larger files at publish time than the API accepts at upload time (for example, YouTube accepts multi-GB videos natively). The REST upload ceiling is 100 MB for video.
In posts
Reference uploaded media by ID in the mediaIds array when
creating a post. Order is preserved (sortOrder), which
matters for carousels and galleries.
{
"caption": "Behind the scenes",
"accountIds": ["acc_instagram"],
"mediaIds": ["med_01", "med_02", "med_03"]
}
When a post is created, each attached media asset is validated against the
destination platform's constraints for that post type — aspect ratio,
resolution, duration, bitrate, and file count. Mismatches return 400 with a
descriptive message before anything is published.
Related
- Posts API — attach
mediaIdshere. - Limits — per-platform media constraints.
- Media library — the user-facing counterpart.