POST /v1/scan
Submit a file for scanning. Supports two modes: direct multipart upload (send the file in the request) or presigned URL (get a URL to upload to separately).
POST https://api.filesafety.dev/v1/scanAuthentication
Section titled “Authentication”Requires x-api-key header. See Authentication.
Direct upload
Section titled “Direct upload”Send the file as multipart form data in a single request. Best for server-side integrations where the file is already available.
Request
Section titled “Request”Content-Type: multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
file | binary | Yes | The file to scan. |
webhook_url | string | Yes | HTTPS URL where scan results will be POSTed on completion. |
scan_types | JSON string | No | Array of scan types to run. Options: "virus", "nsfw". Defaults to ["virus", "nsfw"]. |
metadata | JSON string | No | Arbitrary key-value object (max 4 KB) returned with scan results. |
Example request
Section titled “Example request”curl -X POST https://api.filesafety.dev/v1/scan \ -H "x-api-key: fs_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345" \ -F "file=@./document.pdf" \ -F "webhook_url=https://your-app.com/webhooks/filesafety" \ -F 'scan_types=["virus","nsfw"]' \ -F 'metadata={"user_id":"usr_123"}'Response — 200 OK
Section titled “Response — 200 OK”{ "scan_id": "scn_01HX7Z9K3M2N4P5Q6R7S8T9U0V", "status": "pending", "eta_seconds": 15}| Field | Type | Description |
|---|---|---|
scan_id | string | Unique scan identifier. Use this to poll for results. |
status | string | Always "pending" for new direct uploads. |
eta_seconds | integer | Estimated time to completion in seconds. |
Presigned URL
Section titled “Presigned URL”Request a presigned URL for uploading the file separately. Best for browser-based uploads or large files where you want to decouple the scan request from the file transfer.
Request
Section titled “Request”Content-Type: application/json
{ "webhook_url": "https://your-app.com/webhooks/filesafety", "scan_types": ["virus", "nsfw"], "metadata": {"user_id": "usr_123"}}| Field | Type | Required | Description |
|---|---|---|---|
webhook_url | string | Yes | HTTPS URL where scan results will be POSTed on completion. |
scan_types | array | No | Array of scan types. Options: "virus", "nsfw". Defaults to ["virus", "nsfw"]. |
metadata | object | No | Arbitrary key-value object (max 4 KB) returned with scan results. |
Example request
Section titled “Example request”curl -X POST https://api.filesafety.dev/v1/scan \ -H "x-api-key: fs_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345" \ -H "Content-Type: application/json" \ -d '{ "webhook_url": "https://your-app.com/webhooks/filesafety", "scan_types": ["virus", "nsfw"], "metadata": {"user_id": "usr_123"} }'Response — 200 OK
Section titled “Response — 200 OK”{ "scan_id": "scn_01HX8A1B2C3D4E5F6G7H8I9J0K", "status": "awaiting_upload", "upload_url": "https://upload.filesafety.dev/scn_01HX8A1B2C3D4E5F6G7H8I9J0K?..."}| Field | Type | Description |
|---|---|---|
scan_id | string | Unique scan identifier. |
status | string | "awaiting_upload" — waiting for the file to be uploaded. |
upload_url | string | Presigned URL. PUT the file here to begin scanning. Expires in 15 minutes. |
Uploading to the presigned URL
Section titled “Uploading to the presigned URL”curl -X PUT "UPLOAD_URL_FROM_RESPONSE" \ -H "Content-Type: application/octet-stream" \ --data-binary @./document.pdfOnce the upload succeeds, scanning begins automatically. No additional API call is needed.
Deduplicated response
Section titled “Deduplicated response”If the file has been scanned before (matched by cryptographic hash), the cached result is returned immediately:
Response — 200 OK
Section titled “Response — 200 OK”{ "scan_id": "scn_01HX7Z9K3M2N4P5Q6R7S8T9U0V", "status": "complete", "verdict": "clean", "deduplicated": true}| Field | Type | Description |
|---|---|---|
scan_id | string | Scan identifier (may be the original scan’s ID). |
status | string | "complete" — result available immediately. |
verdict | string | One of: clean, infected, nsfw, mixed, failed. |
deduplicated | boolean | true — result was served from cache. |
Deduplicated scans still count toward your monthly quota.
Error responses
Section titled “Error responses”| Status | Error | Cause |
|---|---|---|
400 | "webhook_url is required" | Missing the webhook_url field. |
400 | "Invalid scan_types" | scan_types contains an unrecognized value. |
401 | "Invalid or missing API key" | Missing or invalid x-api-key header. |
429 | "Quota exceeded" | Free plan quota reached. Upgrade to continue scanning. |
Scan types reference
Section titled “Scan types reference”| Type | What it detects |
|---|---|
virus | Viruses, malware, trojans, ransomware, adware. |
nsfw | Explicit nudity, suggestive content, graphic violence. |
Both scan types are included in every plan at no extra cost.