POST /v1/scan
Submit a file or URL for scanning. Uploaded files are automatically deleted immediately after scanning completes. No file content is retained.
FileSafety offers two scan modes and multiple input methods:
- Sync (
POST /v1/scan) — Upload a file or send a URL, get the result in one request. - Async (
POST /v1/scan/async) — Upload a file or send a URL, get ascan_idimmediately, then poll or receive a webhook.
Authentication
Section titled “Authentication”Requires x-api-key header. See Authentication.
Automatic scan type selection
Section titled “Automatic scan type selection”FileSafety automatically determines which scans to run based on the uploaded file’s size and type. There is no scan_types parameter — all applicable scans run automatically.
By file size
Section titled “By file size”| File Size | Maximum Scans Applied |
|---|---|
| Up to 10 MB | Malware detection + Content analysis (images) + Content analysis (text) |
| 10 MB – 100 MB | Malware detection + Content analysis (text) |
| 100 MB – 1 GB | Malware detection only |
| Over 1 GB | Rejected (file too large) |
By file type
Section titled “By file type”Within the size limits above, scans are further filtered by file extension to avoid unnecessary processing:
| Scan | File Types |
|---|---|
| Malware detection | All files (always runs) |
| Content analysis (images) | .jpg, .jpeg, .png, .gif, .bmp, .webp, .tiff, .tif |
| Content analysis (text) | .txt, .csv, .json, .xml, .html, .htm, .md, .log, .yaml, .yml, .pdf, .png, .jpg, .jpeg, .tiff, .tif |
Files with no extension or an unrecognized extension receive all scans applicable to their size. For example, a 2 MB .zip file receives only malware detection, while a 2 MB .jpg receives malware detection, image content analysis, and text extraction (via OCR).
All scan types are included in every plan at no extra cost.
Sync scan — POST /v1/scan
Section titled “Sync scan — POST /v1/scan”POST https://api.filesafety.dev/v1/scanSend a file or URL and receive the full scan result in the response. The API waits for the scan to complete (typically 10–25 seconds). Best for files under ~100 MB. May timeout for very large files — use async mode if you expect long scan times.
File upload
Section titled “File upload”Content-Type: multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
file | binary | Yes | The file to scan (up to 1 GB). |
webhook_url | string | No | HTTPS URL where scan results will also be POSTed. |
metadata | JSON string | No | Arbitrary key-value object (max 4 KB) returned with results. |
curl -X POST https://api.filesafety.dev/v1/scan \ -H "x-api-key: fs_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345" \ -F "file=@./document.pdf"With optional webhook and metadata:
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 'metadata={"user_id":"usr_123"}'URL scanning
Section titled “URL scanning”Content-Type: application/json
Instead of uploading a file, send a URL. FileSafety fetches and scans the content at that URL.
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | The URL to fetch and scan. Must be HTTPS. |
webhook_url | string | No | HTTPS URL where scan results will also be POSTed. |
metadata | object | No | Arbitrary key-value object (max 4 KB) returned with results. |
curl -X POST https://api.filesafety.dev/v1/scan \ -H "x-api-key: fs_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345" \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com/files/report.pdf"}'Response — 200 OK
Section titled “Response — 200 OK”{ "scan_id": "scn_01HX7Z9K3M2N4P5Q6R7S8T9U0V", "status": "complete", "verdict": "clean", "virus": { "clean": true, "signature": null }, "nsfw": { "clean": true, "categories": [], "confidence": 0.01 }, "text": { "clean": true, "toxic": { "labels": [], "maxScore": 0.0 }, "pii": { "entities": [], "count": 0 }, "sentiment": { "dominant": "NEUTRAL", "scores": {} } }, "file_hash": "sha256:e3b0c44298fc1c149afbf4c8...", "completed_at": "2025-01-15T10:30:45.123Z"}If the scan takes longer than ~25 seconds, the sync endpoint returns "status": "scanning" with a scan_id for polling via GET /v1/scan/{id}.
If the file was previously scanned (matched by hash), the cached result returns instantly.
Async scan — POST /v1/scan/async
Section titled “Async scan — POST /v1/scan/async”POST https://api.filesafety.dev/v1/scan/asyncSubmit a file or URL and receive a scan_id immediately. Retrieve results by polling GET /v1/scan/{id} or providing a webhook_url. Best for large files or batch processing. Supports files up to 1 GB via direct upload or presigned URL.
File upload (up to 1 GB)
Section titled “File upload (up to 1 GB)”Content-Type: multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
file | binary | Yes | The file to scan (up to 1 GB). |
webhook_url | string | No | HTTPS URL where scan results will be POSTed on completion. |
metadata | JSON string | No | Arbitrary key-value object (max 4 KB) returned with results. |
curl -X POST https://api.filesafety.dev/v1/scan/async \ -H "x-api-key: fs_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345" \ -F "file=@./large-archive.zip" \ -F "webhook_url=https://your-app.com/webhooks/filesafety"Response — 202 Accepted
Section titled “Response — 202 Accepted”{ "scan_id": "scn_01HX8A1B2C3D4E5F6G7H8I9J0K", "status": "pending", "eta_seconds": 15}URL scanning
Section titled “URL scanning”Content-Type: application/json
curl -X POST https://api.filesafety.dev/v1/scan/async \ -H "x-api-key: fs_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345" \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com/files/report.pdf", "webhook_url": "https://your-app.com/webhooks/filesafety"}'| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | The URL to fetch and scan. Must be HTTPS. |
webhook_url | string | No | HTTPS URL where scan results will be POSTed on completion. |
metadata | object | No | Arbitrary key-value object (max 4 KB) returned with results. |
Response — 202 Accepted
Section titled “Response — 202 Accepted”{ "scan_id": "scn_01HX8A1B2C3D4E5F6G7H8I9J0K", "status": "pending", "eta_seconds": 15}Presigned URL (for browser or deferred uploads)
Section titled “Presigned URL (for browser or deferred uploads)”Request a presigned upload URL without sending a file. Upload later via PUT. Best for browser-based uploads or when you need to decouple the upload from the scan request.
Content-Type: application/json
| Field | Type | Required | Description |
|---|---|---|---|
webhook_url | string | No | HTTPS URL where scan results will be POSTed on completion. |
metadata | object | No | Arbitrary key-value object (max 4 KB) returned with results. |
curl -X POST https://api.filesafety.dev/v1/scan/async \ -H "x-api-key: fs_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345" \ -H "Content-Type: application/json" \ -d '{"webhook_url": "https://your-app.com/webhooks/filesafety"}'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. |
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. Poll GET /v1/scan/{id} for results.
Response schema
Section titled “Response schema”| Field | Type | Description |
|---|---|---|
scan_id | string | Unique scan identifier. |
status | string | "complete", "failed", "scanning", "pending", or "awaiting_upload". |
verdict | string | One of: clean, infected, nsfw, toxic, mixed, failed. |
virus | object | Virus scan result. clean: true if no threats found. signature contains the threat name if infected. |
nsfw | object | Content analysis (images) result. clean: true if safe. categories lists flagged content types. |
text | object | Content analysis (text) result. Includes toxicity detection, PII detection, and sentiment analysis. |
file_hash | string | Cryptographic hash of the scanned file. |
completed_at | string | ISO 8601 timestamp of scan completion. |
Error responses
Section titled “Error responses”| Status | Error | Cause |
|---|---|---|
400 | "Invalid JSON body" | Malformed JSON in request body. |
400 | "Invalid URL" | The provided URL is not a valid HTTPS URL. |
401 | "Invalid or missing API key" | Missing or invalid x-api-key header. |
413 | "File too large" | File exceeds 1 GB maximum size. |
429 | "Monthly scan quota exceeded" | Free plan quota reached. Upgrade to continue scanning. |
Verdict reference
Section titled “Verdict reference”| Verdict | Meaning |
|---|---|
clean | No threats detected. |
infected | Virus or malware detected. Check virus.signature for the threat name. |
nsfw | Explicit or suggestive content detected. Check nsfw.categories for details. |
toxic | Toxic or harmful text content detected. Check text.toxic for details. |
mixed | Multiple types of issues detected. |
failed | Scan could not be completed. Retry the request. |
Scan types
Section titled “Scan types”Scan types are applied automatically based on file size and type. You do not need to specify them.
| Type | What it detects | Applicable files |
|---|---|---|
| Malware detection | Viruses, malware, trojans, ransomware, adware. | All files |
| Content analysis (images) | Explicit nudity, suggestive content, graphic violence. | Images only (.jpg, .png, .gif, .bmp, .webp, .tiff) |
| Content analysis (text) | Toxic language, PII (personal information), and sentiment. | Text and document files (.txt, .csv, .json, .xml, .html, .md, .pdf, and images via OCR) |
All scan types are included in every plan at no extra cost.