Two endpoints, no auth, no keys
FileDitch has two public APIs. Upload lives on new.fileditch.com: POST a file, get a JSON response with a URL. Status lives on fileditchfiles.st: give it a file link, learn whether that file still exists and how big it is. No API keys, no accounts, no bullshit. Both are free to use from scripts, bots, monitors and browsers.
Files are kept as long as storage allows — no scheduled expiry. See retention under Limits below.
POST / PUThttps://new.fileditch.com/upload.php
curl -T yourfile.mp4 "https://new.fileditch.com/upload.php?filename=yourfile.mp4"
curl -X POST \ -H "Content-Type: application/octet-stream" \ --data-binary @yourfile.mp4 \ "https://new.fileditch.com/upload.php?filename=yourfile.mp4"
curl -F "file=@yourfile.mp4" https://new.fileditch.com/upload.php
import requests
with open("yourfile.mp4", "rb") as f:
r = requests.put("https://new.fileditch.com/upload.php",
params={"filename": "yourfile.mp4"}, data=f)
print(r.json()["url"])
const res = await fetch("https://new.fileditch.com/upload.php?filename=" + encodeURIComponent(file.name), {
method: "PUT",
body: file // a File / Blob / Buffer / ReadableStream
});
const { url } = await res.json();
The server resolves the filename in this order. The first match wins:
1. ?filename= query parameter 2. X-Filename request header 3. Content-Disposition header (filename="..." or RFC 5987 filename*=) 4. Multipart field filename 5. Random hex string (fallback)
Unicode filenames are preserved. Spaces become underscores; the characters / \ : * ? " < > | # & ; = % + @ ! , ` ~ ^ $ { } become underscores too; leading/trailing dots are removed; only the last path component is used. Names longer than 255 bytes are replaced by a random name.
| success | boolean — always true on success |
| url | string — direct link to the uploaded file. The part after the domain is the file path you feed to the Status API |
| filename | string — final filename stored on server |
| size | integer — file size in bytes |
{
"success": true,
"url": "https://fileditchfiles.st/alpha27/a3f9c1b2d4e5f6071829/yourfile.mp4",
"filename": "yourfile.mp4",
"size": 104857600
}
{ "error": "description" }
| 400 | Empty file, no file sent, or incomplete upload (connection closed early) |
| 403 | Blocked file type (executables, scripts, web files) |
| 405 | Wrong HTTP method |
| 411 | Multipart upload without Content-Length (chunked transfer-encoding) — use a raw PUT instead |
| 413 | File exceeds 150 GB limit |
| 429 | Rate limited — back off and retry (see the Retry-After header) |
| 500 | Server error |
| 503 | Server busy — retry in a few seconds |
| 507 | Server out of space — try again later |
| Max file size | 150 GB |
| Retention | Indefinite while accessed — only files untouched for 45+ days may be removed, and only when space is needed |
| Blocked extensions | php, html, js, exe, apk, sh, py, bat and other executables & scripts — the file content is sniffed too, so renaming an executable does not help |
| Rate limits | 2 requests/s and 10 concurrent uploads per IP on this endpoint |
| Upload duration | No limit for raw uploads (curl -T / --data-binary). Multipart (curl -F) requests are cut off after 6 hours — use a raw upload for very large files |
| Authentication | None required |
A tiny read-only endpoint that answers one question: does this file still exist, and how big is it? Use it to check links before you post them, to prune dead links from a bot or a forum, to monitor an upload you care about, or to show a file size without downloading anything. It is deliberately open: no proof-of-work, no landing page, no user-agent checks, so curl, Python, cron jobs and browsers are all welcome.
GET / HEADhttps://fileditchfiles.st/api/<folder>/<random-id>/<filename>
The path is exactly the part after the domain in a normal FileDitch link. Take any file URL and insert /api after the host and you have the status URL. This is the only supported form; do not call any .php file directly. fileditchfiles.me works identically. Percent-encode filenames with spaces or non-ASCII characters as you would in any URL; a literal + in a name stays a +.
https://fileditchfiles.st/charlie21/aa59bb5b707fb30f002e/cat.mp4 https://fileditchfiles.st/api/charlie21/aa59bb5b707fb30f002e/cat.mp4
Always application/json, always HTTP 200 when the request itself is valid. Three fields, nothing else:
| file | string — the normalised path that was checked, always starting with / |
| status | true — the file exists on its originfalse — the file is gone (deleted, expired, or never existed)"unknown" — the origin could not be reached right now. Not a verdict; retry later |
| size | integer bytes when status is true, otherwise null |
{"file":"/charlie21/aa59bb5b707fb30f002e/cat.mp4","status":true,"size":50720388}
{"file":"/charlie21/aa59bb5b707fb30f002e/cat.mp4","status":false,"size":null}
{"file":"/charlie21/aa59bb5b707fb30f002e/cat.mp4","status":"unknown","size":null}
Treat the three states differently. false is final and safe to act on (drop the link). "unknown" is transient: a shard was slow or restarting. Do not delete anything on "unknown"; check again in a minute. In JavaScript remember that "unknown" is a truthy string, so test with === true, not a bare if (status).
Only a bad request gets a non-200. The body is then {"error":"..."} with one of these codes:
| 400 | bad-path — path missing, not starting with /, or containing . / .. segments |
| 405 | method-not-allowed — only GET, HEAD and OPTIONS are accepted |
| 429 | Rate limited — slow down (see limits below) |
| 503 | api-disabled — the API is switched off for maintenance. Honour the Retry-After header |
curl -s https://fileditchfiles.st/api/charlie21/aa59bb5b707fb30f002e/cat.mp4 # just the status, with jq curl -s https://fileditchfiles.st/api/charlie21/aa59bb5b707fb30f002e/cat.mp4 | jq .status # HEAD works too if you only want the headers curl -sI https://fileditchfiles.st/api/charlie21/aa59bb5b707fb30f002e/cat.mp4
fdstat() { curl -s "$(echo "$1" | sed -E 's#^(https?://[^/]+)/#\1/api/#')"; }
fdstat https://fileditchfiles.st/charlie21/aa59bb5b707fb30f002e/cat.mp4
async function fileditchStatus(fileUrl) {
const u = new URL(fileUrl);
const r = await fetch(`${u.origin}/api${u.pathname}`);
if (!r.ok) throw new Error(`status api ${r.status}`);
return r.json(); // { file, status, size }
}
const s = await fileditchStatus("https://fileditchfiles.st/charlie21/aa59bb5b707fb30f002e/cat.mp4");
if (s.status === true) console.log("alive,", s.size, "bytes");
else if (s.status === false) console.log("gone");
else console.log("unknown, retry later");
import requests
from urllib.parse import urlsplit
def fileditch_status(file_url):
u = urlsplit(file_url)
r = requests.get(f"{u.scheme}://{u.netloc}/api{u.path}", timeout=15)
r.raise_for_status()
return r.json()
s = fileditch_status("https://fileditchfiles.st/charlie21/aa59bb5b707fb30f002e/cat.mp4")
if s["status"] is True:
print("alive", s["size"], "bytes")
elif s["status"] is False:
print("gone")
else:
print("unknown, retry later")
URL=$(curl -sT cat.mp4 "https://new.fileditch.com/upload.php?filename=cat.mp4" | jq -r .url)
curl -s "${URL/fileditchfiles.st\//fileditchfiles.st/api/}"
| Caching | Answers are cached server-side for 1 hour, and the response carries Cache-Control: public, max-age=3600 so browsers and proxies may cache it too. A false (gone) answer is re-checked after 60 seconds, so a freshly uploaded file that briefly reads as gone while it propagates will flip to true quickly. A file that is deleted can therefore still read as true for up to an hour |
| CORS | Access-Control-Allow-Origin: * — call it straight from any web page. OPTIONS preflight is answered with 204 |
| Rate limits | 20 requests/s per IP with a burst of 40, and 40 concurrent connections. Above that you get 429. Spread bulk checks out; with the cache, re-checking the same file more than once an hour is pointless anyway |
| Methods | GET, HEAD, OPTIONS. Anything else returns 405 with an Allow header |
| User agents | Any. The bot filtering on the normal file pages does not apply to /api/ |
| Side effects | None. A status check is not counted as a download and does not touch the 45-day retention clock |
| Authentication | None required |
Something missing that you need? Tell us on the contact page or Discord.