API
No key, no account, no client library. The same 391 conversions and 13 tools the site offers, across 38 formats, over a handful of endpoints. If you are a language model reading this, you can call it directly.
Endpoints
GET
/api/limitsWhat this deployment will accept.
The per-file cap and the per-client rate limits, as the running container has them rather than as the documentation remembers them. Worth reading before sending something large: the relay refuses an oversized upload from its Content-Length, which is faster than sending it but slower than not trying.
POST
/api/jobs?from={format}&to={format}Start a conversion.
The file is the raw request body, not multipart. Content-Length is required, because the quota is checked against the declared size before a byte is read. Returns 202 with a task id and the engine holding it.
POST
/api/jobs?operation={id}&format={format}Start an operation that keeps the format.
Compressing an MP4 produces an MP4, which is not a conversion and has no entry in the pair matrix. Otherwise identical to the call above.
GET
/api/jobs/{id}?engine={engine}Poll a job.
Returns state, progress and, once failed, a classified reason. The engine comes from the create response; asking all four on every poll would be a fan-out.
GET
/api/jobs/{id}/output?engine={engine}&format={format}Download the result.
409 until the job is done. The task id is 128 bits of randomness and there are no accounts, so holding the id is the whole authorisation story.
DELETE
/api/jobs/{id}?engine={engine}Cancel a job and free the engine.
Always 204. Cancelling a job that has already finished is not an error.
A whole conversion
# Start the job. The response names the engine you poll.
curl -X POST 'https://toolsforfree.app/api/jobs?from=mov&to=mp4' \
--data-binary @holiday.mov \
-H 'x-tff-options: {"videoCodec":"h264","crf":23}'
# {"taskId":"nQ8...","engine":"media"}
# Poll until state is "done".
curl 'https://toolsforfree.app/api/jobs/nQ8...?engine=media'
# {"state":"running","progress":0.42}
# Then take the file.
curl -o holiday.mp4 'https://toolsforfree.app/api/jobs/nQ8.../output?engine=media&format=mp4'Options
Conversion settings travel as JSON in an x-tff-options header rather than in the body, which is occupied by the file. Anything the schema does not recognise is rejected with 400 invalid-options rather than ignored, so a typo in a codec name fails loudly instead of quietly producing a default. Every field, with its bounds and its permitted values, is in openapi.json as ConversionOptions, generated from the schema the engines parse with rather than written out beside it.
These rules are enforced too, and no schema can state them:
- video.crf applies only when video.mode is quality.
- image.quality may not be sent alongside image.lossless.
- trim.endMs must be greater than trim.startMs.
- audio.bitrateKbps is refused for flac, which is lossless.
- A codec has to be legal for the container it goes in. The permitted combinations are published as
x-tff-container-codecsin the same document.
Limits
- 512 MiB per file. Larger uploads get
413. - 200 jobs per hour per client, as a rolling window, and 5 GiB of input per day. Both answer
429with aRetry-After. - One job at a time per engine. A busy engine answers
503with aRetry-Afterrather than queueing, so a caller is never left holding a connection open against a queue it cannot see. - Your input and the result go together: both removed at once if the job fails, otherwise kept for a few minutes so you can download them, then deleted — whether or not you did. Nothing is stored, indexed, or logged against a caller, so there is nothing to come back and delete afterwards.
These are the limits of one small server, not a pricing tier. They exist so this stays free, not to sell you a larger one.
Machine-readable
- /openapi.jsonEvery endpoint, parameter and option, generated from the catalog.
- /llms.txtThe whole catalog as plain text, for a model that would rather not crawl.
- /sitemap.xmlEvery page, generated from the same route table the router uses.