Skip to content

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/scan

Requires x-api-key header. See Authentication.


Send the file as multipart form data in a single request. Best for server-side integrations where the file is already available.

Content-Type: multipart/form-data

FieldTypeRequiredDescription
filebinaryYesThe file to scan.
webhook_urlstringYesHTTPS URL where scan results will be POSTed on completion.
scan_typesJSON stringNoArray of scan types to run. Options: "virus", "nsfw". Defaults to ["virus", "nsfw"].
metadataJSON stringNoArbitrary key-value object (max 4 KB) returned with scan results.
Terminal window
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"}'
{
"scan_id": "scn_01HX7Z9K3M2N4P5Q6R7S8T9U0V",
"status": "pending",
"eta_seconds": 15
}
FieldTypeDescription
scan_idstringUnique scan identifier. Use this to poll for results.
statusstringAlways "pending" for new direct uploads.
eta_secondsintegerEstimated time to completion in seconds.

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.

Content-Type: application/json

{
"webhook_url": "https://your-app.com/webhooks/filesafety",
"scan_types": ["virus", "nsfw"],
"metadata": {"user_id": "usr_123"}
}
FieldTypeRequiredDescription
webhook_urlstringYesHTTPS URL where scan results will be POSTed on completion.
scan_typesarrayNoArray of scan types. Options: "virus", "nsfw". Defaults to ["virus", "nsfw"].
metadataobjectNoArbitrary key-value object (max 4 KB) returned with scan results.
Terminal window
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"}
}'
{
"scan_id": "scn_01HX8A1B2C3D4E5F6G7H8I9J0K",
"status": "awaiting_upload",
"upload_url": "https://upload.filesafety.dev/scn_01HX8A1B2C3D4E5F6G7H8I9J0K?..."
}
FieldTypeDescription
scan_idstringUnique scan identifier.
statusstring"awaiting_upload" — waiting for the file to be uploaded.
upload_urlstringPresigned URL. PUT the file here to begin scanning. Expires in 15 minutes.
Terminal window
curl -X PUT "UPLOAD_URL_FROM_RESPONSE" \
-H "Content-Type: application/octet-stream" \
--data-binary @./document.pdf

Once the upload succeeds, scanning begins automatically. No additional API call is needed.


If the file has been scanned before (matched by cryptographic hash), the cached result is returned immediately:

{
"scan_id": "scn_01HX7Z9K3M2N4P5Q6R7S8T9U0V",
"status": "complete",
"verdict": "clean",
"deduplicated": true
}
FieldTypeDescription
scan_idstringScan identifier (may be the original scan’s ID).
statusstring"complete" — result available immediately.
verdictstringOne of: clean, infected, nsfw, mixed, failed.
deduplicatedbooleantrue — result was served from cache.

Deduplicated scans still count toward your monthly quota.


StatusErrorCause
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.

TypeWhat it detects
virusViruses, malware, trojans, ransomware, adware.
nsfwExplicit nudity, suggestive content, graphic violence.

Both scan types are included in every plan at no extra cost.