Documentation
Everything the API does, on one page. The base URL is https://app.freeshka.org/v1.
Authentication
All requests take a bearer token. Tokens are created per project and can be scoped to a single bucket or to a key prefix.
curl https://app.freeshka.org/v1/buckets \
-H "Authorization: Bearer $FREESHKA_TOKEN"
A token that is missing, malformed or expired returns 401. A valid token that lacks permission for the target key returns 403.
Buckets
| Method | Path | Description |
|---|---|---|
GET | /v1/buckets | List buckets in the project. |
POST | /v1/buckets | Create a bucket. Body takes name and region. |
GET | /v1/buckets/{bucket} | Bucket metadata: size, object count, region, visibility. |
DELETE | /v1/buckets/{bucket} | Delete an empty bucket. |
Bucket names are 3–48 characters, lowercase letters, digits and hyphens. Region is ams or fra and cannot be changed later.
Objects
| Method | Path | Description |
|---|---|---|
PUT | /v1/buckets/{bucket}/{key} | Upload or replace an object. |
GET | /v1/buckets/{bucket}/{key} | Download an object. Supports Range. |
HEAD | /v1/buckets/{bucket}/{key} | Metadata only: size, content type, etag. |
DELETE | /v1/buckets/{bucket}/{key} | Delete an object or a specific version. |
GET | /v1/buckets/{bucket}?prefix= | List keys, paginated with cursor. |
Multipart uploads
Files above 64 MB should be uploaded in parts. Start a session, send the parts in any order, then commit.
POST /v1/buckets/assets/video.mp4?uploads
-> { "upload_id": "u_7Rk2ZP" }
PUT /v1/buckets/assets/video.mp4?upload_id=u_7Rk2ZP&part=1
PUT /v1/buckets/assets/video.mp4?upload_id=u_7Rk2ZP&part=2
POST /v1/buckets/assets/video.mp4?upload_id=u_7Rk2ZP&commit
-> { "etag": "b1946ac92492d2347c6235b4d2611184", "parts": 2 }
Parts are 5 MB minimum except the last one. An upload session that is not committed within 24 hours is discarded and its parts are billed to nobody.
Signed URLs
Private objects can be shared with a time-limited link. The signature covers the method, the key and the expiry.
POST /v1/buckets/assets/report.pdf?sign
Content-Type: application/json
{ "expires_in": 3600, "method": "GET" }
{
"url": "https://cdn.freeshka.org/assets/report.pdf?exp=1789531200&sig=KqvP...",
"expires_at": "2026-09-21T11:20:00Z"
}
Optional fields: ip pins the link to one address, referrer restricts it to a hostname. Expired links return 410.
S3 compatibility
The same buckets are reachable over the S3 protocol at https://s3.freeshka.org with signature v4. Set the region to the bucket region and disable virtual-host addressing.
aws --endpoint-url https://s3.freeshka.org \
--region ams \
s3 cp ./report.pdf s3://assets/report.pdf
Supported: bucket and object CRUD, multipart, list v2, copy, conditional headers. Not supported: ACLs, bucket policies, lifecycle rules, website hosting, requester-pays.
Errors
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | Malformed body, bad bucket name or unsupported parameter. |
| 401 | unauthorized | Token missing, malformed or expired. |
| 403 | forbidden | Token is valid but not scoped to this key. |
| 404 | not_found | Bucket or key does not exist. |
| 409 | conflict | Bucket name taken, or delete attempted on a non-empty bucket. |
| 413 | too_large | Single-request upload above 64 MB. Use multipart. |
| 429 | slow_down | Burst limit reached. Retry after the Retry-After header. |
| 503 | unavailable | Region is failing over. Retries are safe. |
Errors are returned as JSON with code, message and a request_id. Quote the request id when reporting a problem.
Limits
- Single-request upload: 64 MB. Multipart object: 5 TB.
- Key length: 1024 bytes. Keys are case sensitive and may contain slashes.
- Buckets per project: 100 on Starter, unlimited on Team.
- Sustained request rate: 500/s per bucket, bursts to 2000/s.