Skip to main content
Posts carry an ordered media array. Each item names where the media comes from with source_type, so one list can mix an uploaded file, a public URL, and media you uploaded to a platform yourself. Order is preserved and decides carousel sequence.
The older media_ids array still works and is not going away without notice. ["abc"] is exactly equivalent to [{ "source_type": "media_id", "source": "abc" }]. If you send both, media wins and media_ids is ignored. Responses continue to include media_ids alongside media, listing the media_id items only.
Uploading and verifying media does not cost credits. A credit is charged only when the post publishes or is scheduled.

Choosing an upload path

There are two ways to get a file into SocialAPI. Both return a media_id that you use the same way. If a file is larger than 50 MB, use the presigned-URL path.

Server-side upload

Send the file as multipart form data in a single request. The file field is required.
Response:
The returned media is immediately ready. Pass it as a media_id source on your next POST /v1/posts call (see Using media in posts).

Presigned URL upload

Use this path for large files. It has three steps: request a signed URL, upload the bytes to it, then verify. Step 1: Request a signed upload URL. Both media_type (the file’s MIME type) and filename are required.
Response:
The upload_url is valid for 15 minutes. Step 2: Upload the file with an HTTP PUT to upload_url. The Content-Type must match the media_type you declared in step 1.
Step 3: Verify the upload. This step is required. It confirms the bytes landed, records the file size, and flips the media from pending to ready.
If you skip verification, the media stays pending and publishing fails with Media not found.

Using media in posts

Pass the media array on POST /v1/posts. Order is preserved. A single entry produces an image or video; multiple entries produce a carousel, subject to each platform’s limits.
media is also accepted per target, where it overrides the post-level list for that platform only. A target that sets no media inherits the post-level one. Per-platform media limits (image versus video, carousel counts, maximum items per post) are listed in the Posts overview capability matrix.

Publishing from a public URL

Set source_type to url and we fetch the file at publish time. Nothing is stored in your media library and no storage quota is consumed.
The URL must be publicly reachable over http or https. URLs resolving to private, loopback, link-local, or otherwise internal addresses are rejected. The check runs again at publish time, so a URL that stops resolving publicly between scheduling and publishing fails the post rather than silently dropping the media. Because the fetch happens when the post publishes, the URL must still be live at that moment. For a post scheduled weeks out, an uploaded media_id is the safer choice: a link that expires or rotates in the meantime will fail the post.

Publishing a platform attachment ID

If you have already uploaded media to a platform through its own API, pass that ID directly and we publish it without re-uploading.
type is required here (image, video, audio, or file). For url and media_id we infer it from the file, but a platform attachment ID is opaque to us, so you have to declare it. Supported on Instagram, Threads, LinkedIn, LinkedIn Page, and X/Twitter. Any other platform returns 422 publishing.media.unsupported_source. Mixing rules differ by platform: On Instagram and Threads a platform_attachment_id is one finished, publishable container (a single photo or video, or a carousel you assembled yourself), so it cannot be combined with anything else. Sending more than one item returns 422 publishing.media.mixing_unsupported. To build a mixed carousel there, pass url or media_id items and we assemble it for you. These gates are per target: a post can still publish to its other targets when one target is rejected.

Managing your library

List media. Returns your ready files, newest first, with cursor pagination (default 50 per page, max 100). Each item includes a short-lived url for previewing the file.
Delete media.
Check storage usage. Returns bytes used, the plan limit, and file count. The same values come back as X-Storage-Used and X-Storage-Limit response headers on media requests (-1 means unlimited).

Storage limits

An upload that would exceed your quota returns 413. On the presigned path the object is removed and the media is marked failed.

Constraints and gotchas

  • A public URL now publishes directly as a url source. In the deprecated media_ids array a raw URL is still ignored: media_ids only accepts media library IDs.
  • url sources are fetched at publish time and must still be reachable then. Prefer an uploaded media_id for posts scheduled far ahead.
  • platform_attachment_id requires an explicit type, works only on Instagram, Threads, LinkedIn, LinkedIn Page, and X/Twitter, and cannot be mixed with other media on Instagram or Threads.
  • The verify step is mandatory on the presigned path. Without it, media stays pending and publishing fails with Media not found.
  • The server-side upload endpoint caps files at 50 MB. Use the presigned path for anything larger.
  • Media has three states: pending (created, bytes not yet confirmed), ready (usable in posts), and failed (quota exceeded, or the object was missing at verify).
  • Video duration is detected automatically at verify or upload time. You do not need to send it.
  • Per-platform format, size, and count rules are enforced when you publish. See each platform’s posts page.