PublishQPublishQ

Media uploads

Upload images and videos to PublishQ and attach them to your posts. Send the file in one request, or straight to storage for files up to 4 GB.

Using the web app, SDK, CLI or MCP server? You can skip this page 🎉

Uploads of any file up to 4 GB are handled for you: publishq.media.upload({ file }) (SDK), pq media upload --file video.mp4 (CLI) and publishq_upload_media (MCP) pick the right way on their own. This page is for direct API calls.

If you call the API directly, this is how uploads work.

Every image or video you attach to a post is uploaded first. The upload gives you a media id, and you put that id in mediaIds when you create or update a post.

There are two ways to upload, and the trade-off is simplicity against file size:

Upload mediaCreate media upload
Calls13, plus 1 optional
File sizeUp to 100 MBUp to 4 GB (500 MB on the Free plan)

If your files never go over 100 MB, use the one-call upload. If they can, use the storage upload: it takes files of any size up to 4 GB, small ones included. An AI agent writing the integration for you can go straight for the storage upload, since the extra steps cost it nothing.

Both accept the same file types (JPEG, PNG, GIF, WebP, MP4, WebM, MOV and AVI) and give you a media id that works the same way in posts.

Upload in one request (up to 100 MB)

Send the file as multipart/form-data under the field name file:

curl -X POST https://publishq.com/api/v1/media \
  -H "Authorization: Bearer pq_live_..." \
  -F "[email protected]"

You get back the media object:

{
  "id": "f6370ca1-1f47-4f68-be72-c1206913b326",
  "type": "IMAGE",
  "url": "https://cdn.publishq.com/uploads/images/f6370ca1-1f47-4f68-be72-c1206913b326.jpg",
  "source": "UPLOAD",
  "created_at": "2026-10-21T14:25:41.000Z",
  "usageCount": 0
}

Put its id in mediaIds when you create or update a post.

Upload straight to storage (up to 4 GB)

Create the upload

curl -X POST https://publishq.com/api/v1/media/uploads \
  -H "Authorization: Bearer pq_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "launch-video.mp4",
    "contentType": "video/mp4",
    "fileSize": 262144000
  }'

fileSize is the exact size of the file in bytes. A file over your plan's limit (4 GB, or 500 MB on the Free plan), or one that does not fit in your storage, is refused here, before you upload anything.

You get back the media id and a signed uploadUrl:

{
  "id": "f6370ca1-1f47-4f68-be72-c1206913b326",
  "uploadUrl": "https://s3.us-east-1.amazonaws.com/...?X-Amz-Signature=...",
  "expiresAt": "2026-10-21T15:36:39.000Z"
}

Send the file to storage

PUT the file to uploadUrl with the same Content-Type you sent in step 1. This request goes to storage, not to the PublishQ API, so it carries no Authorization header.

curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: video/mp4" \
  --upload-file launch-video.mp4

Storage answers 200 with an empty body, or 403 if the file is not exactly fileSize bytes. The uploadUrl expires 1 hour after step 1, so start the upload before then.

Complete the upload (optional)

curl -X POST https://publishq.com/api/v1/media/uploads/f6370ca1-1f47-4f68-be72-c1206913b326/complete \
  -H "Authorization: Bearer pq_live_..."

This checks the file, adds it to your media library now and returns the media object, the same one the one-call upload returns. Skip it if you go straight to step 4: using the id in a post does the same. If the file has not finished uploading, you get 409 media_upload_incomplete: wait for step 2 to finish and retry.

Attach the media to a post

Put the id in mediaIds:

curl -X POST https://publishq.com/api/v1/posts \
  -H "Authorization: Bearer pq_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Our launch video",
    "mediaIds": ["f6370ca1-1f47-4f68-be72-c1206913b326"],
    "accounts": [{ "accountId": "acc_..." }],
    "scheduledAt": "2026-11-01T12:00:00Z"
  }'

Complete or use the upload within 1 day

An uploaded file sits in temporary storage until you complete it (step 3) or use its id in a post (step 4). Files left there for 1 day are deleted, and their id stops working.

Platform limits

Each platform has its own limits on file size, video length and format. PublishQ checks them against the real file when you create or update the post, so a file that is too big for one of the selected platforms is refused right away with 400 platform_validation_failed. The limits for each platform are on its page under Platforms.

On this page