Files API — upload, list, get and delete
/v2/files is where your files live on our side: the pictures, clips and sounds you upload, and the results of
your jobs. Four routes, the same key as /v2/jobs, no charge for any of them. Every file has a stable link on our
CDN that you can put straight into a model's input.
- Base URL:
https://api.sociaro.com - Auth:
Authorization: Bearer <your-api-key>. - Whose files: they belong to the owner of your key, the same owner as your jobs. Keys of one team share
their files; keys that are not in a team share them across the whole organisation. A file id that belongs to
someone else gets the same
404as an id that does not exist.
Upload
POST /v2/files. The body is the file itself, as raw bytes, and Content-Type says what kind of file it is. The
name is optional, in an X-Filename header.
curl https://api.sociaro.com/v2/files \
-H "Authorization: Bearer $SOCIARO_API_KEY" \
-H "Content-Type: image/png" \
-H "X-Filename: cat.png" \
--data-binary @cat.png
{"id": "med_3f9c2b7e41a85d06b1c4e9f07a2d8c35",
"source": "upload",
"job_id": null,
"type": "image",
"content_type": "image/png",
"bytes": 482113,
"url": "https://cdn.sociaro.com/v2/files/3f9c2b7e41a85d06b1c4e9f07a2d8c35/9a1e5c0d7b3f4682a1d9e8c7b6f50342.png",
"filename": "cat.png",
"created_at": "2026-11-01T12:00:00.457268+00:00"}
The answer is 201. Every upload makes a new file, even when the bytes are the same as before, so if a response
is lost, list your files to see whether it was stored.
X-Filename is a label for you and nothing more. Send it percent-encoded UTF-8; control characters are removed and
what is left is cut to 255 characters. It is never part of the link.
What can be uploaded
The first bytes of the file have to match the type you declare. A file that does not match is refused, whatever the name says.
picture image/jpeg image/png image/webp image/gif
clip video/mp4 video/webm video/quicktime
sound audio/mpeg audio/wav audio/x-wav audio/mp4 audio/x-m4a audio/ogg audio/flac
Anything else is refused, SVG and HTML included. The limit is 100 MB, counted as 100,000,000 bytes, measured on the bytes we actually receive.
Use the link in a model
url is a public link that works with a plain GET: no key, no signature, no deadline. Put it wherever a model takes
a link to a picture, clip or sound. For example, as the first frame of a Seedance clip on POST /v2/jobs (the
envelope, the required Idempotency-Key header and the per-model input are in the Jobs API):
{"model": "bytedance/seedance-2.0",
"input": {"content": [{"type": "text", "text": "The cat stretches and walks out of the frame."},
{"type": "image_url",
"image_url": {"url": "https://cdn.sociaro.com/v2/files/3f9c2b7e41a85d06b1c4e9f07a2d8c35/9a1e5c0d7b3f4682a1d9e8c7b6f50342.png"},
"role": "first_frame"}],
"resolution": "480p", "duration": 4, "generate_audio": false}}
The path of the link cannot be guessed, but anyone who has the link can fetch the file: hand it out as you would the file itself.
List
GET /v2/files lists every file you own, newest first: your uploads and the outputs of your jobs.
| query parameter | |
|---|---|
limit |
1 to 100, default 50. |
after |
The next_cursor of the previous page. Treat it as opaque. |
source |
upload or job, to see only one of the two. |
job_id |
Only the files of that job. This is how you find the id of a job's output, to delete it. |
curl "https://api.sociaro.com/v2/files?limit=2&source=upload" \
-H "Authorization: Bearer $SOCIARO_API_KEY"
{"data": [{"id": "med_3f9c2b7e41a85d06b1c4e9f07a2d8c35", "source": "upload", "job_id": null, "type": "image",
"content_type": "image/png", "bytes": 482113,
"url": "https://cdn.sociaro.com/v2/files/3f9c2b7e41a85d06b1c4e9f07a2d8c35/9a1e5c0d7b3f4682a1d9e8c7b6f50342.png",
"filename": "cat.png", "created_at": "2026-11-01T12:00:00.457268+00:00"}],
"has_more": false,
"next_cursor": null}
While has_more is true, send the next_cursor back as after for the next page.
The fields of a file:
| field | |
|---|---|
id |
The id to get or delete the file with. |
source |
upload for a file you uploaded, job for an output of one of your jobs. |
job_id |
The job a job file belongs to, otherwise null. |
type |
image, video or audio. |
content_type |
The file's type. |
bytes |
Its size. |
url |
Its link on our CDN. For a job output it is the same link as in that job's outputs[]. |
filename |
The X-Filename you sent, or null. Always null for a job output. |
created_at |
When it was stored. |
width, height, duration_sec |
Only on a file where we recorded them, as in outputs[]. |
Get
GET /v2/files/{id} answers 200 with the same object, or 404.
Delete
DELETE /v2/files/{id} removes the file, an upload or a job output alike:
curl -X DELETE https://api.sociaro.com/v2/files/med_3f9c2b7e41a85d06b1c4e9f07a2d8c35 \
-H "Authorization: Bearer $SOCIARO_API_KEY"
{"id": "med_3f9c2b7e41a85d06b1c4e9f07a2d8c35", "deleted": true}
- The gateway never deletes a file on its own. A file stays until you delete it.
- Deleting the same id again is a
404. - After a delete, the link may still be served from the CDN cache for a while. The stored file is gone at once.
- Deleting a job output removes it from that job's
outputs[]. The job stayssucceededand what it cost does not change. - A
503can leave the file partly deleted: its link may already be gone while it is still listed. Send the sameDELETEagain until it returns200or404.
Errors
The envelope is the one every /v2 route uses: {"error": {"code": …, "message": …}}. Match on code.
| status | error.code |
meaning |
|---|---|---|
400 |
invalid_input |
The body is empty, the Content-Length is not a number, or a query parameter is not valid. |
401 |
invalid_key |
The key is missing, wrong or revoked, or it has no account behind it. |
404 |
file_not_found |
No such file. An id that is not yours looks the same. |
408 |
upload_timeout |
The body did not arrive within the time allowed. Nothing was stored. |
413 |
file_too_large |
More than 100,000,000 bytes. |
415 |
unsupported_media_type |
The Content-Type is missing or not one of the types above. |
415 |
content_type_mismatch |
The bytes are not what the Content-Type says. |
429 |
too_many_uploads |
Too many uploads are running at once. Wait the Retry-After seconds and send it again. |
503 |
unavailable |
Storage or the database is not available. On an upload: nothing was stored. On a delete: send the same DELETE again. |
Limits
- Up to 4 uploads at once for the owner of your key (a team, or the organisation for keys without a team), and 12
at once across the gateway. Beyond that the answer is
429, withRetry-After: 5, before the body is read. - An upload has 10 minutes to send its body.
- Uploading, listing, getting and deleting are free. They are not checked against your balance.