back

API DOCS

upload files programmatically. bots, scripts, whatever.

FILES

POSThttps://mysticbin.xyz/api/upload
fieldtypedesc
filefilemultipart/form-data (required)
passwordstringpassword-protect the file
slugstringcustom slug (3-32 chars, a-z0-9-)
collection_idstringadd to a collection

headers

headervaluedesc
X-Api-Keystringoptional — raises limit to 50/day

response

{
  "success": true,
  "file": {
    "id": "a1b2c3d4e5f6g7h8",
    "name": "image.png",
    "size": 204800,
    "sizeFormatted": "200 KB",
    "mime": "image/png",
    "url": "https://mysticbin.xyz/api/files/a1b2c3d4e5f6g7h8/download",
    "directUrl": "https://mysticbin.xyz/api/files/a1b2c3d4e5f6g7h8/view",
    "deleteToken": "d3l3t3_t0k3n_h3r3",
    "hasPassword": false,
    "slug": null,
    "thumbnailUrl": "https://mysticbin.xyz/api/files/a1b2c3d4e5f6g7h8/thumbnail"
  },
  "remaining": 2
}

error

{
  "success": false,
  "error": "Rate limit exceeded. Max 3 uploads per day.",
  "remaining": 0
}
POSThttps://mysticbin.xyz/api/upload/bulk

upload up to 10 files in a single request.

fieldtypedesc
files[]file[]multipart array of files (max 10)

response

{
  "success": true,
  "files": [
    { "id": "abc123", "name": "a.png", "url": "..." },
    { "id": "def456", "name": "b.jpg", "url": "..." }
  ],
  "errors": [],
  "remaining": 1
}
GEThttps://mysticbin.xyz/api/files/{id}

returns metadata for a file. supports file ID or custom slug.

response

{
  "success": true,
  "file": {
    "id": "a1b2c3d4e5f6g7h8",
    "name": "image.png",
    "size": 204800,
    "sizeFormatted": "200 KB",
    "mime": "image/png",
    "uploadedAt": "2026-03-14 12: 00: 00",
    "downloads": 5,
    "hasPassword": false,
    "slug": "my-screenshot",
    "thumbnailUrl": "https://mysticbin.xyz/api/files/a1b2c3d4e5f6g7h8/thumbnail"
  }
}

password-protected files return limited metadata until unlocked.

GEThttps://mysticbin.xyz/api/files/{id}/download

force-downloads the file as an attachment.

GEThttps://mysticbin.xyz/api/files/{id}/view

serves the file inline (images, PDFs display in browser). unsafe types are forced to download.

password-protected files

send the password via header:

headervaluedesc
X-File-Passwordstringfile password
GEThttps://mysticbin.xyz/api/files/{id}/thumbnail

returns a 200px WebP thumbnail. auto-generated on first request for JPEG, PNG, GIF and WebP files.

DELETEhttps://mysticbin.xyz/api/files/{id}

permanently deletes the file and its thumbnail.

headervaluedesc
X-Delete-Tokenstringthe token returned at upload time

response

{
  "success": true,
  "message": "File deleted"
}

COLLECTIONS

POSThttps://mysticbin.xyz/api/collections

group files into a named collection.

fieldtypedesc
titlestringcollection name (required)
descriptionstringoptional description

response

{
  "success": true,
  "collection": {
    "id": "col_abc123",
    "title": "screenshots",
    "editToken": "xxxxxx"
  }
}

save the editToken — you need it to add files or delete the collection.

GEThttps://mysticbin.xyz/api/collections/{id}
{
  "success": true,
  "collection": {
    "id": "col_abc123",
    "title": "screenshots",
    "description": "my stuff",
    "fileCount": 3,
    "files": [
      { "id": "abc", "name": "a.png", "size": 1024, "mime": "image/png" }
    ]
  }
}
POSThttps://mysticbin.xyz/api/collections/{id}/files
header / fieldtypedesc
X-Edit-Tokenheadercollection edit token
file_idstringID of existing file to add
DELETEhttps://mysticbin.xyz/api/collections/{id}
headervaluedesc
X-Edit-Tokenstringcollection edit token

removes the collection. files inside are NOT deleted, only unlinked.

AUTH

API keys raise your upload limit from 3 to 50 per day and are required for webhook management.

send your key in the X-Api-Key header on any request.

example

"tk">-cmd">curl "tk-flag">-X POST \
  "tk-flag">-H "X">-Api">-Key: mb_your_key_here" \
  "tk-flag">-F "file=@image.png" \
  https://mysticbin.xyz/api/upload

API keys are issued by the admin. contact the operator to get one.

GEThttps://mysticbin.xyz/api/status

check your current rate limit status.

{
  "remaining": 2,
  "used": 1,
  "limit": 3,
  "version": "3.0.0"
}

with an API key, limit increases to 50. rate limit info is also returned in response headers: X-RateLimit-Used and X-RateLimit-Max.

WEBHOOKS

POSThttps://mysticbin.xyz/api/webhooks

requires an API key. receive HTTP POST callbacks when events occur.

fieldtypedesc
urlstringHTTPS callback URL (required)
eventsarray["file.uploaded", "file.deleted", "file.downloaded", "collection.created"]

response

{
  "success": true,
  "webhook": {
    "id": "wh_abc123",
    "url": "https://example.com/hook",
    "events": ["file.uploaded"],
    "secret": "whsec_xxxxxx"
  }
}

payloads are signed with HMAC-SHA256. verify using the X-Webhook-Signature header and your secret.

GEThttps://mysticbin.xyz/api/webhooks

list all webhooks for your API key.

DELETEhttps://mysticbin.xyz/api/webhooks/{id}

remove a webhook. must own it via the same API key.

EXAMPLES

basic upload

"tk">-cmd">curl "tk-flag">-X POST "tk-flag">-F "file=@/path/to/image.png" https://mysticbin.xyz/api/upload

with password + custom slug

"tk">-cmd">curl "tk-flag">-X POST \
  "tk-flag">-F "file=@secret.pdf" \
  "tk-flag">-F "password=hunter2" \
  "tk-flag">-F "slug=my">-secret">-doc" \
  https://mysticbin.xyz/api/upload

with API key

"tk">-cmd">curl "tk-flag">-X POST \
  "tk-flag">-H "X">-Api">-Key: mb_your_key_here" \
  "tk-flag">-F "file=@image.png" \
  https://mysticbin.xyz/api/upload

delete a file

"tk">-cmd">curl "tk-flag">-X DELETE \
  "tk-flag">-H "X">-Delete">-Token: your_token" \
  https://mysticbin.xyz/api/files/a1b2c3d4e5f6g7h8
"tk-kw">import requests

"tk-cmt"># upload a file
url = "https://mysticbin.xyz/api/upload"
files = {"file": open("image.png", "rb")}
data = {"slug": "cool-pic"}  "tk-cmt"># optional
r = requests.post(url, files=files, data=data)
result = r.json()

"tk-kw">if result["success"]:
    print(result["file"]["url"])
    print("delete token:", result["file"]["deleteToken"])
"tk-kw">else:
    print(result["error"])
"tk-kw">const fs = "tk-kw">require("fs");
"tk-kw">const FormData = "tk-kw">require("form-data");

"tk-kw">async "tk-kw">function upload(filePath) {
  "tk-kw">const form = "tk-kw">new FormData();
  form.append("file", fs.createReadStream(filePath));

  "tk-kw">const res = "tk-kw">await fetch("https:">//mysticbin.xyz/api/upload", {
    method: "POST",
    body: form,
  });
  "tk-kw">const data = "tk-kw">await res.json();
  "tk-kw">if (data.success) {
    console.log("URL:", data.file.url);
    console.log("Delete token:", data.file.deleteToken);
  }
  "tk-kw">return data;
}
"tk-kw">import aiohttp

"tk-kw">async "tk-kw">def upload(file_path: str) -> str | "tk-kw">None:
    "tk-kw">async "tk-kw">with aiohttp.ClientSession() "tk-kw">as session:
        data = aiohttp.FormData()
        data.add_field("file", open(file_path, "rb"))
        "tk-kw">async "tk-kw">with session.post(
            "https://mysticbin.xyz/api/upload", data=data
        ) "tk-kw">as resp:
            result = "tk-kw">await resp.json()
            "tk-kw">return result["file"]["url"] "tk-kw">if result["success"] "tk-kw">else "tk-kw">None
"tk-kw">const fs = "tk-kw">require("fs");
"tk-kw">const FormData = "tk-kw">require("form-data");

"tk-kw">async "tk-kw">function upload(filePath) {
  "tk-kw">const form = "tk-kw">new FormData();
  form.append("file", fs.createReadStream(filePath));
  "tk-kw">const res = "tk-kw">await fetch("https:">//mysticbin.xyz/api/upload", {
    method: "POST",
    body: form,
  });
  "tk-kw">const data = "tk-kw">await res.json();
  "tk-kw">return data.success ? data.file.url : "tk-kw">null;
}
"tk-kw">package main

"tk-kw">import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "mime/multipart"
    "net/http"
    "os"
)

"tk-kw">func upload(filePath string) (string, "tk-kw">err) {
    file, "tk-kw">err := os.Open(filePath)
    "tk-kw">if "tk-kw">err != "tk-kw">nil {
        "tk-kw">return "", "tk-kw">err
    }
    "tk-kw">defer file.Close()

    "tk-kw">var buf bytes.Buffer
    w := multipart.NewWriter(&buf)
    fw, "tk-kw">err := w.CreateFormFile("file", filePath)
    "tk-kw">if "tk-kw">err != "tk-kw">nil {
        "tk-kw">return "", "tk-kw">err
    }
    io.Copy(fw, file)
    w.Close()

    resp, "tk-kw">err := http.Post(
        "https:">//mysticbin.xyz/api/upload",
        w.FormDataContentType(),
        &buf,
    )
    "tk-kw">if "tk-kw">err != "tk-kw">nil {
        "tk-kw">return "", "tk-kw">err
    }
    "tk-kw">defer resp.Body.Close()

    "tk-kw">var result map[string]interface{}
    json.NewDecoder(resp.Body).Decode(&result)
    "tk-kw">if result["success"] == true {
        f := result["file"].(map[string]interface{})
        "tk-kw">return f["url"].(string), "tk-kw">nil
    }
    "tk-kw">return "", fmt.Errorf("%v", result["error"])
}
"tk-kw">use reqwest::multipart;
"tk-kw">use std::path::Path;

"tk-kw">async "tk-kw">fn upload(file_path: &str) -> Result<String, Box<dyn std::error::Error>> {
    "tk-kw">let file_name = Path::new(file_path)
        .file_name()
        .unwrap()
        .to_string_lossy()
        .to_string();

    "tk-kw">let file_bytes = tokio::fs::read(file_path)."tk-kw">await?;
    "tk-kw">let part = multipart::Part::bytes(file_bytes)
        .file_name(file_name);

    "tk-kw">let form = multipart::Form::new()
        .part("file", part);

    "tk-kw">let client = reqwest::Client::new();
    "tk-kw">let res = client
        .post("https:">//mysticbin.xyz/api/upload")
        .multipart(form)
        .send()
        ."tk-kw">await?
        .json::<serde_json::Value>()
        ."tk-kw">await?;

    "tk-kw">if res["success"] == true {
        "tk-kw">Ok(res["file"]["url"].as_str().unwrap().to_string())
    } "tk-kw">else {
        "tk-kw">Err(res["error"].to_string().into())
    }
}

limits

200mbmax file size
3uploads / day
50with API key
10bulk files

error codes

codemeaning
400validation error — bad input, blocked extension, etc.
401unauthorized — missing or invalid API key / admin key
403forbidden — wrong password, wrong edit token
404not found — file or collection doesn't exist
429rate limited — try again tomorrow or use an API key
500server error — something went wrong on our end
MysticByte © 2026