Upload Images, Video and PDFs via API
Upload and manage media files for use in posts. Every media file is stored in your workspace's media library and can be referenced by multiple posts.
Upload a file
Use POST /media/upload with a multipart form. Maximum file size is 100 MB for a direct upload. To ingest larger files (up to 1 GB), use Upload from a URL, which downloads asynchronously.
Code
Response:
Code
The media record is nested under data. Note that size is a human-readable string (e.g. "0.80 MB"), not a byte count. The compatibility block is a preflight check of the file against your connected platforms. Save data.id — you reference media on posts through the media_ids field.
status is ready for normal uploads. Large asynchronous URL ingests start as processing — poll GET /media/:id until status is ready before using the file in a post. A source that can't be fetched or validated becomes failed.
Upload from a URL
Skip the download-and-reupload dance by pointing OmniSocials at a public URL. The server fetches the file (up to 1 GB) and stores it in your media library.
Code
The response shape matches POST /media/upload. Files above ~100 MB are ingested asynchronously and come back with status: "processing".
Upload a PDF as a carousel
Upload a PDF and OmniSocials rasterizes it into one image slide per page (up to 20 pages), then returns a media ID for every slide. Pass all of the returned IDs to media_ids on a post to publish the deck as a carousel — on LinkedIn it posts as a native swipeable document; on Instagram, TikTok, Threads and Pinterest as an image carousel.
PDFs are accepted on every upload path: POST /media/upload, upload-from-url, upload-from-base64, the presigned upload-by-token flow, and media_urls on a post (where the PDF expands in place into one slide per page). The upload response carries the whole set:
Code
data mirrors the first slide for backward compatibility; use media_ids to attach the full carousel.
Inline URLs on a post
If you only need the media for a single post, skip the media library entirely. Pass URLs directly in media_urls on the post body. Note the size split: media_urls on a post caps at 100 MB per file, while upload-from-url handles up to 1 GB — for big videos, upload first and pass the returned media id.
Code
The API downloads, validates, and attaches the file automatically. The maximum number of items is platform-dependent (Instagram/Threads up to 10, TikTok up to 35, X/Bluesky/Mastodon up to 4). Use the media library (via upload or upload-from-url) when the same file appears on multiple posts, to save on download bandwidth.
Any entry — in media_urls or media_ids — can be an object instead of a bare string to carry an accessibility description (alt text, max 1500 characters): { "url": "https://example.com/bike.jpg", "alt": "A red bicycle leaning against a brick wall" } (or { "id": "123", "alt": "..." } for library media). See Attaching media for which platforms receive it.
Supported formats
| Media type | Formats |
|---|---|
| Images | JPEG, PNG, GIF, WebP, HEIC/HEIF (converted to JPEG on upload) |
| Videos | MP4, MOV, AVI |
| Documents | PDF (rasterized into image slides — see above) |
Platform-specific size, aspect ratio, and duration requirements are enforced at publish time, not upload time. A video can upload successfully and still fail to publish to Instagram if it exceeds Instagram's length limit. See the Platforms section for per-platform media specs.
Managing media
| Endpoint | Description |
|---|---|
GET /media | List everything in the library |
GET /media/:id | Fetch a single media record |
PATCH /media/:id | Rename a media file or move it to a folder |
DELETE /media/:id | Delete from the library |
POST /media/check | Preflight-check a file's compatibility with your connected platforms |
POST /media/upload-from-base64 | Upload a base64-encoded file |
Reading media (GET /media, GET /media/:id) requires the media:read scope; all writes require media:write.
Deleting a media file that is still attached to a scheduled or in-progress post returns 409 media_in_use. Deleting one that is only on already-published posts succeeds and does not remove it from those live posts.