Uploading files
POST /api/v1/files uploads one file with multipart/form-data. The public
endpoint currently accepts files up to 100 MiB (104,857,600 bytes) and
requires the upload scope plus the key owner's files.upload permission.
Request
| Field | Required | Description |
|---|---|---|
file | yes | The file. Its filename and content type come from this part. |
collection_id | no | UUID of a collection to add the file to. An inaccessible or missing collection leaves the file in the library only. |
Content-Length is required. Accepted content types are image/*, video/*,
application/pdf, text/plain, and application/octet-stream. Known image
extensions—including common RAW formats, PSD, TIFF, HEIC, and HEIF—are
normalized from the filename before validation.
curl --fail-with-body \
-X POST "$NEW_ARCHIVE_URL/api/v1/files" \
-H "Authorization: Bearer $NEW_ARCHIVE_API_KEY" \
-F "file=@sunset.jpg" \
-F "collection_id=0198c9a0-7d3e-7c41-b7a2-3f9d1c2e4a5b"Upload response
A successful upload returns 202 Accepted. The bytes are stored, but
background processing may still be creating thumbnails and searchable data.
{
"id": "0198c9a0-7d3e-7c41-b7a2-3f9d1c2e4a5b",
"version_id": "0198c9a0-8a12-7e55-9c01-d4b6f7a8e9c0",
"status": "processing"
}Uploads over the limit return 413 payload_too_large. A full team storage
quota returns 507 quota_exceeded.
Read status and metadata
GET /api/v1/files/:fileId accepts either the upload or search scope.
Use it to poll an upload or retrieve one search result in detail.
curl --fail-with-body \
"$NEW_ARCHIVE_URL/api/v1/files/0198c9a0-7d3e-7c41-b7a2-3f9d1c2e4a5b" \
-H "Authorization: Bearer $NEW_ARCHIVE_API_KEY"{
"id": "0198c9a0-7d3e-7c41-b7a2-3f9d1c2e4a5b",
"status": "ready",
"original_name": "sunset.jpg",
"mime_type": "image/jpeg",
"size_bytes": 2481931,
"width": 4000,
"height": 3000,
"taken_at": "2026-06-30T18:41:02.000Z",
"created_at": "2026-07-11T09:12:44.201Z",
"thumbnail_url": "https://storage.example/signed-thumbnail"
}| Field | Type | Description |
|---|---|---|
id | string | Stable file UUID. |
status | string | processing, ready, or failed. |
original_name | string | Filename supplied at upload. |
mime_type | string | Normalized content type. |
size_bytes | number | Stored original size in bytes. |
width | number or null | Pixel width when available. |
height | number or null | Pixel height when available. |
taken_at | string or null | Capture time from metadata, as ISO 8601. |
created_at | string | Upload time, as ISO 8601. |
thumbnail_url | string or null | Signed thumbnail URL for ready files, valid for about one hour. |
An unknown file ID—or one belonging to another team—returns 404 not_found.
Original-file downloads are not part of API v1.
Poll until processing finishes
Poll no faster than once per second because each request counts toward the key's rate limit.
async function waitForFile(fileId) {
const url = `${process.env.NEW_ARCHIVE_URL}/api/v1/files/${fileId}`
for (;;) {
const response = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.NEW_ARCHIVE_API_KEY}` },
})
const body = await response.json()
if (!response.ok)
throw new Error(`${body.error.code}: ${body.error.message}`)
if (body.status !== "processing") return body
await new Promise((resolve) => setTimeout(resolve, 2000))
}
}