Upload a file and get a direct link back. Every response is JSON and you do not need an API key.
https://anya-file-host.vercel.appThree endpoints cover most needs: upload a file, import one from a link, and open it again from its direct URL.
Cached delivery with support for range requests.
Files are encrypted before they reach storage.
Plain JSON and nothing to configure.
/upload/hosturl?url={media_url}/{timestamp}.{ext}multipart/form-data to /upload.url.curl -X POST https://anya-file-host.vercel.app/upload \
-F "file=@path/to/file.jpg"/uploadSend the file as multipart/form-data. Vercel limits one request body to about 4.5 MB, so use this for small files. For anything bigger, see Large files.
| Name | Type | Required | Description |
|---|---|---|---|
file | file | Yes | The file to upload. |
curl -X POST https://anya-file-host.vercel.app/upload \
-F "file=@path/to/file.jpg"/hosturl?url={media_url}We download the remote file on our server and store it for you. It works for files up to the 100 MB limit, which makes it handy for copying images that already live somewhere else.
| Name | Type | Required | Description |
|---|---|---|---|
url | string | Yes | A public http or https link to the file. Remember to URL-encode it. |
curl "https://anya-file-host.vercel.app/hosturl?url=https://example.com/image.jpg"The website cuts each file into pieces of at most 4,000,000 bytes, uploads every piece, and then asks the server to save the result. You can do the same from your own code, for files up to 100 MB.
/api/upload/chunkThe body is the raw bytes of one piece. You get back a signed record for that piece.
| Header | Type | Required | Description |
|---|---|---|---|
x-upload-id | string | Yes | Random, 8 to 64 characters, the same for every piece of one file. |
x-chunk-index | integer | Yes | The position of the piece: 0, 1, 2 and so on. |
x-file-name | string | No | The file name, URL-encoded. |
/api/upload/completeSend JSON with the records from the step above. The response is the normal success JSON.
| Field | Type | Required | Description |
|---|---|---|---|
uploadId | string | Yes | The same id used for every piece. |
name | string | Yes | The original file name. |
type | string | No | The MIME type of the file. |
meta | object | No | Optional width, height and duration. |
chunks | array | Yes | The records returned by each piece upload. |
const CHUNK = 4_000_000;
const uploadId = crypto.randomUUID().replaceAll('-', '');
const chunks = [];
for (let i = 0; i * CHUNK < file.size; i++) {
const res = await fetch('https://anya-file-host.vercel.app/api/upload/chunk', {
method: 'POST',
headers: {
'x-upload-id': uploadId,
'x-chunk-index': String(i),
'x-file-name': encodeURIComponent(file.name),
},
body: file.slice(i * CHUNK, (i + 1) * CHUNK),
});
chunks.push(await res.json());
}
const done = await fetch('https://anya-file-host.vercel.app/api/upload/complete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ uploadId, name: file.name, type: file.type, chunks }),
});
console.log((await done.json()).url);/{timestamp}.{ext}Every upload returns a direct link such as https://anya-file-host.vercel.app/1753020712833.png. Open it in a browser, put it in an <img> tag, or hot-link it anywhere. The file is decrypted as it is sent, and range requests work, so video and audio can seek. Types that can run code in a browser (html, js, css, xml and svg) always download.
{
"data": "https://anya-file-host.vercel.app/1753020712833.png",
"url": "https://anya-file-host.vercel.app/1753020712833.png",
"id": "1753020712833.png",
"filename": "sample.png",
"size": 83638,
"uploaded_on": "2025-07-20T14:11:52.833Z",
"media_type": "image/png"
}data and urlidfilenamesizeuploaded_onmedia_type{
"error": "Failed to download file from URL"
}A failed request returns an HTTP status code and a JSON body with a readable error message.
| Status | Meaning | Common cause |
|---|---|---|
| 400 | Bad request | A file, url, id or JSON body is missing or invalid. |
| 401 | Unauthorized | Sign in is required for this route, or the credentials are wrong. |
| 404 | Not found | The file does not exist or has been deleted. |
| 413 | Too large | The file or piece is over the size limit. |
| 416 | Range not satisfiable | The Range header asks for bytes outside the file. |
| 500 | Server error | Something went wrong on our side. Try again in a moment. |
| 502 | Storage error | The storage provider could not deliver the file right now. |
Every file is split into pieces and each piece is encrypted on our server before it is sent to Telegram. The pieces are saved with random names, so the people who can see the Telegram chat only find unreadable data.
When someone opens your link, the pieces are fetched, checked and decrypted on the fly. If a piece was changed in storage, the download stops instead of serving damaged data. Files uploaded before encryption was added keep working as they are.
The admin dashboard has its own sign in page at /admin. For scripts, the same routes also accept HTTP Basic credentials built from the USERNAME and PASSWORD environment variables.
/admin Needs sign inBrowse, preview, copy and delete everything that has been uploaded.
/admin/api/media?page=1&limit=100 Needs sign in/delete-images Needs sign incurl -X POST https://anya-file-host.vercel.app/delete-images \
-u admin:YOUR_PASSWORD \
-H "Content-Type: application/json" \
-d '["https://anya-file-host.vercel.app/1753020712833.png"]'POST /upload for small files and /hosturl for files that already exist online.ENABLE_AUTH, uploads need the admin credentials too.