Paste the script below into a terminal to test a Filestack key in about a minute. It runs the operations your project calls against one of your files and prints the response from each task.
You have inherited an application somebody else configured, and you want to know what it is set up for before you build on it. You have just changed plan and want to confirm it took effect. Or an operation has started behaving differently and you want to see the whole surface at once rather than one call at a time.
It is also worth running before you design anything. The operations that fail loudly you find in the first ten minutes. The ones that cost an afternoon are those that succeed in a way you did not expect, and those only show up when you compare what came back to what you sent.
Key takeaways
- Test the tasks your application actually calls because the response shows what each one does with your file.
- A 200 response proves the request succeeded, but a size comparison shows whether the file changed.
- A 403 identifies a capability the current application cannot use.
- Response type still needs a manual check because status and byte count cannot describe the output shape.
- Recheck task access and quotas after plan changes or when the application adds new operations.
What “capable of” actually means
An operation can come back three ways, and only two of them are obvious.
It works. You get a 200 and a file that is different from what you sent.
It is on another plan. The API says so explicitly and names the task, so there is no guesswork about which one:
You don’t have permission to perform this task: ocr. Please check your access settings
It answers 200 and changes nothing. The request succeeds, you get a file back, and the file is byte-identical to the one you sent, usually because there was nothing in it for that task to act on. Stripping metadata from a file with no metadata is the clearest case. Nothing in the status code tells you, so the script compares sizes.
The script
Swap in the handle of any image you have already uploaded. The handle identifies the application, so no API key goes in these URLs, and it needs nothing but curl.
#!/usr/bin/env bash
HANDLE="YOUR_IMAGE_HANDLE"
BASE="https://cdn.filestackcontent.com"
orig=$(curl -s -o /dev/null -w '%{size_download}' "$BASE/$HANDLE")
echo "original file: $orig bytes"
printf '%-28s %-5s %s\n' TASK CODE VERDICT
for task in resize=width:200 output=format:webp compress crop=dim:%5B0,0,400,250%5D \
blur_faces=amount:10 detect_faces watermark=file:$HANDLE,size:30 \
no_metadata zip \
ocr tags sfw caption copyright doc_detection \
smart_crop=width:300,height:300 enhance upscale=noise:low redeye; do
read -r code size < <(curl -s -o /dev/null -w '%{http_code} %{size_download}' "$BASE/$task/$HANDLE")
case "$code" in
403) verdict="available on a higher plan" ;;
200) if [ "$size" = "$orig" ]; then verdict="200 but byte-identical, it did nothing"
else verdict="works, returned $size bytes"; fi ;;
*) verdict="$code, check the task syntax" ;;
esac
printf '%-28s %-5s %s\n' "${task%%=*}" "$code" "$verdict"
done
Add or remove tasks freely. Every operation is a URL segment, so the list is just names, and the processing API reference has the rest of them with their parameters.
Reading the output
Here is a run against a 319,136 byte photograph on the free plan:
original file: 319136 bytes
TASK CODE VERDICT
resize 200 works, returned 19879 bytes
output 200 works, returned 322232 bytes
compress 200 works, returned 297412 bytes
crop 200 works, returned 7833 bytes
blur_faces 200 works, returned 165914 bytes
detect_faces 200 works, returned 165914 bytes
watermark 200 works, returned 411100 bytes
no_metadata 200 200 but byte-identical, it did nothing
zip 200 works, returned 318050 bytes
ocr 403 available on a higher plan
tags 403 available on a higher plan
sfw 403 available on a higher plan
caption 403 available on a higher plan
copyright 403 available on a higher plan
doc_detection 403 available on a higher plan
smart_crop 403 available on a higher plan
enhance 403 available on a higher plan
upscale 403 available on a higher plan
redeye 403 available on a higher plan
Four things in there are worth pausing on.
The core image editing api tasks run on the free plan, and so does facial detection. We put blur faces and its detection siblings on the free plan so privacy work can start there.
Most machine learning operations are on the higher plans. Facial detection is the free-plan exception. Deterministic transformations of pixels or bytes make up the rest of the free plan’s core processing surface.
Byte counts separate a working task from a useful result. compress returned 297,412 bytes against a 319,136 byte original, a saving of under seven percent, while resize returned 19,879. Both are successes, but only one is going to matter to your bandwidth. Watching the bytes shows which operations are worth building around rather than which ones merely respond.
The metadata stripper is caught doing nothing, which no status code would have told you. Run the same script without the size comparison and it reports a clean 200. That is the sort of result that gets built on and debugged three weeks later.
One thing to check by hand
Response type. The script reports status and size, not shape. detect_faces returns an annotated image by default and the coordinates as JSON with detect_faces=export:true, so pick the one your code expects. Check the Content-Type header before parsing a response that can return either an image or JSON.
Checking the other half
Operations are one limit and quotas are the other, and no script reads those. Yours are in the Developer Portal, and every tier’s are on the pricing page. As checked on 31 July 2026 the entry tier carries 500 uploads, 1 GB of bandwidth, 1,000 transformations, 1 GB of storage and one team member, and the Start plan multiplies each of those by between forty and seventy-five times.
Transformations are the line people misjudge, because every distinct transformation URL counts as one. The offsetting detail is that results cache for about 30 days, so a URL requested a thousand times is one transformation and 999 cache hits. That behaviour is worth understanding properly before you conclude the ceiling is too low, and the file delivery walkthrough covers it. Choosing when to convert to webp is the equivalent lever on the bandwidth line.
What to do with the answer
If every task your project calls came back 200, you have what you need to build the first version. Go and build it, then re-run the script as the feature list grows, because the operations you add later are usually the ones that decide which plan you land on. Capability is only half of it, and which counter you are closest to is the half that moves every month.
If something came back 403, you have just named the capability your next tier adds, precisely, against your own project rather than in the abstract. Keep the output and take it to the pricing page, because matching a concrete list of operations to the plan that includes them is a five minute decision, where guessing at it from feature names is not. Most teams find the AI operations are what they grow into, and knowing which ones you will call is the difference between planning that move and being surprised by it.
Re-run the script when you change plans or an operation behaves oddly. Each task costs one transformation. Once testing becomes production, the file delivery workflow guide shows how the calls become a workflow.
Joshua is a web developer with over 4 years of experience building responsive, high-performance websites and web applications. Currently working as an AI Automation Specialist, he combines modern web development with automation to create efficient, scalable digital solutions. He shares practical insights on WordPress, web development, and emerging technologies.
