Bucket objects API endpoints
These endpoints work with the files (objects) inside a Cloider bucket: list a folder, upload and download files through short lived presigned URLs, create folders, delete files and folders, and, on a versioned bucket, list versions, restore deleted files and old versions, and delete versions for good. They are what the dashboard's Files tab and cloider bucket ls, upload, download and rm use.
For heavy transfers, many files or files over 5 GB, use an S3 tool with an access key instead: see S3 tools. All paths are relative to https://cloider.com/api.
At a glance
| Method | Path | Access |
|---|---|---|
| GET | /buckets/:hashed_id/objects | Read |
| POST | /buckets/:hashed_id/objects/upload | Write |
| GET | /buckets/:hashed_id/objects/download | Read |
| POST | /buckets/:hashed_id/folders | Write |
| POST | /buckets/:hashed_id/objects/delete | Write |
| GET | /buckets/:hashed_id/objects/versions | Read |
| GET | /buckets/:hashed_id/objects/deleted | Read |
| POST | /buckets/:hashed_id/objects/restore | Write |
| POST | /buckets/:hashed_id/objects/versions/delete | Write |
Keys, folders and prefixes
S3 has no real folders. Every object has a key, its full path such as images/logo.png, and a "folder" is all the keys that share a prefix ending in /, such as images/. These endpoints follow that model:
- A key is 1 to 1024 characters, doesn't start with
/and doesn't contain//. - A prefix is empty for the bucket's root, or a valid key that ends with
/. - An empty folder is a zero byte object whose key is the folder's prefix. Create a folder makes one.
Every endpoint here needs the bucket to be Ready. Before that, they answer 409 with The bucket isn't ready!. A bucket whose file browser can't be used answers 503 with The file browser isn't available for this bucket!.
List a folder
GET /buckets/:hashed_id/objects
Lists the subfolders and files directly inside one folder, 200 entries per page.
Access: any token.
| Query parameter | Required | Description |
|---|---|---|
prefix | No | The folder, for example images/. Empty or left out for the bucket's root |
token | No | The next_token of the previous page, to read the next one |
curl -G https://cloider.com/api/buckets/b7r2x/objects \
-H "Authorization: Bearer $CLOIDER_TOKEN" \
--data-urlencode "prefix=images/"
{
"prefix": "images/",
"folders": [
{"prefix": "images/icons/", "name": "icons"}
],
"objects": [
{"key": "images/logo.png", "name": "logo.png", "size": 18342, "last_modified": "2026-10-18T13:02:51Z"},
{"key": "images/hero.jpg", "name": "hero.jpg", "size": 412880, "last_modified": "2026-10-19T08:47:12Z"}
],
"next_token": ""
}
| Field | Description |
|---|---|
prefix | The folder listed |
folders | Subfolders, each with its full prefix and its name inside this folder |
objects | Files, each with its full key, its name inside this folder, its size in bytes and last_modified |
next_token | Pass it as token to get the next page. Empty on the last page |
Read every page of a large folder:
TOKEN=""
while :; do
PAGE=$(curl -s -G https://cloider.com/api/buckets/b7r2x/objects \
-H "Authorization: Bearer $CLOIDER_TOKEN" \
--data-urlencode "prefix=images/" --data-urlencode "token=$TOKEN")
jq -r '.objects[].key' <<<"$PAGE"
TOKEN=$(jq -r .next_token <<<"$PAGE")
[ -z "$TOKEN" ] && break
done
Errors: 400 Invalid folder! for a malformed prefix, 404 Bucket not found!, 502 Couldn't list the files, try again in a moment!.
Upload a file
POST /buckets/:hashed_id/objects/upload
Uploading is two steps. First you ask the API for a presigned URL for one file, giving its key, size and content type. Then you PUT the file's bytes to that URL, straight to the storage service. The bytes never pass through the API.
Access: Read & Write token.
Step 1: get a presigned URL
| Field | Type | Required | Description |
|---|---|---|---|
key | string | Yes | The file's full key, for example images/logo.png. Can't end with / |
size | integer | Yes | The file's exact size in bytes, at most 5 GB (5368709120) |
content_type | string | No | The file's MIME type, for example image/png. Default application/octet-stream |
FILE=logo.png
URL=$(curl -s -X POST https://cloider.com/api/buckets/b7r2x/objects/upload \
-H "Authorization: Bearer $CLOIDER_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"key\": \"images/$FILE\", \"size\": $(wc -c < $FILE), \"content_type\": \"image/png\"}" \
| jq -r .url)
{"url": "https://s3.eu-central-2.cloider.com/acme-assets/images/logo.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=3600&..."}
Step 2: PUT the bytes
The URL is signed for that exact size and content type, so send exactly those. Don't send the Authorization header to the storage URL: the signature in the URL is the authorization.
curl -X PUT "$URL" \
-H "Content-Type: image/png" \
--data-binary @"$FILE"
A 200 from the storage service means the file is stored. The URL is valid for one hour and for this one file. An existing file with the same key is replaced (on a versioned bucket, the old one becomes a previous version).
Upload in JavaScript
import { readFile, stat } from 'node:fs/promises';
const path = 'logo.png';
const key = 'images/logo.png';
const type = 'image/png';
const { size } = await stat(path);
const res = await fetch('https://cloider.com/api/buckets/b7r2x/objects/upload', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CLOIDER_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ key, size, content_type: type }),
});
if (!res.ok) throw new Error(JSON.stringify((await res.json()).errors));
const { url } = await res.json();
const put = await fetch(url, {
method: 'PUT',
headers: { 'Content-Type': type },
body: await readFile(path),
});
if (!put.ok) throw new Error(`upload failed: ${put.status} ${await put.text()}`);
| Status | Message |
|---|---|
400 | Invalid file name! |
400 | Files over 5 GB can't be uploaded from the dashboard, use an S3 client! |
403 | This API token is read only!, You have read-only access to this account, you can not change files! |
502 | Couldn't start the upload, try again in a moment! |
Download a file
GET /buckets/:hashed_id/objects/download
Returns a presigned URL that downloads one file, or one version of it. Fetch the URL to get the bytes; it is valid for one hour and makes the browser save the file under its name.
Access: any token, including read only tokens.
| Query parameter | Required | Description |
|---|---|---|
key | Yes | The file's full key |
version_id | No | A version from the versions list, to download that version instead of the current one |
URL=$(curl -s -G https://cloider.com/api/buckets/b7r2x/objects/download \
-H "Authorization: Bearer $CLOIDER_TOKEN" \
--data-urlencode "key=images/logo.png" | jq -r .url)
curl -o logo.png "$URL"
{"url": "https://s3.eu-central-2.cloider.com/acme-assets/images/logo.png?response-content-disposition=attachment...&X-Amz-Signature=..."}
On a public bucket you can also download a file without any API call, from <bucket url>/<key>, for example https://acme-assets.s3.eu-central-2.cloider.com/images/logo.png.
Errors: 400 Invalid file!, 404 Bucket not found!, 502 Couldn't download the file, try again in a moment!.
Create a folder
POST /buckets/:hashed_id/folders
Creates an empty folder, a zero byte object named like the folder, so it shows in listings before any file is in it. You don't need it to upload into a folder: uploading reports/2026/q3.pdf makes the folders appear by itself.
Access: Read & Write token.
| Field | Type | Required | Description |
|---|---|---|---|
prefix | string | Yes | The folder's full path, ending with /, for example reports/2026/ |
curl -X POST https://cloider.com/api/buckets/b7r2x/folders \
-H "Authorization: Bearer $CLOIDER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"prefix": "reports/2026/"}'
{"success": true}
A malformed prefix answers 400 with Invalid folder name on prefix.
Delete files and folders
POST /buckets/:hashed_id/objects/delete
Deletes files by key, and folders with everything under them. On a versioned bucket a delete keeps the previous version, so the file can be restored.
Access: Read & Write token.
| Field | Type | Required | Description |
|---|---|---|---|
keys | string list | One of the two | Files to delete, by full key |
prefixes | string list | One of the two | Folders to delete, each ending with /, with every file under them. Can't be empty (the whole bucket) |
Together, keys and prefixes can name up to 1000 entries. A folder can hold at most 10000 files to be deleted this way.
curl -X POST https://cloider.com/api/buckets/b7r2x/objects/delete \
-H "Authorization: Bearer $CLOIDER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"keys": ["images/old-logo.png"], "prefixes": ["tmp/"]}'
{"failed": []}
failed lists the keys that couldn't be deleted. An empty list means everything was deleted.
| Status | Message |
|---|---|
400 | Nothing to delete!, Too many files at once, select fewer!, Invalid file!, Invalid folder! |
400 | The folder "tmp/" holds over 10000 files, delete it with an S3 client! |
403 | This API token is read only!, You have read-only access to this account, you can not change files! |
502 | Couldn't delete the files, try again in a moment! |
List a file's versions
GET /buckets/:hashed_id/objects/versions
Lists the versions of one file on a versioned bucket, newest first, up to the latest 1000. Deletions show as delete markers.
Access: any token.
| Query parameter | Required | Description |
|---|---|---|
key | Yes | The file's full key |
curl -G https://cloider.com/api/buckets/k5t1v/objects/versions \
-H "Authorization: Bearer $CLOIDER_TOKEN" \
--data-urlencode "key=db/orders.sql.gz"
{
"versions": [
{"version_id": "0000019a2c1f8e4b", "size": 0, "last_modified": "2026-10-20T07:11:02Z", "is_latest": true, "delete_marker": true},
{"version_id": "0000019a27d03c18", "size": 52428800, "last_modified": "2026-10-19T02:30:44Z", "is_latest": false, "delete_marker": false},
{"version_id": "0000019a22a9f001", "size": 51380224, "last_modified": "2026-10-18T02:30:39Z", "is_latest": false, "delete_marker": false}
],
"truncated": false
}
| Field | Description |
|---|---|
version_id | The version's id |
size | Size in bytes. 0 for a delete marker |
last_modified | When the version was written, or the file deleted |
is_latest | The current version. When it is a delete marker, the file is deleted |
delete_marker | true for a deletion |
truncated | true when the file has more than 1000 versions and the older ones aren't listed |
List deleted files
GET /buckets/:hashed_id/objects/deleted
Lists the files of one folder that were deleted on a versioned bucket and can still be restored, with subfolders that contain such files.
Access: any token.
| Query parameter | Required | Description |
|---|---|---|
prefix | No | The folder. Empty for the root |
token | No | The previous page's next_token |
curl -G https://cloider.com/api/buckets/k5t1v/objects/deleted \
-H "Authorization: Bearer $CLOIDER_TOKEN" \
--data-urlencode "prefix=db/"
{
"prefix": "db/",
"folders": [],
"objects": [
{
"key": "db/orders.sql.gz",
"name": "orders.sql.gz",
"deleted_at": "2026-10-20T07:11:02Z",
"marker_version_id": "0000019a2c1f8e4b"
}
],
"next_token": ""
}
marker_version_id is the delete marker's version. Restoring that version undeletes the file.
Restore a deleted file or an old version
POST /buckets/:hashed_id/objects/restore
On a versioned bucket, makes a file current again:
- Given the latest delete marker of a deleted file, it undeletes the file by removing the marker.
- Given an older version, it copies that version on top, so it becomes the current version. The versions in between are kept.
Access: Read & Write token.
| Field | Type | Required | Description |
|---|---|---|---|
key | string | Yes | The file's full key |
version_id | string | Yes | The version to restore, or the delete marker to remove |
curl -X POST https://cloider.com/api/buckets/k5t1v/objects/restore \
-H "Authorization: Bearer $CLOIDER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"key": "db/orders.sql.gz", "version_id": "0000019a2c1f8e4b"}'
{"success": true}
| Status | Message |
|---|---|
400 | Invalid version! |
400 | Only the latest deletion can be undone! (an older delete marker) |
400 | This is already the current version! |
400 | Versions over 5 GB can't be restored from the dashboard, use an S3 client! |
404 | This version doesn't exist anymore! |
502 | Couldn't restore the file, try again in a moment!, Couldn't restore the version, try again in a moment! |
Delete versions for good
POST /buckets/:hashed_id/objects/versions/delete
Permanently deletes chosen versions of one file. Deleted versions can't be restored. Deleting the latest delete marker this way also undeletes the file.
Access: Read & Write token.
| Field | Type | Required | Description |
|---|---|---|---|
key | string | Yes | The file's full key |
version_ids | string list | Yes | 1 to 1000 version ids |
curl -X POST https://cloider.com/api/buckets/k5t1v/objects/versions/delete \
-H "Authorization: Bearer $CLOIDER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"key": "db/orders.sql.gz", "version_ids": ["0000019a22a9f001"]}'
{"failed": []}
Errors: 400 with Invalid file!, Nothing to delete!, Too many versions at once, select fewer! or Invalid version!, and 502 Couldn't delete the versions, try again in a moment!. See Versioning and Data protection.
Related
- Manage files in the dashboard and CLI
- Buckets and bucket keys
- SDKs for S3 access from your code