API reference
Media
Two ways to attach media: public URLs the server downloads, or presigned uploads for large files.
Base URL https://api.overads.io/public/v1. Every route needs a key with the scope shown, sent as Authorization: Bearer sk_live_YOUR_KEY. The machine-readable contract is at https://api.overads.io/public/v1/openapi.json, no key needed. Scope for this family: media:write.
Which path
mediaUrlson the post: the fast path. Public https, up to 10 URLs, 15 MB per image and 25 MB per video. The server downloads the file into overads storage; the post cannot tell how the media arrived.- Presigned upload: for anything private or larger, up to 500 MB of video. Three calls: get a URL,
PUTthe bytes, complete.
Every URL is fetched through the same guard the rest of the platform uses: public addresses only, redirects re-checked, the socket pinned to the vetted address. A refusal names the host, never the full URL, because signed URLs carry tokens.
POST/media/upload-url
Scope media:write. A presigned PUT for a direct upload to overads storage.
Images up to 15 MB (image/jpeg, image/png, image/gif, image/webp); video up to 500 MB (video/mp4, video/quicktime, video/webm). The URL is valid for 15 minutes.
PUT the bytes to uploadUrl with exactly the returned headers, then call complete. For a file already on the public web skip this and pass mediaUrls on the post.
| Body | Type | Meaning | |
|---|---|---|---|
kind | enum | required | image or video. |
contentType | string | required | The Content-Type the PUT will send. |
bytes | integer | required | Exact byte length of the file the PUT will send. |
Response: { data: { media, uploadUrl, method: "PUT", headers, expiresAt }, warnings: [] }. media.status is uploading until completed.
{
"data": {
"media": { "id": "7c2e…", "url": "https://media.overads.io/social-media/…/7c2e….mp4", "kind": "video", "status": "uploading", "source": "upload", "bytes": 48213004, "width": null, "height": null, "durationMs": null, "createdAt": "…" },
"uploadUrl": "https://….amazonaws.com/…?X-Amz-Signature=…",
"method": "PUT",
"headers": { "Content-Type": "video/mp4", "Content-Length": "48213004" },
"expiresAt": "2026-09-11T09:45:00.000Z"
},
"warnings": []
}curl -X POST https://api.overads.io/public/v1/media/upload-url -H "Authorization: Bearer sk_live_YOUR_KEY" -H "Content-Type: application/json" \
-d '{ "kind": "video", "contentType": "video/mp4", "bytes": 48213004 }'POST/media/:id/complete
Scope media:write. Confirm the upload landed. The asset becomes ready and can be attached through mediaIds.
Refused with MEDIA_NOT_READY when no object is in storage yet, and with MEDIA_TOO_LARGE when the uploaded object is over the cap for its kind.
Response: { data: Media, warnings: [] }.
curl -X POST https://api.overads.io/public/v1/media/7c2e1b0a-4f3d-4e2b-9a1c-2d3e4f5a6b7c/complete -H "Authorization: Bearer sk_live_YOUR_KEY"DELETE/media/:id
Scope media:write. Delete a media asset and its stored object.
Posts that already reference the URL keep the string, but the object is gone. Delete only media no post still needs.
Response: 204, no body.
curl -X DELETE https://api.overads.io/public/v1/media/7c2e1b0a-4f3d-4e2b-9a1c-2d3e4f5a6b7c -H "Authorization: Bearer sk_live_YOUR_KEY"The upload, end to end
# 1. reserve
RES=$(curl -s -X POST https://api.overads.io/public/v1/media/upload-url -H "Authorization: Bearer sk_live_YOUR_KEY" -H "Content-Type: application/json" \
-d "{ \"kind\": \"video\", \"contentType\": \"video/mp4\", \"bytes\": $(stat -f%z reel.mp4) }")
URL=$(echo "$RES" | jq -r .data.uploadUrl)
ID=$(echo "$RES" | jq -r .data.media.id)
# 2. put the bytes with the returned headers
curl -X PUT "$URL" -H "Content-Type: video/mp4" --data-binary @reel.mp4
# 3. complete, then attach
curl -X POST https://api.overads.io/public/v1/media/$ID/complete -H "Authorization: Bearer sk_live_YOUR_KEY"
curl -X POST https://api.overads.io/public/v1/posts -H "Authorization: Bearer sk_live_YOUR_KEY" -H "Content-Type: application/json" \
-d "{ \"content\": \"New reel\", \"connectionIds\": [\"...\"], \"mediaIds\": [\"$ID\"] }"