Skip to content

File conversions

Convert images, documents, spreadsheets, audio, video, archives, and ebooks between formats, understand what each job costs, and save results into a bucket.

On this page

SteadyLink converts files between formats as temporary jobs: you upload a source (or point at a file already in your workspace), a worker converts it, and you download the result before it expires. Nothing is added to your storage unless you deliberately save a result into a bucket. This guide covers what can be converted, how the limits and credits work, and how to run conversions from the dashboard, the public converter, or the API.

Where to convert#

  • Dashboard > Convert: drop several files, pick an output format per group or per file, and download results. Jobs use your workspace's plan limits and credits.
  • steadylink.io/convert: the public converter. It works without an account (guest limits apply) and offers Save to workspace when you are signed in. A guest who signs up can still save a result they converted before signing in, as long as it has not expired.
  • The API: create jobs with an API key or a signed-in session. Useful for converting files already stored in SteadyLink without downloading them first.

Supported formats#

A conversion is supported when the source format is an input of a group and the target is an output of the same group.

GroupInputsOutputs
ImagesJPG, JPEG, PNG, WebP, AVIF, GIF, TIFF, BMP, HEIC, ICO, SVGJPG, PNG, WebP, AVIF, GIF, TIFF, BMP, ICO
DocumentsDOC, DOCX, ODT, RTF, TXT, HTMLPDF, DOCX, ODT, RTF, TXT, HTML
SpreadsheetsXLS, XLSX, ODS, CSVPDF, XLSX, ODS, CSV
PresentationsPPT, PPTX, ODPPDF, PPTX, ODP
PDFPDFJPG, PNG, TXT, DOCX, PDF
AudioMP3, WAV, AAC, FLAC, OGG, M4A, OPUSMP3, WAV, AAC, FLAC, OGG, M4A, OPUS
VideoMP4, WebM, MOV, AVI, MKVMP4, WebM, MOV, AVI, MKV, GIF, MP3
ArchivesZIP, TAR, TAR.GZ, 7ZZIP, TAR, TAR.GZ, 7Z
EbooksEPUB, MOBI, AZW3, PDF, TXTEPUB, MOBI, AZW3, PDF, TXT

Format names are case-insensitive, a leading dot is ignored, and jpeg, tif, and tgz are treated as jpg, tiff, and tar.gz. A PDF source always uses the PDF group, so PDF to EPUB is not available. An unsupported pair is rejected with 422 and a message such as "Conversion from PNG to PDF is not supported". GET /api/conversions/catalog returns this table along with the limits for every plan, and needs no authentication.

Note that SVG and HEIC are inputs only: you can convert from them but not to them.

Options#

Options are optional and only affect the formats they apply to.

OptionValuesEffect
width, height1 to 16,384 pxResize while keeping the aspect ratio. Give one to scale proportionally. Images are only ever scaled down.
quality1 to 100, default 85Encoder quality for JPG, WebP, and AVIF output. Out-of-range values are clamped.
effortfast, balanced (default), maximumEncoding effort for WebP and AVIF. More effort gives smaller files and takes longer.
backgroundUp to 32 characters, default whiteFill color for transparent areas when the output (JPG, BMP) has no transparency.
fps1 to 30, default 12Frame rate of GIFs made from video. GIFs default to 720 px wide.

Any other option name is rejected with 422 "Unsupported conversion option". Converted images have their metadata stripped, and photos are rotated according to their EXIF orientation first. Video and audio outputs also drop source metadata.

Limits by plan#

Guest jobs on the public converter, without an account:

LimitValue
Source size25 MB, or 15 MB for video
Jobs10 per rolling 24 hours
Running at once2
Result retention1 hour

Signed-in and API-key jobs use the workspace's plan (with an active subscription; otherwise Free limits):

PlanMax sourceMax video sourceRunning at onceVideo running at onceMonthly creditsMonthly video creditsMax video lengthMax video outputResult retention
Free100 MB50 MB21250505 min1920 x 108024 hours
Personal250 MB100 MB212,50050015 min1920 x 108024 hours
Pro500 MB250 MB4225,0005,0001 hour3840 x 216048 hours
Business1 GB500 MB64250,00050,0003 hours3840 x 216072 hours
Enterprise2 GB1 GB882,500,000500,0008 hours7680 x 43207 days

"Running at once" counts jobs that are created, uploaded, queued, or processing, across the whole workspace. A video longer than the plan allows fails with video_duration_limit. A video output larger than the plan's maximum is not rejected: it is scaled down to fit, keeping the aspect ratio, and the job's stageDetail says so, for example "Adjusted to 1920x1080 to fit the Free plan". Enterprise contracts can raise any of these values.

When a limit is hit at creation time:

StatusCodeCause
413conversion_size_limit, video_size_limitSource larger than the plan allows.
429conversion_concurrency_limit, video_concurrency_limitToo many jobs running. Wait for one to finish.
429conversion_daily_limitGuest daily job limit reached.
429usage_limit_exceededNot enough monthly credits left for the job's estimate. The response includes resetAt.

How credits are calculated#

Credits measure the work a job causes. Regular conversions and video conversions draw from separate monthly allowances. Guest jobs do not use credits.

Before a job runs, SteadyLink reserves a conservative estimate, so a job only starts if the workspace can afford it. For audio and video the worker measures the real duration and resolution after downloading and reserves the difference if the estimate was low. When the job finishes, the reservation is replaced by the measured cost. Cancelled jobs, malware blocks, missing sources, and worker failures refund their reservation.

The measured cost of a finished job is:

Text
credits = max(1, base estimate + output blocks + compute seconds x compute weight)
  • output blocks: the result size divided by 10 MB, rounded up.
  • compute seconds: total job time from download to upload, rounded up to a whole second.
  • compute weight: 4 for video, 2 for audio, 1 for everything else.

The base estimate depends on the group. "Input blocks" is the source size divided by 5 MB, rounded up.

GroupBase estimate
Images1 + input blocks + requested output megapixels / 8, rounded up (at least 1; it is 1 when you do not set both width and height)
Documents8 + 2 per page + 2 per input block
PDF10 + 2 per page + 2 per input block
Spreadsheets14 + 3 per page + 2 per input block
Presentations16 + 3 per page + 2 per input block
Ebooks12 + 2 per page + 2 per input block
Archives12 + 2 per file inside the archive + 3 per input block
Audio30 + 5 per started 15 seconds + 2 per input block
Video100 + 12 per started 5 seconds x resolution factor + 4 per input block

Pages are counted for sources in the PDF group; every other document counts as one page. The video resolution factor is the output's pixel count divided by 1280 x 720, rounded up, and never less than 3 (a 1080p output, or anything smaller); a 4K output has a factor of 9.

Two worked examples:

  • A 3 MB PNG converted to WebP at 1,200 px wide in under a second: 1 + 1 input block + 1 for pixels = 3, plus 1 output block and 1 compute second, for 5 credits.
  • A 40-second, 60 MB 1080p MOV converted to MP4 in 25 seconds, producing 30 MB: 100 + (8 blocks x 12 x 3) + (12 input blocks x 4) = 436, plus 3 output blocks and 25 x 4 compute, for 539 video credits.

Convert with the API#

Pass sourceAssetId and the job is queued immediately; there is nothing to upload. The source must have passed malware scanning (409 otherwise), must not be encrypted at rest (409 "Encrypted stored files cannot be converted in place yet"), and inputFormat must match the stored file's extension. Add sourceVersion to convert an older revision instead of the current one.

curl -X POST https://api.steadylink.io/api/conversions \
  -H "X-API-Key: $STEADYLINK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sourceAssetId": "3f2a9c1e-7b4d-4e8a-9c21-5d6f0a1b2c3d",
    "inputFormat": "png",
    "outputFormat": "avif",
    "options": { "width": 1200, "quality": 70 }
  }'
201 CreatedResponse
{
  "id": "d7e9f1a3-5b6c-4d8e-a0f2-3b4c5d6e7f80",
  "status": "queued",
  "category": "image",
  "inputFormat": "png",
  "outputFormat": "avif",
  "sourceName": "hero.png",
  "sourceBytes": 3145728,
  "outputName": null,
  "outputBytes": null,
  "outputMime": null,
  "progress": 5,
  "stage": "queued",
  "stageDetail": "Waiting for conversion capacity",
  "serviceTier": "priority",
  "timings": {},
  "error": null,
  "expiresAt": "2026-10-09T15:42:10.551820",
  "savedAssetId": null,
  "saveUploadSessionId": null,
  "registered": true,
  "createdAt": "2026-10-08T15:42:10.551820"
}

Convert a new file#

Send filename, size (in bytes), and the formats. The response includes an uploadUrl; PUT the exact bytes there with Content-Type: application/octet-stream, then call start.

Terminal
# 1. Create the job
curl -X POST https://api.steadylink.io/api/conversions \
  -H "X-API-Key: $STEADYLINK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "filename": "brochure.docx", "size": 482113, "inputFormat": "docx", "outputFormat": "pdf" }'

# 2. Upload the source to the returned uploadUrl
curl -X PUT "$UPLOAD_URL" -H "Content-Type: application/octet-stream" --data-binary @brochure.docx

# 3. Queue it
curl -X POST https://api.steadylink.io/api/conversions/d7e9f1a3-5b6c-4d8e-a0f2-3b4c5d6e7f80/start \
  -H "X-API-Key: $STEADYLINK_API_KEY"

start checks that the uploaded object exists and that its size matches size exactly. A missing upload returns 400 "Upload has not completed"; a size mismatch returns 400 "Uploaded file size does not match" and refunds the credit reservation. If a browser cannot reach the storage host, it can stream the bytes through the API instead with PUT /api/conversions/{job_id}/upload.

Follow progress and download#

Poll GET /api/conversions/{job_id}. The stage moves through queued, downloading, scanning, converting, verifying, uploading, and complete, and timings reports milliseconds per stage when it finishes. A failed job has status: "failed" and an error with a code and message.

When status is complete, GET /api/conversions/{job_id}/download returns a JSON body with a url that downloads the result under its output name. The URL is valid for 15 minutes; call the route again for a fresh one. Calling it before the job completes returns 409 "Conversion is not ready".

To download several results in one file, POST /api/conversions/archive with 2 to 25 completed job IDs and "format": "zip" or "tar.gz". The combined results can be up to 1 GB.

Guest jobs and access tokens#

A job created without credentials is a guest job. Its creation response includes an accessToken, which is the only way to reach the job afterwards: send it as X-Conversion-Token on start, GET, download, save, and DELETE. Keep it for the hour the job lives. Jobs created with an API key belong to that exact key; another key in the same workspace cannot read them. Jobs created in a signed-in session belong to that user.

GET /api/conversions/recent lists the caller's unexpired, unfailed jobs (up to 100), which is how the dashboard restores its list after a reload. It requires an API key or a session.

Save a result into a bucket#

Results are deleted when they expire. To keep one, save it into a bucket:

Terminal
curl -X POST https://api.steadylink.io/api/conversions/d7e9f1a3-5b6c-4d8e-a0f2-3b4c5d6e7f80/save \
  -H "X-API-Key: $STEADYLINK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "bucketId": "0c9e4b1a-6d2f-4a7e-b3c5-8f1d2e3a4b5c",
    "path": "converted/2026",
    "filename": "hero.avif"
  }'

The route answers 202 Accepted and the save runs in the background through the same pipeline as a normal upload: the file is finalized, scanned, encrypted if the bucket requires it, and given a stable link. Poll the job until savedAssetId is set; that is the new file's asset ID. path is an optional folder (.. is rejected), and filename defaults to the result's output name.

Saving uses workspace storage like any other file but no additional credits. Saving the same job twice does nothing the second time. Only the job's owner can save it, and a signed-in user needs a role that can edit files; viewers cannot save results or run conversions with workspace credits. With API keys, reading jobs needs assets:read and creating, starting, saving, and cancelling need assets:write.

Cancel or clean up#

DELETE /api/conversions/{job_id} cancels a job that has not finished and refunds its credit reservation. On a finished job, it expires the job immediately, hiding it from recent lists; its stored files are removed shortly after.

Next steps#