Skip to main content

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​

MethodPathAccess
GET/buckets/:hashed_id/objectsRead
POST/buckets/:hashed_id/objects/uploadWrite
GET/buckets/:hashed_id/objects/downloadRead
POST/buckets/:hashed_id/foldersWrite
POST/buckets/:hashed_id/objects/deleteWrite
GET/buckets/:hashed_id/objects/versionsRead
GET/buckets/:hashed_id/objects/deletedRead
POST/buckets/:hashed_id/objects/restoreWrite
POST/buckets/:hashed_id/objects/versions/deleteWrite

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 parameterRequiredDescription
prefixNoThe folder, for example images/. Empty or left out for the bucket's root
tokenNoThe 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/"
Response
{
"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": ""
}
FieldDescription
prefixThe folder listed
foldersSubfolders, each with its full prefix and its name inside this folder
objectsFiles, each with its full key, its name inside this folder, its size in bytes and last_modified
next_tokenPass 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​

FieldTypeRequiredDescription
keystringYesThe file's full key, for example images/logo.png. Can't end with /
sizeintegerYesThe file's exact size in bytes, at most 5 GB (5368709120)
content_typestringNoThe 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)
Response
{"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
upload.mjs
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()}`);
StatusMessage
400Invalid file name!
400Files over 5 GB can't be uploaded from the dashboard, use an S3 client!
403This API token is read only!, You have read-only access to this account, you can not change files!
502Couldn'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 parameterRequiredDescription
keyYesThe file's full key
version_idNoA 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"
Response
{"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.

FieldTypeRequiredDescription
prefixstringYesThe 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/"}'
Response
{"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.

FieldTypeRequiredDescription
keysstring listOne of the twoFiles to delete, by full key
prefixesstring listOne of the twoFolders 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/"]}'
Response
{"failed": []}

failed lists the keys that couldn't be deleted. An empty list means everything was deleted.

StatusMessage
400Nothing to delete!, Too many files at once, select fewer!, Invalid file!, Invalid folder!
400The folder "tmp/" holds over 10000 files, delete it with an S3 client!
403This API token is read only!, You have read-only access to this account, you can not change files!
502Couldn'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 parameterRequiredDescription
keyYesThe 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"
Response
{
"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
}
FieldDescription
version_idThe version's id
sizeSize in bytes. 0 for a delete marker
last_modifiedWhen the version was written, or the file deleted
is_latestThe current version. When it is a delete marker, the file is deleted
delete_markertrue for a deletion
truncatedtrue 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 parameterRequiredDescription
prefixNoThe folder. Empty for the root
tokenNoThe previous page's next_token
curl -G https://cloider.com/api/buckets/k5t1v/objects/deleted \
-H "Authorization: Bearer $CLOIDER_TOKEN" \
--data-urlencode "prefix=db/"
Response
{
"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.

FieldTypeRequiredDescription
keystringYesThe file's full key
version_idstringYesThe 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"}'
Response
{"success": true}
StatusMessage
400Invalid version!
400Only the latest deletion can be undone! (an older delete marker)
400This is already the current version!
400Versions over 5 GB can't be restored from the dashboard, use an S3 client!
404This version doesn't exist anymore!
502Couldn'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.

FieldTypeRequiredDescription
keystringYesThe file's full key
version_idsstring listYes1 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"]}'
Response
{"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.