Media
Upload photos and videos up to 1 GB and get a URL to publish with.
You can pass any public https link as video_url, photo_url or media[]; Google Drive, Dropbox and OneDrive share links are turned into direct file links. Or upload the file to PostWire: the bytes go straight from your client to storage, never through the API. Uploaded media is deleted after 30 days.
POST /api/media/upload-url: Get a signed URL to upload a photo or video (up to 1 GB). Files over 50 MB: send "multipart": true, PUT bytes [start, end) to each returned part's upload_url, then GET /api/media/finalize?path= returns one media_url for the whole file. Media passed by URL is fetched by us: up to 1GB for TikTok and YouTube (streamed), 80MB elsewhere.GET /api/media/finalize: Confirm the upload landed and get the media_url to publish withPOST /api/media/upload-link: A link to upload a file from another device (a phone), valid 24 hours
Get a signed URL to upload a photo or video (up to 1 GB). Files over 50 MB: send "multipart": true, PUT bytes [start, end) to each returned part's upload_url, then GET /api/media/finalize?path= returns one media_url for the whole file. Media passed by URL is fetched by us: up to 1GB for TikTok and YouTube (streamed), 80MB elsewhere.
/api/media/upload-urlUpload your own file instead of hosting it yourself. The bytes go straight from your client to storage — they never pass through this API. 1) POST here. 2) PUT the raw file bytes to upload_url. 3) GET /api/media/finalize?path=<path> to receive media_url. 4) pass media_url as video_url or photo_url to /api/post. Media is deleted after 30 days.
Needs an API key: Authorization: Bearer pw_live_….
Body (JSON)
| Field | Type | Description |
|---|---|---|
content_type required | string | One of: image/jpeg, image/png, image/webp, image/gif, video/mp4, video/quicktime, video/webm. |
size_bytes | integer | |
multipart | boolean | true for files over 50 MB: the answer lists parts, each with its own upload_url and byte range [start, end). |
Responses
| Status | Means |
|---|---|
200 | upload_url + path |
413 | code use_multipart (send multipart: true) or too_large (over 1 GB) |
curl -X POST "https://postwire.io/api/media/upload-url" \
-H "Authorization: Bearer $POSTWIRE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content_type": "video/mp4",
"size_bytes": 48213504
}'const res = await fetch("https://postwire.io/api/media/upload-url", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.POSTWIRE_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
"content_type": "video/mp4",
"size_bytes": 48213504
}),
});
console.log(res.status, await res.json());import os, requests
r = requests.post(
"https://postwire.io/api/media/upload-url",
headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
json={
"content_type": "video/mp4",
"size_bytes": 48213504,
},
)
print(r.status_code, r.json())Example response
{
"path": "<account_id>/2f9c….mp4",
"upload_url": "https://…supabase.co/storage/v1/object/upload/sign/pw-media/…",
"token": "…",
"content_type": "video/mp4",
"max_bytes": 52428800,
"retention_days": 30,
"how": "1) PUT the raw file bytes to upload_url. 2) GET /api/media/finalize?path=<path> …"
}Then PUT the raw bytes to upload_url with the same Content-Type, and call GET /api/media/finalize?path= with the returned path.
Confirm the upload landed and get the media_url to publish with
/api/media/finalizeNeeds an API key: Authorization: Bearer pw_live_….
Parameters
| Field | Type | Description |
|---|---|---|
path required | query, string |
Responses
| Status | Means |
|---|---|
200 | media_url + bytes |
404 | not uploaded yet |
curl "https://postwire.io/api/media/finalize?path=%3Caccount_id%3E%2F2f9c%E2%80%A6.mp4" \
-H "Authorization: Bearer $POSTWIRE_API_KEY"const res = await fetch("https://postwire.io/api/media/finalize?path=%3Caccount_id%3E%2F2f9c%E2%80%A6.mp4", {
method: "GET",
headers: { Authorization: `Bearer ${process.env.POSTWIRE_API_KEY}` },
});
console.log(res.status, await res.json());import os, requests
r = requests.get(
"https://postwire.io/api/media/finalize?path=%3Caccount_id%3E%2F2f9c%E2%80%A6.mp4",
headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
)
print(r.status_code, r.json())Example response
{
"ok": true,
"path": "<account_id>/2f9c….mp4",
"media_url": "https://…/pw-media/<account_id>/2f9c….mp4?token=…",
"bytes": 48213504,
"content_type": "video/mp4",
"expires_at": "2026-10-04T12:00:00.000Z"
}A link to upload a file from another device (a phone), valid 24 hours
/api/media/upload-linkFor an assistant or a flow that cannot send the file itself: open the url on the device that has the video, upload, and GET /api/media/uploads/{upload_id} (or the dashboard) returns its media_url. 30 links an hour.
Needs an API key: Authorization: Bearer pw_live_….
Responses
| Status | Means |
|---|---|
200 | { url, upload_id, expires_in: 86400, max_bytes, accepts[] } |
curl -X POST "https://postwire.io/api/media/upload-link" \
-H "Authorization: Bearer $POSTWIRE_API_KEY"const res = await fetch("https://postwire.io/api/media/upload-link", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.POSTWIRE_API_KEY}` },
});
console.log(res.status, await res.json());import os, requests
r = requests.post(
"https://postwire.io/api/media/upload-link",
headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
)
print(r.status_code, r.json())