Skip to main content
Media in SocialAPI is the images and videos you attach to a post. A post carries an ordered 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 of media 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 the file field:
Response (HTTP 201):
The file is 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 media_type and its name as filename. Both are required.
Response (HTTP 200):
The response has no public URL: the file becomes usable through its 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 ready.
Response (HTTP 200):
If the file is not at the upload URL when you verify, the media becomes 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

Pass media 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.
The formats, sizes, and counts each platform accepts are on its publishing page. GET /v1/posts/validate returns them as data. See Publish a post.

Publish from a public URL

A url source is not stored in your library and uses no storage quota.
  • Reachable: the URL must be a public http or https address when you create the post and again when the post publishes. A URL that resolves to a private, loopback, or link-local address returns 400 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 .jpg or .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.
For a post scheduled far ahead, upload the file and use a 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.
Instagram, Threads, LinkedIn, and X accept 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:
Response (HTTP 200):
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.
Last modified on October 19, 2026