Batch image optimization: 250 photos in one API call

Optimize up to 250 images in one batch call. Read the per-image results, store each signed URL and pay only for the images that succeeded.

A grid of many photographs, one of them marked failed, fanning into a stack of results that are ready

Optimizing one photo is a single request. Optimizing a gallery, a product catalogue or a day of inspection photos is a batch problem. You want one call, one set of options, a result for every image and no failed photo spoiling the rest.

The JustAPI image optimization API has a batch endpoint for that. This article shows the request, how to read the results, and how to avoid paying for images you never use.

The request

POST https://api.justapi.tech/v1/image/batch takes an images array and one shared options object. Each image is an object with either a url or a file_id, and an optional ref. The ref is your own identifier for the image. Use a value that is unique within the batch, because the results come back keyed by it.

curl https://api.justapi.tech/v1/image/batch \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "images": [
      { "url": "https://files.example.com/site/front-door.jpg", "ref": "front-door" },
      { "url": "https://files.example.com/site/roof.heic", "ref": "roof" },
      { "url": "https://files.example.com/site/meter.png", "ref": "meter" }
    ],
    "options": { "width": 1600, "format": "jpeg", "quality": 76, "progressive": false },
    "idempotency_key": "REPLACE_WITH_STORED_KEY"
  }'

A batch can hold up to 250 images. Each image can be up to 50 MB. Keep progressive at false if the images will be placed in a PDF, because baseline JPEG is what PDF viewers handle reliably.

If one image needs different settings, add a per_image_options object keyed by ref. The values there override the shared options for that one image only.

Read the results

The response has a results array. Each entry carries the ref you sent and a status of ok or failed. A successful entry includes a signed url and the output details. A failed entry includes an error with a code and a message. The response also includes a summary with total, succeeded and failed.

A batch of images fanning out and back by refTwelve images are sent in one batch call. Results return keyed by ref: two succeed with signed URLs, and one fails with invalid_source. Only the successful image is charged.Up to 250 images in one callfront-doorok: signed urlroofok: signed urlmeterfailed: invalid_sourceMatch each result by ref
Each image carries its own ref, and results come back keyed by it. A failed image never stops the rest.

Match each result to your own record by ref, not by its position. Then handle the two cases separately:

  • For ok, save the URL and its expiry, and download the file before the link expires.
  • For failed, store the error code and show a useful message. A common case is invalid_source, which means the URL could not be downloaded, for example because the server returned 404.

One bad image never fails the others. If every image fails, the job is marked failed and nothing is charged.

Sync or async

A batch of up to 25 images is answered in the same request. A larger batch, or a call with "mode": "async", returns 202 with a job_id. You then read the results from GET /v1/jobs/{job_id}, or receive them at a webhook. Check the image API reference for the exact limits before you choose a timeout.

For a gallery upload from a user, the usual pattern is to accept the upload, store the files, and run the batch in the background. The user sees a status, not a spinning request.

Pair each result with its record

Store the ref with the record it belongs to before you send the batch, not after. A typical pattern is to write one row per image with a status of queued, send the batch, then update each row from the matching result. If the response is lost, you can list the rows still in queued and check the job instead of guessing what happened.

Keep the ref short and stable, such as a product id or a file name without spaces. Do not use the position in the array as the only link, because a reordered list breaks it.

Retries

Store one idempotency key for each batch and send it in the body. If your workflow retries after a timeout, the same key and the same body return the same job. Do not generate a new key on each retry, because that creates a second batch and a second charge.

Retries with one idempotency key return the same jobTwo calls with the same key and body point at one job, charged once. A retry with the same key returns that job and charges nothing more.Call 1: key K-7body carries idempotency_keyJob J-1 createdcharged onceRetry: same key, same bodyafter a timeout or a step rerunSame key and body: the same job, charged $0.00 again.A new key or a changed body: a new job, charged again.
One key and one body mean one job. A retry returns that job, and a new key is a new job.

What it costs

You pay per image that succeeded. At the first volume tier, the price is $0.0012 per image, with no batch surcharge. The price falls to $0.0010 for the next units and to $0.0008 after that. A batch of 1,000 successful images costs $1.20 at the first tier.

In a measured run, a batch of 250 images finished in 23 seconds and cost $0.20 at the prices in force at the time. Your timings will depend on the size of the source images and their hosting, so measure your own set before you promise a turnaround time to a customer.

Image optimization: price by monthly volume tierThree columns with heights drawn to scale from the tier prices in site data: tier 1 is the highest, tier 2 lower and tier 3 lowest. Each tier applies only to the units inside its range of the month.Image optimizationPrice per image, by monthly tier$0.0012Tier 11 to 1,000$0.001Tier 21,001 to 10,000$0.0008Tier 3above 10,000
Price per image by monthly tier, drawn to scale. Units above each break are charged at that tier.

The pricing page has the tiers and a calculator. The rule is simple: an image that fails is not charged, and a result that is not smaller than its source is returned without a charge.

Practical checks

Before you run a large batch, test a small set of around 20 images from the same source. Check that:

  1. Every ref in the results matches a record in your system.
  2. Each ok URL downloads a file of the expected format and width.
  3. The HEIC photos are rotated the right way. The HEIC pipeline guide explains the checks for iPhone photos.
  4. Failed entries show a sensible error that your team can act on.

Remove the test files from your storage when the check is done. Signed result URLs expire, and each file can be deleted once you have a copy.

Start with a small batch

Start free at app.justapi.tech. New accounts get 1 USD of credit with no card, which covers many test batches. If you only need to optimize a few photos, the free image optimizer handles one image up to 3 MB at a time.