media array, and each item names where the file comes from: your media library, a public URL, or a media ID you created on the platform. Order decides carousel sequence.
Uploading, verifying, and listing media cost no credits. A credit is charged when a post is published or scheduled. Storage counts against your plan: Free has 100 MB, and every paid plan is unlimited. See Plans and usage.
Endpoints
Media sources
Each item ofmedia has a source_type and a source:
media is also accepted on a target, where it replaces the post’s list for that target. A target without media uses the post’s list.
The older media_ids array still works: ["abc"] equals [{"source_type": "media_id", "source": "abc"}]. When you send both, media wins. Responses return media_ids next to media, listing the media_id items only.
Choose an upload path
Upload with one request
Send the file as multipart form data in thefile field:
ready at once. A request without file returns 400 validation.field_required. A file over 50 MB returns 400 validation.field_invalid.
Upload with a signed URL
1
Request a signed URL
Send the file’s MIME type as Response (HTTP 200):The response has no public URL: the file becomes usable through its
media_type and its name as filename. Both are required.media_id. upload_url stops working at expires_at, 15 minutes after the request. The media is pending.2
Upload the bytes
Send the file with
PUT to upload_url. The Content-Type header must equal the media_type from step 1.3
Verify the upload
Verification is required. It confirms the file arrived, records its size, and makes the media Response (HTTP 200):
ready.failed and the request returns 404 media.not_found. If the file would take you over your storage quota, SocialAPI deletes it, the media becomes failed, and the request returns 413 billing.storage_quota.
Media has 3 statuses: pending (created, bytes not confirmed), ready (usable in posts), and failed. Only ready media publishes. A post that names media that is not ready returns 400 publishing.validation_failed, as Track post delivery describes.
Attach media to a post
Passmedia on POST /v1/posts. One item is an image or a video, and 2 or more items are a carousel, within each platform’s limits.
GET /v1/posts/validate returns them as data. See Publish a post.
Publish from a public URL
Aurl source is not stored in your library and uses no storage quota.
- Reachable: the URL must be a public
httporhttpsaddress when you create the post and again when the post publishes. A URL that resolves to a private, loopback, or link-local address returns400 validation.field_invalid. A post scheduled for later fails when the URL stops working before it publishes. - Typed: SocialAPI reads the file type from the URL. When it cannot, the request returns
422 publishing.media.untyped_url. Use a URL with an extension such as.jpgor.mp4, or upload the file. - Lookup failure: when the host cannot be looked up for a moment, the request returns
503 system.internal. Send it again.
media_id: a link that expires or changes before the post publishes fails the post.
Publish a platform attachment ID
If you uploaded media through a platform’s own API, pass its ID and SocialAPI publishes it without a second upload.type is required.
platform_attachment_id. Any other platform returns 422 publishing.media.unsupported_source for that target. A missing type returns 422 publishing.media.missing_type.
These errors apply per target: the post still publishes to its other targets. To build a mixed carousel on Instagram or Threads, send
url or media_id items.
Manage your library
GET /v1/media lists your ready files, newest first. Each item has id, filename, content_type, size_bytes, status, created_at, and a url for previewing the file. The url stops working after 2 hours. limit is 1 to 100 and defaults to 50. cursor is the next_cursor of the previous page. See Pagination.
DELETE /v1/media/{id} deletes a file and returns HTTP 204. SocialAPI does not check whether a post uses the file, so a scheduled post that uses a deleted file fails when it publishes. A file that does not exist returns 404 media.not_found.
GET /v1/media/storage returns your storage use:
limit_bytes is -1 when your plan has no limit. GET /v1/media/storage, GET /v1/media/upload-url, POST /v1/media/upload, and POST /v1/media/{id}/verify also return the X-Storage-Used and X-Storage-Limit headers. Only ready files count.
Limits
SocialAPI reads the duration of a video when the upload completes. You do not send it.
Scopes
GET /v1/media/storage needs no scope: any valid API key can call it.
Use from an AI agent
An agent uploads with the signed URL. The server-side upload has no tool. See MCP tools.
Related
- Publish a post: the
mediafield and validation - Track post delivery: media status and delivery errors
- Plans and usage: storage allowance for each plan
- Pagination: cursor lists
- Errors: every media error code