upload files programmatically. bots, scripts, whatever.
https://mysticbin.xyz/api/uploadfilefilemultipart/form-data (required)passwordstringpassword-protect the fileslugstringcustom slug (3-32 chars, a-z0-9-)collection_idstringadd to a collectionheaders
X-Api-Keystringoptional — raises limit to 50/dayresponse
{
"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
}
https://mysticbin.xyz/api/upload/bulkupload up to 10 files in a single request.
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
}
https://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.
https://mysticbin.xyz/api/files/{id}/downloadforce-downloads the file as an attachment.
https://mysticbin.xyz/api/files/{id}/viewserves the file inline (images, PDFs display in browser). unsafe types are forced to download.
password-protected files
send the password via header:
X-File-Passwordstringfile passwordhttps://mysticbin.xyz/api/files/{id}/thumbnailreturns a 200px WebP thumbnail. auto-generated on first request for JPEG, PNG, GIF and WebP files.
https://mysticbin.xyz/api/files/{id}permanently deletes the file and its thumbnail.
X-Delete-Tokenstringthe token returned at upload timeresponse
{
"success": true,
"message": "File deleted"
}
https://mysticbin.xyz/api/collectionsgroup files into a named collection.
titlestringcollection name (required)descriptionstringoptional descriptionresponse
{
"success": true,
"collection": {
"id": "col_abc123",
"title": "screenshots",
"editToken": "xxxxxx"
}
}
save the editToken — you need it to add files or delete the collection.
https://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" }
]
}
}
https://mysticbin.xyz/api/collections/{id}/filesX-Edit-Tokenheadercollection edit tokenfile_idstringID of existing file to addhttps://mysticbin.xyz/api/collections/{id}X-Edit-Tokenstringcollection edit tokenremoves the collection. files inside are NOT deleted, only unlinked.
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.
https://mysticbin.xyz/api/statuscheck 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.
https://mysticbin.xyz/api/webhooksrequires an API key. receive HTTP POST callbacks when events occur.
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.
https://mysticbin.xyz/api/webhookslist all webhooks for your API key.
https://mysticbin.xyz/api/webhooks/{id}remove a webhook. must own it via the same API key.
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()) } }
