Quickstart

Upload a file. Get a URL back.

The simplest file host, priced for Bangladesh. One key and one HTTP request — no SDK, no library to learn.

Three steps

  1. 1

    Sign up free

    Create an account and verify your email.

    Sign up →
  2. 2

    Create an app

    Give it a lowercase name such as my-shop. Your API key is shown once — copy it and store it.

    Create an app →
  3. 3

    Send one request

    Pick your language below, paste it, put in your key and app name, and run it. It prints the file’s URL.

    Jump to the code →

Upload one file

The key is a secret. Keep it on your server. Anyone can read a key shipped inside a mobile app or a web page — for a public app, upload through your own backend.

From any terminal (Git Bash on Windows). The last line prints just the URL; leave it off to see the whole answer, including the message when an upload is refused.

bash
URL=https://cloud.zettabyteincorp.com/backend/api

curl -s -X POST "$URL/bucket/upload" \
  -H "zetta-api-key: $ZETTA_API_KEY" \
  -H "zetta-app-name: my-shop" \
  -F "files=@photo.jpg" \
  -F "subFolder=avatars" \
  | grep -o '"url":"[^"]*' | cut -d'"' -f4

API reference

Four routes, nothing else. Every request goes to the base URL below and sends both headers.

https://cloud.zettabyteincorp.com/backend/api

Upload files

POST /bucket/upload

Up to 20 files in one request.

Headers

  • zetta-api-key — your app’s API key.
  • zetta-app-name — your app’s name, e.g. my-shop. Case and spaces around it do not matter.

Request

  • Body: multipart/form-data.
  • Field files — one part per file. The field is files; file is refused. Each part needs its real content type, not application/octet-stream.
  • Field subFolder — optional. Groups files under uploads/<app>/<subFolder>; slashes are allowed, .. is not.

Success response

  • 201, success: true, and one entry per stored file in data.files: id, url, storedName, originalName, size.
  • size is what was stored, after photos are converted to WebP. skippedFiles lists any file that was refused, with a reason.
{
  "success": true,
  "message": "✅ 1 images uploaded. ",
  "data": {
    "files": [
      {
        "id": "0b7c3f9e-5a3e-4d57-9a53-3a1f2c9d8e11",
        "url": "https://cloud.zettabyteincorp.com/uploads/my-shop/avatars/photo-1759570000000.webp",
        "storedName": "photo-1759570000000.webp",
        "originalName": "photo.jpg",
        "size": 48213
      }
    ]
  },
  "skippedFiles": []
}

Errors

  • 401MISSING_API_KEYThe zetta-api-key header is missing.
  • 401MISSING_APP_NAMEThe zetta-app-name header is missing.
  • 403INVALID_APP_CREDENTIALSNo app has this name with this key.
  • 403STORAGE_LIMIT_EXCEEDEDThe app has used all its storage. Upgrade the plan or delete files.
  • 403SUBSCRIPTION_SUSPENDEDThe paid period ended more than 7 days ago and was not renewed. Pay the renewal invoice to restore uploads.
  • 403SERVER_STORAGE_FULLThe service’s disk is full. Not your fault; try again later.
  • 201success: falseNo file in the request, every file refused (wrong type), or an invalid subFolder. Read message; skippedFiles says which file and why.
  • 413—A file is larger than 50 MB.

Examples

bash
URL=https://cloud.zettabyteincorp.com/backend/api

curl -s -X POST "$URL/bucket/upload" \
  -H "zetta-api-key: $ZETTA_API_KEY" \
  -H "zetta-app-name: my-shop" \
  -F "files=@photo.jpg" \
  -F "subFolder=avatars" \
  | grep -o '"url":"[^"]*' | cut -d'"' -f4

List files

GET /bucket

Every file of your app, newest first.

Headers

  • zetta-api-key — your app’s API key.
  • zetta-app-name — your app’s name, e.g. my-shop. Case and spaces around it do not matter.

Request

  • No body, no query string.
  • Header sub-folder — optional. Only files in that subFolder; trimmed and lower-cased.

Success response

  • 200, success: true, and data.files: each file with id, appId, originalName, storedName, url, path, subFolder, size and uploadedAt.
{
  "success": true,
  "message": "📂 Files listed successfully.",
  "data": {
    "files": [
      {
        "id": "0b7c3f9e-5a3e-4d57-9a53-3a1f2c9d8e11",
        "appId": "…",
        "originalName": "photo.jpg",
        "storedName": "photo-1759570000000.webp",
        "url": "https://cloud.zettabyteincorp.com/uploads/my-shop/avatars/photo-1759570000000.webp",
        "path": "…",
        "subFolder": "avatars",
        "size": 48213,
        "uploadedAt": "2026-10-04T08:00:00.000Z"
      }
    ]
  }
}

Errors

  • 401MISSING_API_KEYThe zetta-api-key header is missing.
  • 401MISSING_APP_NAMEThe zetta-app-name header is missing.
  • 403INVALID_APP_CREDENTIALSNo app has this name with this key.
  • 400success: falseThe sub-folder header is not a safe folder name.

Examples

bash
URL=https://cloud.zettabyteincorp.com/backend/api

curl -s "$URL/bucket" \
  -H "zetta-api-key: $ZETTA_API_KEY" \
  -H "zetta-app-name: my-shop" \
  -H "sub-folder: avatars"   # optional: only this subFolder

Delete a file

DELETE /bucket/delete/:id

Removes the file from disk and from your usage.

Headers

  • zetta-api-key — your app’s API key.
  • zetta-app-name — your app’s name, e.g. my-shop. Case and spaces around it do not matter.

Request

  • Path :id — the id the upload returned.
  • No body.

Success response

  • 200, success: true and a message. The file’s bytes are subtracted from your usage.
{
  "success": true,
  "message": "🗑️File and metadata deleted successfully!"
}

Errors

  • 401MISSING_API_KEYThe zetta-api-key header is missing.
  • 401MISSING_APP_NAMEThe zetta-app-name header is missing.
  • 403INVALID_APP_CREDENTIALSNo app has this name with this key.
  • 500success: falseThe id does not exist in your app (or belongs to another app). The error field says File metadata not found; it is a 500, not a 404.

Examples

bash
URL=https://cloud.zettabyteincorp.com/backend/api

curl -s -X DELETE "$URL/bucket/delete/$FILE_ID" \
  -H "zetta-api-key: $ZETTA_API_KEY" \
  -H "zetta-app-name: my-shop"

Check usage

GET /bucket/usage

Files, bytes used and your limit.

Headers

  • zetta-api-key — your app’s API key.
  • zetta-app-name — your app’s name, e.g. my-shop. Case and spaces around it do not matter.

Request

  • No body, no query string.

Success response

  • 200, success: true, and data: appId, appName, filesCount, usedBytes and storageLimit (both strings, in bytes), usedPercent and usedHuman.
  • A storageLimit of -1 means no limit.
{
  "success": true,
  "data": {
    "appId": "…",
    "appName": "my-shop",
    "filesCount": 7,
    "usedBytes": "524288",
    "storageLimit": "1048576",
    "usedPercent": 50,
    "usedHuman": "512.00 KB"
  }
}

Errors

  • 401MISSING_API_KEYThe zetta-api-key header is missing.
  • 401MISSING_APP_NAMEThe zetta-app-name header is missing.
  • 403INVALID_APP_CREDENTIALSNo app has this name with this key.

Examples

bash
URL=https://cloud.zettabyteincorp.com/backend/api

curl -s "$URL/bucket/usage" \
  -H "zetta-api-key: $ZETTA_API_KEY" \
  -H "zetta-app-name: my-shop"

Private files

POST /bucket/files/:id/signed-url

For files that must not be public, such as payment receipts. Upload with `visibility=private`, then ask for a link that expires.

Headers

  • zetta-api-key — your app’s API key.
  • zetta-app-name — your app’s name, e.g. my-shop. Case and spaces around it do not matter.

Request

  • Upload: add the form field visibility=private to a normal upload. The file is stored outside the public folder and the answer has id, name, size and private: true — no url.
  • Link: POST /bucket/files/:id/signed-url with the same two headers. JSON body expiresIn is optional: seconds, 60 to 604800 (7 days), default 900 (15 minutes).
  • Open the url with a plain GET — no headers needed. A public file can be signed too.

Success response

  • 200, success: true, url, expiresAt (ISO time) and expiresIn.
  • The file opens inline, with Cache-Control: private, no-store. Make a fresh link each time you show it; do not store the link.
  • In the file list a private file has private: true and no url. Delete works the same as for public files, and private files count toward your storage.
{
  "success": true,
  "url": "https://cloud.zettabyteincorp.com/backend/api/bucket/private/0b7c3f9e-5a3e-4d57-9a53-3a1f2c9d8e11?exp=1759571800&sig=4f1c…",
  "expiresAt": "2026-10-04T08:15:00.000Z",
  "expiresIn": 900
}

Errors

  • 401MISSING_API_KEYThe zetta-api-key header is missing.
  • 401MISSING_APP_NAMEThe zetta-app-name header is missing.
  • 403INVALID_APP_CREDENTIALSNo app has this name with this key.
  • 400—expiresIn is under 60 or over 604800.
  • 404—No file with this id in your app (another app’s file answers the same).
  • 403—Opening a link whose signature is wrong or whose time has passed.

Examples

bash
URL=https://cloud.zettabyteincorp.com/backend/api

# 1. Upload privately: the answer has an id and no url
curl -s -X POST "$URL/bucket/upload" \
  -H "zetta-api-key: $ZETTA_API_KEY" \
  -H "zetta-app-name: my-shop" \
  -F "visibility=private" \
  -F "subFolder=receipts" \
  -F "files=@receipt.pdf;type=application/pdf"

# 2. Make a link that works for 15 minutes (expiresIn: 60 to 604800 seconds)
curl -s -X POST "$URL/bucket/files/$FILE_ID/signed-url" \
  -H "zetta-api-key: $ZETTA_API_KEY" \
  -H "zetta-app-name: my-shop" \
  -H "content-type: application/json" \
  -d '{"expiresIn": 900}'

What comes back

  • Success: success: true, and the URL is in data.files[0].url. Keep data.files[0].id if you will delete the file later.
  • Refused (wrong file type, bad subFolder, no file): success: false with a message. A wrong key or a full quota answers 403, a file over 50 MB 413.
  • So check both: !res.ok || !json.success.

Limits

  • 50 MB per file, up to 20 files per request.
  • Images, PDF, Word and Excel. Send the file’s real type — a file sent as application/octet-stream is refused.
  • Photos are stored as WebP, so their URL ends in .webp.
  • Each app has a storage quota set by its plan. Your usage page shows what is left, and you get an email at 80%.

Ready to upload?

Quickstart - Zetta Cloud