Fileverity API docs
Files
Upload images and documents to Fileverity: multipart for files up to 4 MB, pre-signed upload for files up to 50 MB. File object, limits and retention.
Images and documents are checked in two steps. First you upload the file with POST /files and receive a file ID. Then you create an analysis that references that ID. Text does not need a file; use Text detections.
Supported files
| Images (fvimg-2) | Documents (fvdoc-3) | |
|---|---|---|
| Formats | jpeg, png, webp | pdf, docx, doc, rtf, odt, pptx, txt |
| Maximum size | 10 MB | 50 MB |
| Other limits | 40 megapixels | 100 pages |
Two ways to upload
| Method | Request | Size | File status in the response |
|---|---|---|---|
| Multipart | multipart/form-data with the field file | Up to 4 MB | uploaded |
| Pre-signed URL | JSON with filename, content_type and bytes, then a PUT of the bytes | Up to 50 MB | pending until the bytes arrive |
Images are limited to the image size above whichever method you use.
Multipart upload
Send files up to 4 MB as multipart/form-data in the field file. The format is detected from the file contents, not from its name or declared type.
curl https://api.fileverity.com/v1/files \ -H "Authorization: Bearer $FILEVERITY_API_KEY" \ -F file=@claim-photo.jpgimport { readFile } from "node:fs/promises"; const form = new FormData();form.append("file", new Blob([await readFile("claim-photo.jpg")]), "claim-photo.jpg");const response = await fetch("https://api.fileverity.com/v1/files", { method: "POST", headers: { Authorization: "Bearer " + process.env.FILEVERITY_API_KEY }, body: form,});const file = await response.json();import osimport requests response = requests.post( "https://api.fileverity.com/v1/files", headers={"Authorization": "Bearer " + os.environ["FILEVERITY_API_KEY"]}, files={"file": open("claim-photo.jpg", "rb")},)file = response.json(){ "id": "file_8Hk2pQ4vN9sL1xR7cW3mY6zB", "object": "file", "filename": "claim-photo.jpg", "bytes": 2481920, "content_type": "image/jpeg", "status": "uploaded", "livemode": true, "created": 1793700000, "expires_at": 1793786400}Pre-signed upload
For files larger than 4 MB, up to 50 MB, ask for an upload URL, then send the bytes straight to storage.
POST /fileswith a JSON body:filename(optional),content_typeand the exact size inbytes. The response hasstatus: pendingand anuploadobject.PUTthe file bytes toupload.urlwith the headers inupload.headers, beforeupload.expires_at. NoAuthorizationheader is needed for this request.- Optionally poll
GET /files/{id}untilstatusisuploaded. - Create the analysis with the file ID.
FILE=$(curl -s https://api.fileverity.com/v1/files \ -H "Authorization: Bearer $FILEVERITY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"filename": "contract.pdf", "content_type": "application/pdf", "bytes": 18874368}') UPLOAD_URL=$(echo "$FILE" | jq -r .upload.url)FILE_ID=$(echo "$FILE" | jq -r .id) curl -X PUT "$UPLOAD_URL" \ -H "content-type: application/pdf" \ --data-binary @contract.pdfimport { readFile } from "node:fs/promises"; const bytes = await readFile("contract.pdf");const response = await fetch("https://api.fileverity.com/v1/files", { method: "POST", headers: { Authorization: "Bearer " + process.env.FILEVERITY_API_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ filename: "contract.pdf", content_type: "application/pdf", bytes: bytes.length }),});const file = await response.json(); await fetch(file.upload.url, { method: "PUT", headers: file.upload.headers, body: bytes });import osimport requests data = open("contract.pdf", "rb").read()response = requests.post( "https://api.fileverity.com/v1/files", headers={"Authorization": "Bearer " + os.environ["FILEVERITY_API_KEY"]}, json={"filename": "contract.pdf", "content_type": "application/pdf", "bytes": len(data)},)file = response.json() requests.put(file["upload"]["url"], headers=file["upload"]["headers"], data=data){ "id": "file_Pw3mY6zB8Hk2pQ4vN9sL1xR7", "object": "file", "filename": "contract.pdf", "bytes": 18874368, "content_type": "application/pdf", "status": "pending", "livemode": true, "created": 1793700000, "expires_at": 1793786400, "upload": { "url": "https://example.private.blob.vercel-storage.com/f/0d6f1c2e-7b1a-4c53-9a0e-5e8a41b3c7d2?signature=...", "method": "PUT", "headers": { "content-type": "application/pdf" }, "expires_at": 1793703600 }}The URL shown is shortened. Use the one in your response as it is. Send the content-type header exactly as returned in upload.headers.
Accepted content types
| Kind | Content types |
|---|---|
| Images | image/jpeg, image/png, image/webp |
| Documents | application/pdf, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/msword, application/rtf, application/vnd.oasis.opendocument.text, application/vnd.openxmlformats-officedocument.presentationml.presentation, text/plain |
The file object
| Field | Type | Description |
|---|---|---|
id | string | File ID, prefixed file_. |
object | string | Always file. |
filename | string or null | Sanitized file name. |
bytes | integer or null | Size in bytes. |
content_type | string | Detected or declared content type. |
status | string | pending until uploaded bytes have arrived, then uploaded. |
livemode | boolean | false for files created with a test key. Files are separated by mode. |
created | integer | Unix timestamp. |
expires_at | integer | Unix timestamp after which the file is deleted. |
upload | object | Only on the response to a JSON upload request: url, method (PUT), headers and expires_at. |
Retention
- A file is kept for at most 24 hours (see
expires_at) and then deleted. - A file is deleted as soon as an analysis starts from it. Each uploaded file is used by one analysis; upload it again to check it again.
DELETE /files/{id}removes a file and its stored bytes earlier.- A workspace can hold up to 50 unexpired files and 200 MB per mode at once. Beyond that, uploads fail with
pending_files_limit(HTTP 429, noretry-after) until files are used, deleted or expire. - A file ID that is unknown, expired or already used returns
file_not_foundwhen you create an analysis, andnot_foundwhen you retrieve or delete it.file_not_foundis also returned when the upload toupload.urlhas not finished yet, so wait untilGET /files/{id}showsuploaded.
Errors
| Code | Status | Cause |
|---|---|---|
missing_parameter | 400 | No file field in the multipart body. |
unsupported_media_type | 415 | The body is neither multipart/form-data nor JSON. |
unsupported_file | 415 | The format is not supported. |
invalid_file | 422 | The file is empty, damaged or cannot be read. |
file_too_large | 413 | A multipart file larger than 4 MB, or bytes above 50 MB. |
invalid_parameter | 400 | A JSON field is missing, mistyped or unknown. |
invalid_json | 400 | The JSON body is not an object. |
pending_files_limit | 429 | Too many unexpired uploads in the workspace. Not sent with a retry-after header. |
idempotency_mismatch, idempotency_in_progress | 409 | The Idempotency-Key was used with a different request, or its first request is still running. |
Test keys validate uploads but do not store the bytes. A JSON upload request with a test key returns a test upload object (upload.test is true, the file is already uploaded): there is nothing to PUT.