User API Reference
Complete REST API reference for Emberly users. Upload files, manage domains, authenticate, and integrate with your applications.
The Emberly REST API lets you programmatically upload files, manage sharing settings, configure domains, and build custom integrations.
Authentication
Bearer Token
All user endpoints require authentication via Bearer token:
Get your token:
- Log in to embrly.ca
- Go to Settings → Profile → Upload Token
- Copy the token
- Use it for all API requests
Session Cookies
Some endpoints (domains, profile) also accept browser session cookies for OAuth flows and dashboard access.
Base URL
For self-hosted instances, replace embrly.ca with your domain.
Response Format
All responses are JSON with this structure:
Success (2xx):
Error (4xx/5xx):
Rate Limits
Rate limits vary by plan:
| Plan | Requests/min | Requests/hour |
|---|---|---|
| Spark | 60 | 1,000 |
| Glow+ | 300 | 5,000 |
| Inferno+ | 1,000 | 10,000 |
| Enterprise | Unlimited | Unlimited |
Rate limit headers in response:
Exceeding limits returns 429 Too Many Requests.
HTTP Status Codes
| Code | Meaning | When |
|---|---|---|
| 200 | Success | Request completed |
| 201 | Created | Resource created (file upload) |
| 400 | Bad Request | Invalid parameters |
| 401 | Unauthorized | Missing or invalid token |
| 403 | Forbidden | Plan limit exceeded or permission denied |
| 404 | Not Found | Resource doesn't exist |
| 409 | Conflict | Resource already exists |
| 413 | Payload Too Large | File exceeds size limit |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Server Error | Internal server error |
File Management
Upload a File
POST /api/files
Upload a single file.
Content-Type: multipart/form-data
Request:
| Field | Type | Required | Description |
|---|---|---|---|
file | File | Yes | The file to upload |
visibility | string | No | PUBLIC or PRIVATE (default: PUBLIC) |
password | string | No | Password to download the file |
domain | string | No | Custom domain for the URL |
expiresAt | ISO 8601 | No | Auto-delete date (must be future) |
allowSuggestions | boolean | No | Allow edit suggestions |
cURL:
JavaScript:
Response (201):
Errors:
400— Invalid parameters or file too large401— Invalid token403— Storage quota exceeded or plan limit409— File name already exists
Update File
PATCH /api/files/[id]
Update a file's visibility, password, or expiration.
Request Body:
Response (200):
Delete a File
DELETE /api/files/[id]
Permanently delete a file (cannot be recovered).
Response (200):
Download a File
GET /api/files/[id]/download
Download a file. If password-protected, include password in query string.
Query Parameters:
password(optional) — Password if file is protected
Response: File binary data (200) or error
List Your Files
GET /api/files
List all your uploaded files with pagination and filtering.
Auth: Session cookie or Bearer token
Query Parameters:
page(default: 1) — Page numberlimit(default: 24) — Results per pagesearch— Search by filename or OCR textsortBy—newest(default),oldest,largestvisibility— Comma-separated:PUBLIC,PRIVATE,hasPasswordtypes— Comma-separated MIME types (e.g.image/png,image/jpeg)dateFrom— ISO date filter (uploaded on or after)dateTo— ISO date filter (uploaded on or before)squadId— Filter files belonging to a specific squad
Response (200):
Chunked Upload (Large Files)
For files larger than your single upload limit, use chunked upload.
Step 1: Initialize
POST /api/files/chunks
Response (200):
Step 2: Get Presigned URLs
GET /api/files/chunks/[uploadId]/part/[n]
Returns a temporary S3 URL for uploading chunk N.
Step 3: Upload Each Chunk
Step 4: Complete Upload
POST /api/files/chunks/[uploadId]/complete
Returns the completed file object (same as single upload).
File Collaborators
GET /api/files/[id]/collaborators
List users with access to the file.
Response (200):
POST /api/files/[id]/collaborators
Add a collaborator to a file.
Request Body:
Response (201):
DELETE /api/files/[id]/collaborators/[collaboratorId]
Remove a collaborator.
Response (200):
OCR / Text Extraction
GET /api/files/[id]/ocr
Get extracted text from an image file.
Response (200):
Custom Domains
List Domains
GET /api/domains
List all your custom domains and domain slot usage.
Response (200):
Add a Domain
POST /api/domains
Add a new custom domain.
Request Body:
Response (200):
Errors:
400— Invalid domain format403— Domain slot limit reached409— Domain already registered
Verify Domain
POST /api/domains/[id]/cf-check
Trigger DNS verification and Cloudflare provisioning.
Response (202):
Response (200) once complete:
Errors:
409— CNAME not found or incorrect
Set Primary Domain
PATCH /api/domains/[id]
Set as the primary domain for new uploads.
Request Body:
Response (200): Updated domain object
Delete Domain
DELETE /api/domains/[id]
Remove a custom domain.
Response (200):
User Profile
Get Profile
GET /api/profile
Get authenticated user's profile information.
Response (200):
Update Profile
PATCH /api/profile
Update your profile information.
Request Body:
Response (200): Updated profile object
Upload Avatar
POST /api/profile/avatar
Upload a new profile avatar.
Content-Type: multipart/form-data
Fields:
file(File) — Image file (max 5 MB, JPEG/PNG/GIF)
Response (200):
Change Password
Password changes are made via PATCH /api/profile with the password fields.
PATCH /api/profile
Request Body:
Response (200): Updated profile object
Errors:
400— Current password incorrect, new password too weak, or reuse of a recent password
Two-Factor Authentication
2FA endpoints require a session cookie (browser session), not a bearer token.
GET /api/profile/2fa
Generate a TOTP secret and QR code URI to set up an authenticator app.
Response (200):
POST /api/profile/2fa with { "step": "send-code" }
Validate a TOTP token and send an email confirmation code.
Request Body:
POST /api/profile/2fa with { "step": "verify-code" }
Confirm the email code to complete 2FA enrollment.
Request Body:
Response (200):
DELETE /api/profile/2fa
Disable two-factor authentication (requires email verification).
Request Body:
Response (200):
Teams & Squads (Nexium)
List Your Squads
GET /api/discovery/squads?mine=true
List all squads you're a member of.
Query Parameters:
page(default: 1)limit(default: 20)
Response (200):
Create a Squad
POST /api/discovery/squads
Create a new squad/team.
Request Body:
Response (201):
Invite Member to Squad
POST /api/discovery/squads/[id]/invites
Invite someone to join the squad.
Request Body:
Response (201):
Get Squad Members
GET /api/discovery/squads/[id]/members
List all members of a squad.
Response (200):
Shortened URLs
Create Short URL
POST /api/shorturl
Create a shortened URL.
Request Body:
Response (201):
List Short URLs
GET /api/shorturl
List all your shortened URLs.
Response (200):
Delete Short URL
DELETE /api/shorturl/[id]
Delete a shortened URL.
Response (200):
Error Handling
All error responses follow this format:
Common Error Codes:
| Code | HTTP | Meaning |
|---|---|---|
INVALID_TOKEN | 401 | Token missing, invalid, or expired |
UNAUTHORIZED | 403 | You don't have permission |
QUOTA_EXCEEDED | 403 | Storage quota exceeded |
PLAN_LIMIT | 403 | Feature not available on your plan |
NOT_FOUND | 404 | Resource doesn't exist |
DUPLICATE | 409 | Resource already exists |
RATE_LIMITED | 429 | Too many requests |
SERVER_ERROR | 500 | Internal server error |
Integration Examples
Upload from Node.js
Upload from Python
Retrieve File Analytics
Rate Limit Best Practices
- Check rate limit headers before and after requests
- Implement exponential backoff — wait before retrying on 429
- Batch operations — upload multiple files in parallel (within limits)
- Cache results — don't repeatedly query the same files
- Monitor usage — track API requests to avoid hitting limits
Support
- API Issues? Contact [email protected]
- Found a bug? Create an issue on GitHub
- Need help? Join our Discord