Transform an image synchronously
Runs ONE deterministic op — or ONE saved preset with no AI step — on ONE image and answers with the result bytes. Nothing is stored: no job, no asset, no public URL.
The image is the request body; the op and its parameters are query parameters: ?op=resize&width=1200. An op takes only what its catalogue entry declares, so resizing AND re-encoding is two steps, and two steps are a preset: ?preset=<slug> (or <slug>@<version>) runs all of its steps in this one call, and takes no parameters beside it. Which ops may be used here is GET /api/v1/ops → syncEndpoint; do not hard-code a list.
THREE WAYS TO SEND THE IMAGE: the raw bytes with their own Content-Type (the format is read from that header, so application/octet-stream on a camera RAW is unsupported_format); multipart/form-data with a file part; or application/json with a reference instead of bytes — {assetId} (one of your assets) or {url} (fetched by this service: http(s) only, no private addresses, no redirects) — and then op / preset / parameters may ride in the body too; the query string wins where both say something.
Which presets may be used here follows from their steps: only deterministic steps, however many, run here. A preset with an AI step, or with a step that reads a stored layer (composite, overlay) — or any other step that names one of your assets as its second image (boolean, joinChannel) — is 400 invalid_param on preset: it is still a valid preset, and the same reference runs as a job.
LIMITS: a body of at most 25 MB, an input of at most 50 megapixels (a bigger one is 400 unsupported_format: it is refused on the size its header declares, before it is decoded), 10 s of wall clock (503, deadline_exceeded). At most 4 calls in flight per account: one more is 429 rate_limited with details.reason = "account_concurrency" and Retry-After: 1. A success counts one against the plan's processing allowance, however many steps a preset holds; past it, a success is paid from your balance at the plan's overageCredits (GET /api/v1/ops), and a call the balance cannot cover is 402 insufficient_credit before anything runs. A failure counts and costs nothing. No Idempotency-Key: nothing is created, so send it again.
USE THIS WHEN:
- You are holding an image and only want the result back
- You do not want the input or the output stored
DO NOT USE WHEN:
- The op or a preset step is an AI op, or you have more than one image → POST /api/v1/jobs
- You want an asset id or a permanent URL → POST /api/v1/jobs
Request
- Base URL: https://api.imagestep.dev
- URL: https://api.imagestep.dev/api/v1/images/transform
- Auth: API key in header Authorization
Query parameters
Op name; must be one whose GET /api/v1/ops entry has a syncEndpoint. Mutually exclusive with preset.
A deterministic preset to run as one call: its slug or id, or slug@version to pin one version. Mutually exclusive with op.
url to get JSON with a signed link to the result (valid 5 minutes) instead of the bytes; anything else, or nothing, answers the bytes. The one part of this endpoint that writes storage: a temporary object, gone within a day.
Request body
Response
The transformed image, Content-Type the produced format, with X-ImageStep-Width / X-ImageStep-Height and X-ImageStep-Duration-Ms. With ?response=url, JSON instead: data is a signed link to the result, valid for 5 minutes.
Example response
{
"success": true,
"message": "Operation completed successfully",
"timestamp": "2024-01-15T10:30:45.123Z",
"error": {
"code": "invalid_param",
"message": "format must be one of: jpeg, png, webp",
"param": "format",
"requestId": "9f0c1c2e-6f2a-4f6b-9d3e-0a1b2c3d4e5f"
}
}Changes
No recorded changes to this operation across all 1 revision of this API.