IzFiles API Guide
Upload files, share and embed them, and manage them from your own code or website. Every endpoint answers with JSON.
https://izfiles.comX-API-KEY headerAuthentication
Every API call needs your API key. Send it in a header:
X-API-KEY: YOUR_API_KEYAuthorization: Bearer YOUR_API_KEYworks too.- Upload and note calls also accept the key as a
tokenform field. - A key in the query string (
?token=...) is refused with400, so it never ends up in logs. - Files are saved to the account the key belongs to, and count toward its storage limit.
Quick start
Upload a file and read the link from the answer:
curl -H "X-API-KEY: YOUR_API_KEY" \
-F "file=@/path/to/report.pdf" \
https://izfiles.com/api/upload{
"success": true,
"file_hash": "abc123def456",
"filename": "report.pdf",
"privacy": "private",
"share_url": "https://izfiles.com/share/abc123def456",
"download_url": "https://izfiles.com/download/abc123def456",
"access": "link"
}Keep the file_hash: every other call uses it.
Limits and quotas
| What | Limit |
|---|---|
| One upload request | About 95 MB. Cloudflare answers 413 above 100 MB; use chunked upload for bigger files. |
| File size | Set by your account group (for example 10 GB per file), and the file counts toward your storage. |
/api/upload and chunked start | 1000 per hour per IP address |
| Chunk, status, cancel and complete calls | 300 per minute and 20000 per hour per IP address |
| File info and list | 300 per hour per IP address |
| Signed links | 2000 per hour per IP address |
| Delete | 1000 per hour per IP address |
| Notes | 60 per hour per IP address |
/direct links | 500 per minute per viewer IP address |
| Downloads | Daily request and bandwidth quotas per viewer IP address |
The administrator can change these limits. A call over a limit answers 429; responses carry X-RateLimit-* headers.
Upload a file
/api/uploadmultipart/form-data| Field | Required | Description |
|---|---|---|
file | Yes | The file to upload. |
privacy | No | private (default): not listed on Explore, anyone with the link can open it. public: listed on Explore. |
access | No | link (default) or signed. signed needs privacy=private; see signed links. |
expires_in | No | never (default), 1d, 7d or 30d. After that time the file can no longer be opened. |
token | No | Your API key, if you do not send the X-API-KEY header. |
curl -H "X-API-KEY: YOUR_API_KEY" \
-F "file=@/path/to/photo.png" \
-F "privacy=public" \
https://izfiles.com/api/uploadimport requests
with open("photo.png", "rb") as handle:
response = requests.post(
"https://izfiles.com/api/upload",
headers={"X-API-KEY": "YOUR_API_KEY"},
files={"file": handle},
data={"privacy": "public"},
)
result = response.json()
print(result["share_url"])
if result.get("direct_url"):
print("Embed:", result["direct_url"])const fs = require('fs');
async function uploadFile(path) {
const form = new FormData(); // Node.js 18 or later
form.append('file', new Blob([fs.readFileSync(path)]), require('path').basename(path));
const response = await fetch('https://izfiles.com/api/upload', {
method: 'POST',
headers: { 'X-API-KEY': process.env.IZFILES_API_KEY },
body: form,
});
return response.json(); // file_hash, share_url, download_url ...
}direct_url for public images and videos. Private ones can be embedded too: build the link yourself as https://izfiles.com/direct/<file_hash>. See direct links.Upload from a URL
The server downloads the file for you. Same endpoint, with url instead of file.
/api/uploadmultipart/form-data| Field | Required | Description |
|---|---|---|
url | Yes | Public http or https link to the file. Private network addresses are refused. |
privacy, access, expires_in | No | As in Upload a file. |
curl -H "X-API-KEY: YOUR_API_KEY" \
-F "url=https://example.com/file.pdf" \
https://izfiles.com/api/uploadThe download must finish within 5 minutes and stay under your file size limit. The answer also has source_url.
Large files in chunks
For files over about 95 MB: start an upload, send the file in pieces, then complete it. The result is the same as /api/upload, and the same size and storage limits apply.
-
Start
POST/api/upload/chunked/startform or JSONField Required Description filenameYes Name of the file. file_sizeYes Size in bytes. chunk_sizeNo Bytes per chunk, 1 MB to 90 MB. Default 50 MB (52428800). privacy,access,expires_inNo As in Upload a file. { "success": true, "upload_id": "3f2a9c...", "chunk_size": 52428800, "total_chunks": 12, "received_chunks": 0, "missing_chunks": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], "expires_at": "2026-11-13T10:00:00Z" } -
Send every chunk
PUT/api/upload/chunked/<upload_id>/<index>multipart, fieldfileIndexes run from
0tototal_chunks - 1. Every chunk is exactlychunk_sizebytes, except the last one, which is the rest of the file.Send each chunk asmultipart/form-datain the fieldfile. A rawapplication/octet-streambody is blocked by the web application firewall. -
Complete
POST/api/upload/chunked/<upload_id>/completeoptionalmd5Send the
md5of the whole file to have it checked. The answer has the same fields as/api/upload, plusupload_idandmd5.
# 1. Start
curl -X POST -H "X-API-KEY: YOUR_API_KEY" \
-F "filename=big.zip" -F "file_size=629145600" \
https://izfiles.com/api/upload/chunked/start
# 2. Split into 50 MB parts and send each one (index 0, 1, 2 ...)
split -b 52428800 -d -a 4 big.zip part_
curl -X PUT -H "X-API-KEY: YOUR_API_KEY" -F "file=@part_0000" \
https://izfiles.com/api/upload/chunked/UPLOAD_ID/0
# 3. Complete
curl -X POST -H "X-API-KEY: YOUR_API_KEY" -F "md5=$(md5sum big.zip | cut -d' ' -f1)" \
https://izfiles.com/api/upload/chunked/UPLOAD_ID/completeconst fs = require('fs');
const path = require('path');
const axios = require('axios');
async function uploadLarge(filePath, options = {}) {
const api = axios.create({
baseURL: 'https://izfiles.com/api/upload/chunked',
headers: { 'X-API-KEY': process.env.IZFILES_API_KEY },
maxBodyLength: Infinity,
});
const fileSize = fs.statSync(filePath).size;
const { data: start } = await api.post('/start', {
filename: path.basename(filePath), file_size: fileSize, ...options,
});
const handle = await fs.promises.open(filePath, 'r');
try {
for (let index = 0; index < start.total_chunks; index++) {
const length = Math.min(start.chunk_size, fileSize - index * start.chunk_size);
const chunk = Buffer.alloc(length);
await handle.read(chunk, 0, length, index * start.chunk_size);
for (let attempt = 1; ; attempt++) {
try {
const form = new FormData(); // Node.js 18 or later
form.append('file', new Blob([chunk]), `part_${index}`);
await api.put(`/${start.upload_id}/${index}`, form);
break;
} catch (err) {
if (attempt === 3 || err.response?.status === 400) throw err;
}
}
}
} finally {
await handle.close();
}
const { data } = await api.post(`/${start.upload_id}/complete`);
return data; // same fields as /api/upload
}Good to know
- Chunks can be sent in any order and in parallel. Sending a chunk again replaces it, so retry a failed chunk as it is.
GET /api/upload/chunked/<upload_id>listsmissing_chunks, so an interrupted upload can resume. After completion it returns the result.- Calling complete again returns the same file, not a second copy. If complete answers
400withmissing_chunks, send those and complete again. DELETE /api/upload/chunked/<upload_id>cancels an unfinished upload. Uploads are removed 24 hours after start.- An account can have up to 50 unfinished uploads at once.
Get file info
/api/file/<file_hash>curl -H "X-API-KEY: YOUR_API_KEY" https://izfiles.com/api/file/abc123def456{
"success": true,
"file_hash": "abc123def456",
"filename": "example.png",
"privacy": "public",
"access": "link",
"file_size": 1024000,
"file_type": "png",
"is_image": true,
"upload_date": "2026-11-12T10:30:00",
"download_count": 5,
"has_password": false,
"share_url": "https://izfiles.com/share/abc123def456",
"direct_url": "https://izfiles.com/direct/abc123def456"
}For a signed file, only its owner or an admin gets an answer, and the plain links are left out.
List files
/api/files| Query | Default | Description |
|---|---|---|
page | 1 | Page number. |
per_page | 20 | Items per page, at most 100. |
privacy | all | all, public or private. |
type | all | all or image. |
curl -H "X-API-KEY: YOUR_API_KEY" \
"https://izfiles.com/api/files?privacy=public&type=image&per_page=10"{
"success": true,
"files": [
{ "file_hash": "abc123def456", "filename": "example.png", "privacy": "public", "access": "link", "...": "..." }
],
"pagination": { "page": 1, "per_page": 10, "total": 50, "pages": 5, "has_next": true, "has_prev": false }
}Delete a file
/api/file/<file_hash>curl -X DELETE -H "X-API-KEY: YOUR_API_KEY" https://izfiles.com/api/file/abc123def456async function deleteIzfile(fileHash) {
try {
await axios.delete(`https://izfiles.com/api/file/${fileHash}`, {
headers: { 'X-API-KEY': process.env.IZFILES_API_KEY },
});
} catch (err) {
// 404: already deleted, or not a file of this API key.
if (err.response?.status !== 404) throw err;
}
}{ "success": true, "file_hash": "abc123def456", "deleted": true }- Only the owner or an admin can delete a file. It cannot be undone, and every link stops working at once, signed links included.
404: the file does not exist or is not yours. When retrying a delete, treat it as already deleted.503withRetry-After: the server is busy and nothing was deleted. Retry later.
Download links
Each file has three links. No login or API key is needed to use them.
| Link | Use |
|---|---|
/share/<file_hash> | Web page with a preview and a download button. |
/download/<file_hash> | The file itself, as a download. |
/direct/<file_hash> | The file inline, for images and videos. See direct links. |
curl -L -o report.pdf https://izfiles.com/download/abc123def456
# Password-protected file
curl -L -H "X-File-Password: the-file-password" -o report.pdf \
https://izfiles.com/download/abc123def456private means unlisted, not secret: anyone with the link can download. For links that expire, use signed links. A file with a password always needs the X-File-Password header.Direct links (image, video)
/direct/<file_hash>Serves images and videos inline, so other websites can show them with <img>, <video> or CSS. It works for public and private files.
<img src="https://izfiles.com/direct/abc123def456" alt="Product photo">
<video src="https://izfiles.com/direct/def456abc123" controls preload="metadata"></video>.hero {
background: url('https://izfiles.com/direct/abc123def456') center / cover;
}How it behaves
| Public file | Private file | |
|---|---|---|
Show on another site (<img>, <video>) | Yes | Yes |
Read with fetch() from another site (CORS) | Yes (Access-Control-Allow-Origin: *) | No |
| Caching | public, max-age=86400 (browsers and CDNs, 1 day) | private, max-age=86400 (viewer's browser only, 1 day) |
Video seeking (Range) | Yes | Yes |
- Video answers
206 Partial ContenttoRangerequests, so players can start fast and seek. Each answer carries at most 8 MB; players ask for the next part by themselves. - Signed files need the
expiresandsigof a signed link; the cache then ends with the link. - Password-protected files need
X-File-Password, which an<img>tag cannot send, so they cannot be embedded. - Other file types answer
403. See supported formats.
Signed download links
For files that must not be downloadable with a plain link, such as paid content. Such a file opens only with a valid, expiring signed link, or for its owner or an admin who is logged in. Other files are not affected.
-
Upload with
access=signedcurl -H "X-API-KEY: YOUR_API_KEY" \ -F "file=@/path/to/course.pdf" \ -F "privacy=private" -F "access=signed" \ https://izfiles.com/api/uploadSave the
file_hash. The answer has"access": "signed"and no plain links. To change an existing file, see turn signed access on or off. -
Request a link when a visitor downloads
POST/api/file/<file_hash>/signed-urlJSON or formField Required Description expires_inNo Seconds the link stays valid, 60 to 604800. Default 3600. curl -X POST -H "X-API-KEY: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"expires_in": 300}' \ https://izfiles.com/api/file/abc123def456/signed-url{ "success": true, "file_hash": "abc123def456", "url": "https://izfiles.com/download/abc123def456?expires=1793927000&sig=...", "expires_at": 1793927000, "expires_in": 300, "access": "signed", "has_password": false }expires_atis a Unix time in seconds. The sameexpiresandsigalso work on/direct/<file_hash>. -
Send the visitor to the link
app.get('/orders/:id/download', requireLogin, async (req, res) => { const order = await findPaidOrder(req.user.id, req.params.id); if (!order) return res.sendStatus(403); const { data } = await axios.post( `https://izfiles.com/api/file/${order.fileHash}/signed-url`, { expires_in: 300 }, { headers: { 'X-API-KEY': process.env.IZFILES_API_KEY } }, ); res.set('Cache-Control', 'no-store'); res.redirect(302, data.url); });
Turn signed access on or off
/api/file/<file_hash>/accessJSON or formcurl -X POST -H "X-API-KEY: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"access": "signed"}' \
https://izfiles.com/api/file/abc123def456/accessaccessissignedorlink. Only the owner or an admin can change it.- Turning it on stops every plain link already given out; setting
linkmakes them work again. - A public file cannot be signed. Making a signed file public on its edit page turns signed access off.
- A signed link cannot be cancelled before it expires. A download already running is not cut off at expiry.
Create a note
/api/notesmultipart/form-data| Field | Required | Description |
|---|---|---|
title | Yes | Note title. |
body | One of the two | Note text. |
file | One of the two | A UTF-8 text file to use as the note text. |
type | No | content (default) or code. |
privacy | No | private (default) or public. |
curl -H "X-API-KEY: YOUR_API_KEY" \
-F "title=Release notes" \
-F "body=Version 2 is out." \
https://izfiles.com/api/notes
# Code note from a file
curl -H "X-API-KEY: YOUR_API_KEY" \
-F "title=Deploy script" -F "type=code" \
-F "file=@/path/to/deploy.sh" \
https://izfiles.com/api/notes{
"success": true,
"note_id": 123,
"title": "Release notes",
"type": "content",
"privacy": "private",
"created_at": "2026-11-12T10:30:00",
"share_url": "https://izfiles.com/view_shared_note/abc123..."
}Responses
A successful call answers with "success": true. Upload answers carry these fields:
| Field | Description |
|---|---|
file_hash | The file's ID in every other call and link. |
filename, file_size, file_type | Name, size in bytes and extension. |
privacy, access | As sent at upload. |
is_image | true for images. |
share_url, download_url | Plain links. Left out for signed files. |
direct_url | Only for public images and videos without a password. |
upload_date | UTC time, ISO 8601. |
A failed call answers with an HTTP error status and an error message:
{ "error": "Invalid token" }Errors
| Status | Meaning | What to do |
|---|---|---|
| 400 | A field is missing or not valid, a chunk has the wrong size, or the key was sent in the query string. | Read error and fix the request. |
| 401 | Missing token or Invalid token. | Check the API key. |
| 403 | Password needed, file type not allowed on /direct, or a signed link problem: signature_required, invalid_signature, link_expired. | Send X-File-Password, or request a new signed link. |
| 404 | The file or upload does not exist, or is not yours. | Check the file_hash. |
| 409 | A chunked upload is being completed. | Wait a few seconds and call complete again. |
| 413 | The request is over 100 MB. | Use chunked upload. |
| 429 | A rate limit or download quota was reached. | Slow down and retry later. |
| 503 | The server is busy ("retryable": true). | Retry after the seconds in Retry-After. |
| 507 | The server is low on space. | Retry later. |
Direct link formats
Any file type that is not blocked can be uploaded and downloaded. /direct serves these:
Images
JPG, JPEG, PNG, GIF, BMP, WebP, TIFF, ICO, HEIC
Videos
MP4, M4V, WebM, MOV, OGV
Script and web page files (for example PHP, JS, HTML, SVG, EXE) cannot be uploaded.
Troubleshooting
413 when uploading a big file
One request can carry about 95 MB. Use chunked upload.
A chunk answers 400 "blocked by the web application firewall"
The chunk was sent as a raw body. Send it as multipart/form-data in the field file.
401 on every call
The key is missing or wrong. Send it in the X-API-KEY header, not in the URL.
An embedded image does not show on my website
Check that the file is an image or video, has no password, and, if it is signed, that the link has not expired. Use /direct/<file_hash>, not /share.
fetch() of a private image fails with a CORS error
Only public files allow fetch() from other sites. Private files can still be shown with <img> and <video>, or fetched from your server.
429 Too Many Requests
A rate limit or download quota was reached. See limits, slow down and retry later.
Still stuck? Write to [email protected].