Your uploads. Your workflow.
Upload videos and files from your own scripts, desktop tools, or server. File data goes straight to Cloudflare R2 using temporary upload URLs. Fileroy verifies the uploaded file and returns its real download link.
Start with one command
Download the Python uploader above. It requires Python 3.9+ and has no extra dependencies. It splits large files into parts, uploads up to three parts in parallel, retries temporary failures, and prints real completion progress and your download link.
Visitor upload
python fileroy-upload.py video.mp4
No account or API key is required for visitor uploads. Visitor plan rules and expiry apply. Guest files do not appear in a user account and do not earn account download rewards.
Upload to your account
Sign in, open Settings → API keys, and create a key using your account password. Existing email verification and two-factor authentication checks apply. Set FILEROY_API_KEY in your environment before running the uploader.
# macOS / Linux
export FILEROY_API_KEY='YOUR_PRIVATE_KEY'
python fileroy-upload.py video.mp4
# PowerShell
$env:FILEROY_API_KEY = 'YOUR_PRIVATE_KEY'
python fileroy-upload.py video.mp4
Press Ctrl+C to cancel an unfinished upload. Account uploads appear in My Files and keep the existing download and eligible earnings rules.
Authentication
Base URL: https://fileroy.com/api/v1
For account uploads, send your key in the Authorization header on every API request. Keys expire and can be revoked in Settings → API keys. Only a hash is stored; the full key is shown once.
Authorization: Bearer YOUR_PRIVATE_KEY
Accept: application/json
Content-Type: application/json
For guest uploads, omit Authorization when starting. The response contains a private upload_token. Send it as X-Upload-Token on subsequent controls for that upload. A guest token is restricted to its upload and the same public IP address; changing IP can prevent completion.
The upload workflow
- Read /limits to check the current plan and file rules.
- Start an upload with the filename and exact byte size.
- Request part URLs. Use the part_size returned by Fileroy; the last part may be smaller.
- PUT each exact file slice to its signed URL. Keep all signed query parameters intact. Do not send your Fileroy key to R2.
- Complete the upload. Fileroy checks the actual parts, stored byte size, ownership, and quotas before saving file metadata.
1. Start
curl -X POST 'https://fileroy.com/api/v1/uploads' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_PRIVATE_KEY' \
-d '{"original_name":"video.mp4","name":"My video","size":12345678}'
{"id":"UPLOAD_ID","part_size":16777216,"parts":1,"expires_at":"..."}
This is an example response. Always use the actual returned part_size, parts, and expiry. Guest responses also include upload_token.
2. Request URLs and upload bytes
POST /api/v1/uploads/UPLOAD_ID/parts
{"parts":[1]}
{"urls":[{"part":1,"url":"SIGNED_R2_URL","size":12345678}],"expires_in":600}
curl --request PUT --upload-file part-1.bin 'SIGNED_R2_URL'
Request up to 12 distinct part numbers at a time. Each R2 PUT must have Content-Length equal to that part’s returned size. The included Python uploader handles slicing and headers. URLs expire; request a new URL before retrying an expired one.
3. Finish
POST /api/v1/uploads/UPLOAD_ID/complete
{}
{"type":"success","shared_id":"...","download_link":"https://fileroy.com/SHARED_ID/file"}
Use the actual download_link from the response. Repeating completion returns the same result. Downloads keep the existing visibility, password, permissions, and page flow.
Endpoints
| Method | Path | Purpose |
|---|---|---|
| GET | /limits | Current visitor or authenticated plan limits; use Authorization for your account. |
| POST | /uploads | Reserve quota and start a multipart upload. |
| POST | /uploads/{id}/parts | Get temporary URLs for the requested parts. |
| POST | /uploads/{id}/complete | Verify the stored file and save metadata. Send {}. |
| GET | /uploads/{id} | Read upload state and the download link after completion. |
| DELETE | /uploads/{id} | Cancel an unfinished upload. Send {}. Completed files are never deleted by this endpoint. |
Start request fields
original_name: required filename including extension, maximum 255 characters; no path separators.name: required display name, maximum 255 characters.size: required positive integer, exact file size in bytes.visibility: optional boolean; true by default. Guest uploads are always public.password: optional file password, maximum 255 characters.description: optional text, maximum 1000 characters.folder: optional existing folder ID belonging to your account. Guests cannot use folders.
Limits & error handling
There is no separate API upload allowance. Website and API uploads share your current plan’s maximum file size, storage space, accepted extensions, expiry, and active-upload limit. Pending uploads reserve storage too. API uploads currently require the existing direct R2 storage provider to be enabled.
Control requests accept JSON only, up to 16 KiB. File bytes must go to the signed R2 URL. The API allows up to 60 control requests per minute per source IP, and up to 10 starts per minute. A 429 response includes Retry-After. Keep requests below these limits and retry with backoff.
| Status | Meaning |
|---|---|
| 401 / 403 | Invalid, expired, or revoked key; missing guest token; blocked, unverified, or incomplete account; uploads disabled for the plan. |
| 404 | Unknown upload or ownership check failed. No other user’s upload details are revealed. |
| 409 | Expired, cancelled, or already finished upload, or direct storage unavailable. |
| 413 / 415 | Control body too large or not JSON. |
| 422 | Invalid metadata, incomplete upload, rejected file type, size mismatch, plan limit, or insufficient storage. Read message and errors. |
| 429 / 503 | Rate limit or temporary provider failure. Retry later with backoff. |
Check expires_at when resuming a saved upload. Unfinished uploads expire and are cleaned up automatically. Cancel when abandoning a file to release its reserved storage. The API never accepts arbitrary remote URLs to fetch files.