Endpoint: Compress
POST /compress — compress any supported file. The server detects the format from the file extension and routes to the appropriate compressor automatically.
Examples
# compress a JPG (file upload)
curl -sS -X POST "https://api.tools.fast/compress" \
-H "X-Fast-Api-Key: $API_KEY" \
-F "file=@photo.jpg"
# compress a PNG
curl -sS -X POST "https://api.tools.fast/compress" \
-H "X-Fast-Api-Key: $API_KEY" \
-F "file=@screenshot.png"
# compress a PDF
curl -sS -X POST "https://api.tools.fast/compress" \
-H "X-Fast-Api-Key: $API_KEY" \
-F "file=@document.pdf"
# compress from URL (JSON body)
curl -sS -X POST "https://api.tools.fast/compress" \
-H "X-Fast-Api-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{"inputUrl": "https://example.com/photo.jpg", "sourceFormat": "jpg"}'
# compress from URL (multipart field)
curl -sS -X POST "https://api.tools.fast/compress" \
-H "X-Fast-Api-Key: $API_KEY" \
-F "inputUrl=https://example.com/photo.jpg" \
-F "sourceFormat=jpg"using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Fast-Api-Key", "fast_prod_your_key_here");
// File upload
using var form = new MultipartFormDataContent();
form.Add(new ByteArrayContent(File.ReadAllBytes("photo.jpg")), "file", "photo.jpg");
var response = await http.PostAsync("https://api.tools.fast/compress", form);
var job = await response.Content.ReadFromJsonAsync<JsonElement>();
var jobId = job.GetProperty("id").GetString();
// URL input (JSON body)
var urlResponse = await http.PostAsJsonAsync("https://api.tools.fast/compress",
new { inputUrl = "https://example.com/photo.jpg", sourceFormat = "jpg" });
var urlJob = await urlResponse.Content.ReadFromJsonAsync<JsonElement>();$headers = @{ "X-Fast-Api-Key" = "fast_prod_your_key_here" }
# compress a JPG (file upload)
Invoke-RestMethod -Method Post "https://api.tools.fast/compress" `
-Headers $headers -Form @{ file = Get-Item "photo.jpg" }
# compress from URL (JSON body)
Invoke-RestMethod -Method Post "https://api.tools.fast/compress" `
-Headers ($headers + @{ "Content-Type" = "application/json" }) `
-Body '{"inputUrl": "https://example.com/photo.jpg", "sourceFormat": "jpg"}'Method + path
POST /compress
The server resolves the compressor from the file extension. Supported formats: JPG, PNG, GIF, WebP, AVIF, HEIC, TIFF, BMP, PSD, 20+ RAW camera formats, PDF, DOCX, PPTX.
Auth
- Required:
X-Fast-Api-Key
Content type
Two input methods are supported:
| Method | Content-Type | Use case |
|---|---|---|
| File upload | multipart/form-data | Upload a file from disk |
| URL input | application/json or multipart/form-data with inputUrl field | Compress a file from a URL |
Form fields (multipart/form-data)
| Field | Required | Type | Description |
|---|---|---|---|
file | yes* | file | The file to compress |
inputUrl | yes* | string | URL of a file to fetch and compress (mutually exclusive with file) |
sourceFormat | yes* | string | Source format of the file at the URL (e.g. "jpg", "pdf"). Required when using inputUrl |
options | no | JSON string | Resize and compression settings (see Options) |
webhookUrl | no | string | HTTPS URL to receive a webhook POST on job completion |
webhookSecret | no (required with webhookUrl) | string | Shared secret for HMAC-SHA256 signature verification (16–256 chars) |
webhookEvents | no | string | Comma-separated event filter: succeeded, failed, or both (default: both) |
* Exactly one of file or inputUrl must be provided. Sending both returns 400 url_fetch.mutually_exclusive.
Notes:
- Exactly one file per request.
- The file extension must match a supported format.
- The
optionsfield is a JSON string — not a nested multipart section. See Options Reference for the full schema.
JSON body fields (application/json)
For URL-based input, send a JSON body instead of multipart:
| Field | Required | Type | Description |
|---|---|---|---|
inputUrl | yes | string | URL of the file to compress. Must be HTTPS. Supports presigned S3, GCS, and Azure Blob Storage URLs. |
sourceFormat | yes | string | Source format of the file at the URL (e.g. "jpg", "pdf"). Required for URL input |
options | no | object | Compressor-specific options (see Options) |
outputUrl | — | string | Reserved for future use. Returns 400 if provided. |
webhookUrl | no | string | HTTPS URL for job completion notifications |
webhookSecret | no (required with webhookUrl) | string | Shared secret for HMAC-SHA256 signature verification (16–256 chars) |
webhookEvents | no | string | Comma-separated event filter |
JSON body example
{
"inputUrl": "https://example.com/photo.jpg",
"sourceFormat": "jpg",
"options": {
"quality": 80,
"stripMetadata": true
}
}
URL input notes
sourceFormatrequired — URL input requiressourceFormatso the server can validate format support and enforce per-compressor size limits before downloading. Omitting it returnsurl_fetch.missing_source_format.- Pre-download validation — The format is validated before fetching. Unsupported formats return
compress.unsupported_formatinstantly (no download occurs). - Per-compressor size limits — The download cap uses the compressor's configured file size limit for your account, not a generic cap.
- HTTPS required — HTTP URLs are rejected.
- SSRF protection — Private IPs (10.x, 172.16-31.x, 192.168.x, 127.x), link-local (169.254.x), and IPv6 loopback are blocked.
- Redirects — Up to 3 redirects are followed. Each hop is validated against the SSRF deny list.
- Circuit breaker — Domains returning repeated errors are temporarily blocked to protect the service.
isRetryable— URL fetch errors include anisRetryableboolean indicating whether the request can be retried.
Response
202 Accepted with job details.
Example response
{
"id": "019c56454f8b755996c45a4874a1f3f6",
"status": "Queued",
"fileName": "photo",
"format": "jpg",
"...": "see JobAcceptedResponse model for all fields"
}
Full workflow
API_KEY="fast_prod_your_key_here"
# 1) submit
JOB_ID=$(curl -sS -X POST "https://api.tools.fast/compress" \
-H "X-Fast-Api-Key: $API_KEY" \
-F "file=@photo.jpg" | jq -r '.id')
# 2) poll
while true; do
STATUS=$(curl -sS "https://api.tools.fast/compress/job/${JOB_ID}" \
-H "X-Fast-Api-Key: $API_KEY" | jq -r '.status')
[ "${STATUS}" = "Succeeded" ] && break
[ "${STATUS}" = "Failed" ] || [ "${STATUS}" = "Canceled" ] && exit 1
sleep 1
done
# 3) download
curl -sS "https://api.tools.fast/compress/job/${JOB_ID}/download" \
-H "X-Fast-Api-Key: $API_KEY" \
-o "photo-compressed.jpg"using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Fast-Api-Key", "fast_prod_your_key_here");
// 1) submit
using var form = new MultipartFormDataContent();
form.Add(new ByteArrayContent(File.ReadAllBytes("photo.jpg")), "file", "photo.jpg");
var submit = await http.PostAsync("https://api.tools.fast/compress", form);
var job = await submit.Content.ReadFromJsonAsync<JsonElement>();
var jobId = job.GetProperty("id").GetString();
// 2) poll
string status;
do
{
await Task.Delay(1000);
var poll = await http.GetFromJsonAsync<JsonElement>(
$"https://api.tools.fast/compress/job/{jobId}");
status = poll.GetProperty("status").GetString()!;
} while (status is "Queued" or "Running");
// 3) download
var output = await http.GetByteArrayAsync(
$"https://api.tools.fast/compress/job/{jobId}/download");
File.WriteAllBytes("photo-compressed.jpg", output);$headers = @{ "X-Fast-Api-Key" = "fast_prod_your_key_here" }
# 1) submit
$job = Invoke-RestMethod -Method Post "https://api.tools.fast/compress" `
-Headers $headers `
-Form @{ file = Get-Item "photo.jpg" }
# 2) poll
do {
$status = (Invoke-RestMethod "https://api.tools.fast/compress/job/$($job.id)" `
-Headers $headers).status
if ($status -in "Failed", "Canceled") { throw "Job $($job.id) $status" }
Start-Sleep -Seconds 1
} while ($status -ne "Succeeded")
# 3) download
Invoke-RestMethod "https://api.tools.fast/compress/job/$($job.id)/download" `
-Headers $headers -OutFile "photo-compressed.jpg"Compress from URL (full workflow)
API_KEY="fast_prod_your_key_here"
# 1) submit from URL
JOB_ID=$(curl -sS -X POST "https://api.tools.fast/compress" \
-H "X-Fast-Api-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{"inputUrl": "https://example.com/photo.jpg", "sourceFormat": "jpg"}' | jq -r '.id')
# 2) poll
while true; do
STATUS=$(curl -sS "https://api.tools.fast/compress/job/${JOB_ID}" \
-H "X-Fast-Api-Key: $API_KEY" | jq -r '.status')
[ "${STATUS}" = "Succeeded" ] && break
[ "${STATUS}" = "Failed" ] || [ "${STATUS}" = "Canceled" ] && exit 1
sleep 1
done
# 3) download
curl -sS "https://api.tools.fast/compress/job/${JOB_ID}/download" \
-H "X-Fast-Api-Key: $API_KEY" \
-o "photo-compressed.jpg"using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Fast-Api-Key", "fast_prod_your_key_here");
// 1) submit from URL
var urlResponse = await http.PostAsJsonAsync("https://api.tools.fast/compress",
new { inputUrl = "https://example.com/photo.jpg", sourceFormat = "jpg" });
var job = await urlResponse.Content.ReadFromJsonAsync<JsonElement>();
var jobId = job.GetProperty("id").GetString();
// 2) poll
string status;
do
{
await Task.Delay(1000);
var poll = await http.GetFromJsonAsync<JsonElement>(
$"https://api.tools.fast/compress/job/{jobId}");
status = poll.GetProperty("status").GetString()!;
} while (status is "Queued" or "Running");
// 3) download
var output = await http.GetByteArrayAsync(
$"https://api.tools.fast/compress/job/{jobId}/download");
File.WriteAllBytes("photo-compressed.jpg", output);With webhook notification
# Compress with webhook — no polling needed
curl -sS -X POST "https://api.tools.fast/compress" \
-H "X-Fast-Api-Key: $API_KEY" \
-F "file=@photo.jpg" \
-F "webhookUrl=https://example.com/webhooks/fast" \
-F "webhookSecret=whsec_your_secret_here"using var form = new MultipartFormDataContent();
form.Add(new ByteArrayContent(File.ReadAllBytes("photo.jpg")), "file", "photo.jpg");
form.Add(new StringContent("https://example.com/webhooks/fast"), "webhookUrl");
form.Add(new StringContent("whsec_your_secret_here"), "webhookSecret");
var response = await http.PostAsync("https://api.tools.fast/compress", form);
// No polling needed — your webhook endpoint will be called on completion# Compress with webhook — no polling needed
Invoke-RestMethod -Method Post "https://api.tools.fast/compress" `
-Headers $headers `
-Form @{
file = Get-Item "photo.jpg"
webhookUrl = "https://example.com/webhooks/fast"
webhookSecret = "whsec_your_secret_here"
}See Webhooks for payload schema, signature verification, and retry policy.
Unsupported format error
If the file extension isn't supported, you'll get a 400 with the list of supported formats:
{
"error": "compress.unsupported_format",
"detail": "Unsupported file format: '.mp3'.",
"supportedFormats": [".avif", ".bmp", ".docx", ".gif", ".jpg", "..."]
}
Errors
See Errors for the full error reference with JSON examples.
| Status | Codes | Cause |
|---|---|---|
400 | request.invalid_content_type, request.no_files, request.empty_file, request.invalid_extension, request.multiple_files, compress.unsupported_format, compress.invalid_webhook, url_fetch.missing_source_format, url_fetch.invalid_url, url_fetch.invalid_scheme, url_fetch.private_ip, url_fetch.mutually_exclusive, url_fetch.output_url_reserved, url_fetch.http_error, url_fetch.dns_failure, url_fetch.timeout, url_fetch.too_large, url_fetch.redirect_loop, url_fetch.redirect_denied, url_fetch.domain_circuit_open, url_fetch.url_expired, url_fetch.no_filename, url_fetch.invalid_content | Bad request payload |
401 | api_key.invalid_or_ip_not_allowed | Invalid API key or IP not allowlisted |
402 | entitlements.insufficient_credits | Not enough credits |
403 | url_fetch.guest_not_allowed | URL input requires a signed-in account |
413 | request.file_too_large | File exceeds size limit |
429 | rate_limited, queue.limit_exceeded, url_fetch.rate_limited | Throttled |