Media
Images and videos for posts. Bytes wait in SquidX storage and reach X at publish time, because X media ids expire after about a day and a scheduled post can wait for weeks. A segment carries up to four images, or one GIF alone, or one video alone.
Upload an image
squidx.io/api/v1/mediaposts:writeMultipart upload of one JPEG, PNG, WebP or GIF, 4MB max. Put the returned id in a segment's media list when creating or editing a post. Up to four images per segment, or one GIF alone. Videos use the two-step upload below.
| Name | In | Type | Description |
|---|---|---|---|
filerequired | form-data | file | The image. image/jpeg, image/png, image/webp or image/gif. |
curl -X POST https://squidx.io/api/v1/media \
-H "Authorization: Bearer sqx_your_key" \
-F "file=@./launch.png"{
"data": {
"id": "3c1c9e5a-0d2b-4f6e-8a7c-1b2d3e4f5a6b/7d2e1f0a-9b8c-4d7e-a6f5-4e3d2c1b0a99.png",
"path": "3c1c9e5a-0d2b-4f6e-8a7c-1b2d3e4f5a6b/7d2e1f0a-9b8c-4d7e-a6f5-4e3d2c1b0a99.png",
"type": "photo"
}
}- Attaching media needs a paid workspace, like everything that can end up on X.
- Then: POST /posts with segments: [{ text: "...", media: [{ id: "<id>" }] }].
Upload an image from a URL or base64
squidx.io/api/v1/mediaposts:writeThe same upload with a JSON body, for agents and scripts that hold a link or the bytes rather than a file. SquidX downloads a public https URL itself, or decodes base64 data. Same types and 4MB cap.
| Name | In | Type | Description |
|---|---|---|---|
url | body | string | A public https URL of the image. Private and local addresses are refused. |
data | body | string | The image as base64 (a data: prefix is accepted). Give mimeType with it. Send url or data, not both. |
mimeType | body | string | With data: image/jpeg, image/png, image/webp or image/gif. |
curl -X POST https://squidx.io/api/v1/media \
-H "Authorization: Bearer sqx_your_key" \
-H "Content-Type: application/json" \
-d '{ "url": "https://example.com/launch.png" }'{
"data": {
"id": "3c1c9e5a-0d2b-4f6e-8a7c-1b2d3e4f5a6b/7d2e1f0a-9b8c-4d7e-a6f5-4e3d2c1b0a99.png",
"path": "3c1c9e5a-0d2b-4f6e-8a7c-1b2d3e4f5a6b/7d2e1f0a-9b8c-4d7e-a6f5-4e3d2c1b0a99.png",
"type": "photo"
}
}- The download follows up to three redirects and stops after 10 seconds. Videos are refused here: use the two-step upload below.
Start a video upload
squidx.io/api/v1/media/uploadsposts:writeStep 1 of 2 for a video. SquidX answers a signed URL you PUT the file to, straight into storage: a video is too big for a normal request. MP4 or MOV, 100MB and 140 seconds max. The URL is good for two hours.
| Name | In | Type | Description |
|---|---|---|---|
mimeTyperequired | body | string | video/mp4 or video/quicktime. |
sizerequired | body | integer | The file size in bytes. SquidX measures the real object at step 2. |
curl -X POST https://squidx.io/api/v1/media/uploads \
-H "Authorization: Bearer sqx_your_key" \
-H "Content-Type: application/json" \
-d '{ "mimeType": "video/mp4", "size": 18350212 }'{
"data": {
"path": "3c1c9e5a-0d2b-4f6e-8a7c-1b2d3e4f5a6b/2b7c1d9e-4f5a-4b6c-8d7e-9f0a1b2c3d4e.mp4",
"signedUrl": "https://<project>.supabase.co/storage/v1/object/upload/sign/post-media/3c1c9e5a-.../2b7c1d9e-....mp4?token=eyJ...",
"token": "eyJ...",
"maxBytes": 104857600,
"expiresInSeconds": 7200
}
}- Then PUT the bytes: curl -X PUT "<signedUrl>" -H "content-type: video/mp4" -H "x-upsert: false" --data-binary @clip.mp4
- Recommended encoding: MP4, H.264 video, AAC audio. X checks the codec at publish time; a refusal shows on the post as lastError media_upload.
Finish a video upload
squidx.io/api/v1/media/uploads/completeposts:writeStep 2 of 2. SquidX reads the stored object back, checks its size and type, and answers the media id. Put it in a segment's media list, alone.
| Name | In | Type | Description |
|---|---|---|---|
pathrequired | body | string | The path from step 1. |
curl -X POST https://squidx.io/api/v1/media/uploads/complete \
-H "Authorization: Bearer sqx_your_key" \
-H "Content-Type: application/json" \
-d '{ "path": "3c1c9e5a-0d2b-4f6e-8a7c-1b2d3e4f5a6b/2b7c1d9e-4f5a-4b6c-8d7e-9f0a1b2c3d4e.mp4" }'{
"data": {
"id": "3c1c9e5a-0d2b-4f6e-8a7c-1b2d3e4f5a6b/2b7c1d9e-4f5a-4b6c-8d7e-9f0a1b2c3d4e.mp4",
"path": "3c1c9e5a-0d2b-4f6e-8a7c-1b2d3e4f5a6b/2b7c1d9e-4f5a-4b6c-8d7e-9f0a1b2c3d4e.mp4",
"type": "video"
}
}- A missing object answers 404 missing (the PUT did not finish). A file that is not a video we accept answers 400 invalid and is removed.
- Then: POST /posts with segments: [{ text: "...", media: [{ id: "<id>" }] }]. A video travels alone: no image next to it.
