Back to Blog
TimeToPostAPIpresigned URLsmedia uploadsocial schedulingautomation

How to upload images and video to a social scheduling API with presigned URLs

T
TimeToPost Team

TL;DR: To upload images or video to TimeToPost, first request a presigned upload URL with `POST /api/media/presign`, then `PUT` the file to the returned `uploadUrl`, and finally pass the returned `publicUrl` into `POST /api/posts` as `mediaUrls`. The API validates file type and size before issuing a URL, and the file never proxies through TimeToPost.

Request a presigned upload URL from TimeToPost

TimeToPost uses a direct-to-storage upload flow, which is good news for everyone who enjoys fewer weird middlemen. According to the API reference, media is uploaded directly to storage with a short-lived presigned URL, the file never proxies through the API, and the flow is request URL first, upload file second, then reuse the returned publicUrl in a post.

To start, call POST /api/media/presign with a valid bearer token and the file details TimeToPost needs up front: filename, contentType, and size. The API validates type and size before it issues a URL.

curl -X POST https://timetopost.co/api/media/presign \
  -H "Authorization: Bearer ttp_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "launch.jpg",
    "contentType": "image/jpeg",
    "size": 482133
  }'

The response includes an uploadUrl and a publicUrl. Keep both handy, because one is for upload and the other is what you attach to the post.

Upload the file to the presigned URL

Next, send the file straight to the uploadUrl with PUT and the matching Content-Type.

curl -X PUT "<uploadUrl>" \
  -H "Content-Type: image/jpeg" \
  --data-binary @launch.jpg

That is the whole upload step. The file goes to storage, not through the API, which is exactly why this pattern exists. Less traffic, fewer headaches, less drama.

Attach the returned public URL when creating the post

Once the upload works, create the post with POST /api/posts and slip the publicUrl into mediaUrls.

You can target specific accounts with accountIds or whole platforms with platforms. Add your content, and toss in scheduledAt if you want it to go live later. The API reference is clear that scheduledAt must be a valid ISO 8601 timestamp in the future. Leave it out and you get a draft instead.

curl -X POST https://timetopost.co/api/posts \
  -H "Authorization: Bearer ttp_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Launch day! 🚀",
    "accountIds": ["cmb8x2k4p0001..."],
    "mediaUrls": ["https://media.timetopost.co/uploads/launch.jpg"],
    "scheduledAt": "2026-07-01T15:00:00.000Z"
  }'

Worked example: upload one image, then schedule a post

Here is a quick end-to-end walkthrough using a different filename and caption than the docs.

1) Request the presigned upload URL

Say you are posting a PNG called product-sneak-peek.png.

curl -X POST https://timetopost.co/api/media/presign \
  -H "Authorization: Bearer ttp_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "product-sneak-peek.png",
    "contentType": "image/png",
    "size": 614820
  }'

Hypothetical response:

{
  "success": true,
  "key": "uploads/product-sneak-peek.png",
  "uploadUrl": "https://storage.example.com/upload-target",
  "publicUrl": "https://media.timetopost.co/uploads/product-sneak-peek.png",
  "expiresIn": 300
}

2) Upload the file with PUT

curl -X PUT "https://storage.example.com/upload-target" \
  -H "Content-Type: image/png" \
  --data-binary @product-sneak-peek.png

3) Create the scheduled post

Now reuse the returned publicUrl.

curl -X POST https://timetopost.co/api/posts \
  -H "Authorization: Bearer ttp_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Tiny preview, big mood.",
    "platforms": ["linkedin"],
    "mediaUrls": ["https://media.timetopost.co/uploads/product-sneak-peek.png"],
    "scheduledAt": "2026-07-02T09:30:00.000Z"
  }'

Result: the image is uploaded once, then the post references the public media URL. Clean, boring, effective. The best kind of API workflow.

Add a video the same way, with type and size matching the file

Video follows the exact same presign dance. Send filename, contentType, and size to get the URL, then drop the returned publicUrl into mediaUrls when you create the post.

Do not get creative with the metadata. The contentType and size must match the actual file, because the API validates both before it issues the upload URL.

curl -X POST https://timetopost.co/api/media/presign \
  -H "Authorization: Bearer ttp_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "demo-cut.mp4",
    "contentType": "video/mp4",
    "size": 18400211
  }'

Then PUT the file to the uploadUrl and attach the publicUrl in POST /api/posts exactly like you would for an image.

What to check if the upload is rejected

The documented boundary is pretty clear, which is refreshing.

If the presign request is rejected, check these things first:

  • You sent a valid bearer token on the request.
  • filename, contentType, and size are present.
  • The file type and size pass validation before a URL is issued.

If the API still fails, it returns conventional HTTP status codes. The docs call out 400 for validation errors, 401 for a missing or invalid token, 403 for plan or permission limits, and 404 for not found, including posts owned by other organizations.

How this fits a scheduler migration checklist

If you are moving a posting workflow into TimeToPost, do the media part before you shut the old thing off. Preserve or export your media URLs first, because you will want those public links ready when you recreate posts in TimeToPost through the API.

Then rebuild each post with POST /api/posts, using the stored mediaUrls, your chosen accountIds or platforms, and a future scheduledAt where needed. If an agent is handling the work, the MCP overview is the right companion page, since it explains how the same backend can be driven from an MCP-compatible client instead of raw HTTP. For broader migration planning, the use cases page is a useful map of where TimeToPost fits, without making you play archaeology with old drafts.

Put these strategies into action

TimeToPost helps you schedule content, track performance, and grow your audience, all in one place.

No card for your first post. TimeToPost posts it at your best time.