Skip to content

Quickstart

From a key to a background removed and a public URL, in whichever of the SDKs, the CLI, curl or an MCP client you use. Then the same image through a saved preset of four steps, and the one-call road for when you want nothing stored.

  1. 1. Get a key

    Sign in and create an API key — it is shown once. The Free plan needs no card and comes with a $3 welcome credit for AI ops. Every call sends the key as Authorization: ApiKey is_sk_…; every sample below reads it from IMAGESTEP_API_KEY. Install what you will call from:

    pnpm add imagestep           # JavaScript / TypeScript
    pip install imagestep        # Python
    pnpm add -g imagestep-cli    # the CLI
    export IMAGESTEP_API_KEY=is_sk_…
  2. 2. Upload, then run an op

    Over REST an upload is one call — POST /api/v1/assets/upload, the image as the body with its own Content-Type, up to 25 MB — and the answer is the asset. The SDKs, the CLI and MCP take a file of any size in one line (assets has the three-step route they use underneath). An op on that asset is a job, {"op": "remove_bg", "assetIds": [...]}, and the same request with ?dryRun=true prices it first and creates nothing.

    import { ImageStep } from "imagestep";
    const client = new ImageStep({ apiKey: process.env.IMAGESTEP_API_KEY });
    
    const asset = await client.assets.upload("./product.jpg");
    const price = await client.ops.estimate("remove_bg", { assetIds: asset.id }); // nothing is created
    const job = await client.ops.removeBg(asset.id, { wait: true });
    const [cutout] = await client.jobs.outputs(job);
    const [published] = await client.assets.publish(cutout.id);
    console.log(published.publicUrl);

    Uploading is for work you want kept. If you are holding an image and only want the result back, the shorter road below is one call.

  3. 3. Get the result

    wait holds the response until a one-item job is done, 60 s at most: 200 with the finished job, or 202 while it is still running — GET /api/v1/jobs/{id}?wait= then waits again. A batch answers at once and ignores wait: read it by id the same way, or register a webhook for job.completed and job.failed and never wait at all (webhooks).

    Each output is a new asset — items[i].resultAssetId of the finished job — and the input is left as it was. Publish an output and it gets a publicUrl on cdn.imagestep.dev that does not change; or download its bytes. What each of these things is — assets, ops, jobs — has its own page; every error you can meet is on errors, retries & limits.

Next: several steps, one job

A preset is a list of steps you save once — versioned, under your account — and then run by name. This one is the preset the home page runs: cut the product out, trim it, put it on white, get it under 50 KB.

{
  "name": "Marketplace cut-out",
  "slug": "marketplace-cutout",
  "description": "Background removed, trimmed to the product, on white, a WebP under 50 KB.",
  "steps": [
    {
      "op": "remove_bg"
    },
    {
      "op": "trim"
    },
    {
      "op": "flatten",
      "parameters": {
        "background": "#ffffff"
      }
    },
    {
      "op": "compress",
      "parameters": {
        "format": "webp",
        "maxBytes": 50000
      }
    }
  ]
}
import { readFile } from "node:fs/promises";

const preset = await client.presets.create(JSON.parse(await readFile("marketplace-cutout.json", "utf8")));
console.log(preset.slug, preset.version); // "marketplace-cutout" 1

Then price it and run it over the asset you uploaded above. One of its steps is a model, so it runs as a single chain job: the intermediate images are not yours to manage, and the bill is per step.

const price = await client.presets.run("marketplace-cutout", ["<asset-id>"], { dryRun: true }); // nothing is created
console.log(price.estimatedCredits, price.steps);

const job = await client.presets.run("marketplace-cutout", ["<asset-id>"], { wait: true });
const outputs = await client.jobs.outputs(job);

The dry run lists the steps the way they are billed — the model at its price (227 credits is the $0.0227 the catalogue lists for remove_bg), the three deterministic steps as one process segment at 0:

{
  "type": "chain",
  "presetId": "pre_66a38dbbde6b43659060c8fa7c492173",
  "presetName": "Marketplace cut-out",
  "presetVersion": 1,
  "totalItems": 1,
  "costPerItem": 227,
  "estimatedCredits": 227,
  "creditBalance": 30000,
  "sufficientCredit": true,
  "assetCountLeft": 199,
  "processCountLeft": 200,
  "overageRuns": 0,
  "overageCredits": 0,
  "processPerItem": 1,
  "steps": [
    { "index": 0, "op": "remove_bg", "model": "fal-ai/bria/background/remove", "costPerItem": 227, "maxInputEdge": 2048 },
    { "index": 1, "op": "process", "costPerItem": 0 }
  ]
}

What happens when a step fails, what you pay for then, and how resume picks up from that step: chains. More presets to copy, this one included: recipes.

The shorter road: one call, nothing stored

Everything above is a job: this service has promised to finish the work, which is what the asset, the price and the webhook are for. When you are holding the image and only want the result back, a deterministic op — resize, convert, compress, crop and the rest of what GET /api/v1/ops marks with a syncEndpoint — runs while you wait: bytes in, bytes out, no upload, no asset, no publish. It is free on a paid plan; on Free it counts against the monthly allowance of deterministic ops and is paid from your balance past it. If it fails you send it again, so there is no Idempotency-Key either. AI ops never go this way; a provider call needs an owner for its retry and refund.

import { writeFile } from "node:fs/promises";

const small = await client.images.transform("resize", { file: "./product.jpg", parameters: { width: 1200 } });
await writeFile("out.jpg", small);

A saved preset runs several steps in the same one call — if every step is deterministic: marketplace-cutout above starts with a model, so it is a job and only a job. A template renders to a PNG, and reading metadata is free — limits and the line between the two roads are on synchronous ops.

Not writing code?

The same two roads exist where there is no code to paste. Each page opens with its own install and first call.

  • n8n — install the community node, add the ImageStep credential, pick an op from the Op dropdown. Store Result off is the one-call road; on is a job with an asset id and a permanent URL.
  • Claude, Cursor or any MCP client — one config block, hosted or local; the tab above is what the agent then calls.
  • A coding agent with a shell — imagestep skill --install claude-code teaches it to drive the CLI.

An agent choosing between these starts on the agents page.