Programmatic file transfers with enterprise-grade encryption. Upload, share, and track — all through a simple REST interface.
# 1. Create an upload
curl -X POST https://api.transfilio.com/api/v1/uploads \
-H "Authorization: Bearer zs_live_..." \
-H "Content-Type: application/json" \
-d '{"file_name":"document.pdf","file_size":2048576,"file_type":"application/pdf"}'
# Response
{
"upload_id": "b7c9...",
"status": "pending",
"upload_url": "/api/v1/uploads/b7c9.../content",
"completion_url": "/api/v1/uploads/b7c9.../complete"
}
# 2. PUT the exact file bytes to upload_url
curl -X PUT https://api.transfilio.com/api/v1/uploads/b7c9.../content \
-H "Authorization: Bearer zs_live_..." \
-H "Content-Type: application/octet-stream" \
--data-binary @document.pdf
Get up and running in under 5 minutes
Sign up, go to Settings → API Keys, and generate a new key.
Add your key to the
Authorization
header as a Bearer token.
Upload files, create share links, and track downloads — all via the API.
# Check API status
curl https://api.transfilio.com/api/health
# 1. Create an upload
curl -X POST https://api.transfilio.com/api/v1/uploads \
-H "Authorization: Bearer zs_live_..." \
-H "Content-Type: application/json" \
-d '{"file_name":"report.pdf","file_size":2048576,"file_type":"application/pdf"}'
# 2. Send the exact bytes to the returned upload_url
curl -X PUT https://api.transfilio.com/api/v1/uploads/b7c9.../content \
-H "Authorization: Bearer zs_live_..." \
-H "Content-Type: application/octet-stream" \
--data-binary @report.pdf
# 3. Verify completion (required for signed S3 upload URLs)
curl -X POST https://api.transfilio.com/api/v1/uploads/b7c9.../complete \
-H "Authorization: Bearer zs_live_..."
# 4. Create a share link
curl -X POST https://api.transfilio.com/api/v1/links \
-H "Authorization: Bearer zs_live_..." \
-H "Content-Type: application/json" \
-d '{"transfer_id": "b7c9..."}'
All API requests require authentication via an API key
# In your request headers
Authorization: Bearer zs_live_a1b2c3d4e5f6
Standard OAuth-style Bearer token. Works with all HTTP clients.
# Custom header alternative
X-API-Key: zs_live_a1b2c3d4e5f6
Useful when Authorization header is already in use.
Security:
Never expose keys in client-side code.
Use environment variables and rotate regularly via
POST /api/v1/api-keys/:id/rotate
(24h grace period).
Common settings for all API requests
https://api.transfilio.com
| Header | Value | Required |
|---|---|---|
| Authorization | Bearer {api_key} | Yes |
| Content-Type | application/json | Yes |
| Accept | application/json | Optional |
List endpoints return cursor-paginated results. Use
limit
and
after
query parameters. Pass the previous response's
next_cursor
as the next
after
value.
# Response includes pagination metadata
{
"data": [...],
"pagination": {
"limit": 20,
"has_more": true,
"next_cursor": "b7c9..."
}
}
Complete endpoint reference with request and response details
/api/health
Public
Returns API health status. No authentication required.
Response
/api/v1/uploads
Auth required
Create an upload and receive a server-controlled PUT destination. The destination is an authenticated API path for local storage or a short-lived signed URL for S3.
Parameters
file_name
string
required
—
Original file name
file_size
integer
required
—
File size in bytes
file_type
string
—
MIME type
metadata
object
—
Application-defined metadata
workspace_id
string
—
Accepted workspace destination. Owners, admins, and members may upload; viewers are read-only.
Response
/api/v1/uploads/:id/content
Auth required
Send the raw file bytes with Content-Type application/octet-stream. This authenticated endpoint streams the request to storage, requires the exact declared file size, records a SHA-256 hash, and completes the upload. A mismatch returns PAYLOAD_SIZE_MISMATCH. Use the returned signed upload_url directly instead when storage_provider is s3.
Response
/api/v1/uploads/:id/complete
Auth required
Verify the stored object's size and complete a direct-to-storage upload. It is safe to repeat this request; usage and completion events are recorded only once.
Response
/api/v1/uploads/:id
Auth required
Retrieve upload status, progress, hashes, and storage metadata.
Response
/api/v1/uploads/:id
Auth required
Update mutable upload metadata or mark an upload as uploading, failed, or cancelled. File locations are server controlled, and completed status is accepted only by the verified completion endpoint.
Parameters
status
string
—
uploading, failed, or cancelled
metadata
object
—
Application-defined metadata
error_message
string
—
Failure detail when status is failed
Response
/api/v1/uploads/:id
Auth required
Permanently delete an upload and all associated share links.
Response
/api/v1/links
Auth required
Create a secure share link with optional password, expiry, and download limit.
Parameters
transfer_id
string
required
—
ID of the upload session to share
password
string
—
Optional password protection
expires_in
integer
—
Seconds until expiry
max_downloads
integer
—
Maximum download count
Response
/api/v1/links/:id
Auth required
Retrieve share link details including download statistics.
Response
/api/v1/links/:id
Auth required
Revoke a share link immediately.
Response
/api/v1/transfers
Auth required
List your personal transfers, or pass workspace_id to list transfers visible to an accepted workspace member.
Parameters
limit
integer
—
Results per page (1–100, default: 20)
after
string
—
Transfer ID cursor returned by the previous page
workspace_id
string
—
Workspace whose shared transfers should be listed
Response
/api/v1/usage
Auth required
Get current billing period usage including storage, transfers, and API calls.
Response
/api/v1/api-keys/:id/rotate
Auth required
Rotate an API key with a 24-hour grace period for the old key.
Parameters
grace_period_hours
integer
—
Grace period (default: 24, max: 72)
Response
Per-key hourly protection and plan-based monthly allowances
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 994
X-RateLimit-Reset: 1710345600
X-Api-Monthly-Limit: 1000
X-Api-Monthly-Remaining: 108
X-Api-Monthly-Reset: 1711929600
# When rate limited (HTTP 429):
Retry-After: 42 (seconds)
| Plan | Calls/Month | Max File |
|---|---|---|
| Free | Not included | 2 GB |
| Pro | 1,000 | 5 GB |
| Team | 10,000 | 20 GB |
| Business | 100,000 | 50 GB |
| Enterprise | 1,000,000 | 100 GB |
Each accepted request consumes the monthly plan allowance. Per-key hourly
protection also applies. When either limit is exhausted, the API returns
429 Too Many Requests
with a
Retry-After
header indicating seconds to wait.
Business and Enterprise accounts can receive signed transfer and share lifecycle events
transfer.created
transfer.completed
transfer.failed
transfer.deleted
share.created
share.accessed
share.expired
POST https://your-app.com/webhooks/transfilio
X-Transfilio-Signature: sha256=a1b2c3...
X-Transfilio-Event: transfer.completed
{
"id": "83d30a54-...",
"file_name": "report.pdf",
"file_size": 1048576,
"file_type": "application/pdf",
"status": "completed",
"sha256_hash": "8f4343...",
"occurred_at": "2026-07-13T14:30:00Z"
}
All payloads are signed with HMAC-SHA256. Verify the
X-Transfilio-Signature
header to ensure authenticity.
import hmac, hashlib
def verify_signature(payload, signature, secret):
expected = hmac.new(
secret.encode(),
payload.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(
f"sha256={expected}", signature
)
Consistent JSON error responses with actionable codes
{
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or missing API key",
"details": []
}
}
Use any standard HTTP client — no proprietary SDK is required
import os, requests
base_url = "https://api.transfilio.com"
headers = {"Authorization": "Bearer zs_live_..."}
file_path = "report.pdf"
upload = requests.post(
base_url + "/api/v1/uploads",
headers=headers,
json={"file_name": os.path.basename(file_path), "file_size": os.path.getsize(file_path)}
).json()
upload_url = upload["upload_url"]
put_headers = {"Content-Type": "application/octet-stream"}
if upload_url.startswith("/"):
upload_url = base_url + upload_url
put_headers.update(headers)
with open(file_path, "rb") as file:
requests.put(upload_url, headers=put_headers, data=file).raise_for_status()
requests.post(base_url + upload["completion_url"], headers=headers).raise_for_status()
link = requests.post(
base_url + "/api/v1/links",
headers=headers,
json={"transfer_id": upload["upload_id"], "max_downloads": 10}
).json()
print(link["token"])
Get your API key and start building in minutes. API access starts on Pro with 1,000 calls per month.