Reference

Page scans API

Scan a web page for every image it loads, compress them all, and download them as one zip. Every page scans endpoint with samples.

A page scan opens a web page in a real browser on Iminify's servers, waits for it to load, scrolls to the bottom so lazy images load too, and collects every image the page loaded along the way. Each one is then downloaded and compressed with the settings you gave the scan, as an image of its own on your account.

It's the same scanner as the website's. A scan counts one against your daily page scans; the images it finds don't count against your daily images.

The life of a scan

TEXT
queued ──► scanning ──► completed ──► (its images compress)
   │                ├──► no-image-found
   │                └──► failed
   └──► cancelled

The scanner itself takes from a few seconds to a few minutes, depending on the page. When it's done the scan is completed and its images are queued, and they keep compressing after that. The scan is fully done once its status has ended and images_in_progress is 0. note says what the scanner found and what it did with each image, or why it failed.

A scan passes over SVG and AVIF files, images larger than your plan's file size, tiny tracking pixels, and the same image found twice. It stops at your plan's images per scan. A page on a private network is refused, and a page behind a login usually comes back with no images, because the scanner sees what a signed-out visitor sees.

A whole page in one script

Scan a page, wait for the scanner and every image, then download them all as one zip:

#!/usr/bin/env bash
# Scan a page, wait for the scanner and every image it found, save them as one zip.
# Needs curl 7.76 or newer and jq.
set -euo pipefail

API="https://www.iminify.com/api/v1"
AUTH="Authorization: Bearer $IMINIFY_API_KEY"

# 1. Queue the scan with the settings every image gets.
id=$(curl -sS --fail-with-body "$API/scans" \
  -H "$AUTH" -H "Accept: application/json" -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/pricing", "format": "webp"}' \
  | jq -r '.data.id')

# 2. Wait for the scanner, then for the images it queued.
while true; do
  scan=$(curl -sS --fail-with-body "$API/scans/$id" -H "$AUTH" -H "Accept: application/json")
  status=$(jq -r '.data.status' <<< "$scan")
  pending=$(jq -r '.data.images_in_progress' <<< "$scan")

  case "$status" in
    queued|scanning) ;;
    completed) [ "$pending" -eq 0 ] && break ;;
    *) jq -r '.data.note' <<< "$scan" >&2; exit 1 ;;
  esac

  sleep 5
done

jq -r '.data | "\(.images_finished) images, \(.saved_percent)% smaller"' <<< "$scan"

# 3. Download every finished image as one zip.
curl -sS --fail-with-body -o scan.zip "$API/scans/$id/download" -H "$AUTH"

The Scan object

id string
The scan's id.
url string
The page that was scanned.
status string
Where the scanner is. queued: waiting for the scanner. scanning: the page is open and its images are being collected. completed: images were found and queued. no-image-found: the page loaded but nothing on it could be compressed. failed: the page could not be scanned. cancelled: called off before it started. The images themselves keep compressing after completed; watch images_in_progress.
note string or null
What the scanner wrote when it finished: how many images it found and what became of each, or why it failed. null until then.
settings object
The settings every image the scan found is compressed with.
settings.level string
The compression level.
settings.format string or null
The format every image is converted to, or null to keep each one's own.
settings.keep_metadata boolean
Whether EXIF metadata is kept.
settings.width integer or null
The width every image is resized to, or null.
settings.height integer or null
The height every image is resized to, or null.
settings.scale integer or null
The percentage every image is scaled to, or null.
images_count integer
How many images the scan queued.
images_in_progress integer
How many of them are still queued or being compressed. The scan is done once its status has ended and this is 0.
images_finished integer
How many of them have finished.
original_size integer
Bytes of the finished images before compression.
optimized_size integer
Bytes of the same images after compression.
saved_bytes string
The difference between the two.
saved_percent number or null
The same saving as a percentage, or null while no image has finished.
download_url string
Where to download every finished image as one zip.
images array
The scan's images, newest first. Only on a single scan, not in a list.
created_at timestamp
When the scan was queued.
updated_at timestamp
When it last changed.

List page scans

GET /api/v1/scans

Every page scan on your account, newest first, each with its counts and totals. The images themselves come with a single scan, or from the image list filtered by scan.

Query parameters

status
string
Only scans with this status.

queued scanning completed failed no-image-found cancelled

per_page
integer
Scans a page, 1 to 100. 1 to 100 Default: 15
page
integer
The page to read. Default: 1

Request

curl "https://www.iminify.com/api/v1/scans?status=completed" \
  -H "Authorization: Bearer $IMINIFY_API_KEY" \
  -H "Accept: application/json"

Response 200

JSON
{
    "data": [
        {
            "id": "9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7",
            "url": "https://example.com/pricing",
            "status": "completed",
            "note": "Rendered in a headless browser. 14 image URLs found: 12 queued for compression, 1 duplicates of an image already queued, 1 in a format the compressor does not accept.",
            "settings": {
                "level": "smart",
                "format": "webp",
                "keep_metadata": false,
                "width": 1600,
                "height": null,
                "scale": null
            },
            "images_count": 12,
            "images_in_progress": 0,
            "images_finished": 12,
            "original_size": 18406331,
            "optimized_size": 2114902,
            "saved_bytes": 16291429,
            "saved_percent": 88.51,
            "download_url": "https://www.iminify.com/api/v1/scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7/download",
            "created_at": "2026-10-01T11:05:00+00:00",
            "updated_at": "2026-10-01T11:05:41+00:00"
        }
    ],
    "links": {
        "first": "https://www.iminify.com/api/v1/scans?page=1",
        "last": "https://www.iminify.com/api/v1/scans?page=1",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "links": [
            {
                "url": null,
                "label": "&laquo; Previous",
                "page": null,
                "active": false
            },
            {
                "url": "https://www.iminify.com/api/v1/scans?page=1",
                "label": "1",
                "page": 1,
                "active": true
            },
            {
                "url": null,
                "label": "Next &raquo;",
                "page": null,
                "active": false
            }
        ],
        "path": "https://www.iminify.com/api/v1/scans",
        "per_page": 15,
        "to": 1,
        "total": 1
    }
}

Scan a page

POST /api/v1/scans

Opens the page in a browser on Iminify's servers, waits for it to load, scrolls to the bottom and collects every image it loaded, then compresses each one with the settings you send. It answers straight away with the status queued; a scan takes from a few seconds to a few minutes. Each scan counts one against your daily scans and your page scans a minute, and the images it finds don't count against your daily images. A scan stops at your plan's images per scan, skips SVG and AVIF files and anything over your plan's file size, and compresses an image that appears twice once.

The address can leave out https://. A page on a private network is refused. A page behind a login, or one that blocks automated browsers, usually comes back with no images, because the scanner sees what a signed-out visitor would.

Body (JSON)

url
string (URL) required
The page to scan. https:// can be left out.
level
string
How hard to compress. smart finds the lowest quality that still looks like your upload, ultra goes further and softens fine texture a little, lossless changes no pixel, none only applies the format, the resize and the metadata choice. See Compression settings.

none lossless ultra smart

Default: smart
format
string
Convert to this format. auto encodes the image in every format that can hold it and keeps the smallest. Leave it out to keep the upload's own format; a HEIC or TIFF always comes back in another one, because Iminify doesn't write either. jpeg is read as jpg.

auto webp avif png jpg

keep_metadata
boolean
Keep the EXIF metadata (camera, date, location) in the optimized copy. Off by default, which strips it; the orientation is applied to the pixels first, so a photo never comes back on its side. Default: false
width
integer
Resize to this width in pixels. Alone, the height follows the aspect ratio. With height too, the image is resized to exactly that size, and its proportions change if they differ. Up to 65535. 1 to 65535
height
integer
Resize to this height in pixels, the same way as width. Up to 65535. 1 to 65535
scale
integer
Resize to this percentage of the original, 1 to 100. Cannot be combined with width or height. 1 to 100

Request

curl -X POST "https://www.iminify.com/api/v1/scans" \
  -H "Authorization: Bearer $IMINIFY_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/pricing","level":"smart","format":"webp"}'

Response 202

JSON
{
    "data": {
        "id": "9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7",
        "url": "https://example.com/pricing",
        "status": "queued",
        "note": null,
        "settings": {
            "level": "smart",
            "format": "webp",
            "keep_metadata": false,
            "width": 1600,
            "height": null,
            "scale": null
        },
        "images_count": 0,
        "images_in_progress": 0,
        "images_finished": 0,
        "original_size": 0,
        "optimized_size": 0,
        "saved_bytes": 0,
        "saved_percent": null,
        "download_url": "https://www.iminify.com/api/v1/scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7/download",
        "created_at": "2026-10-01T11:05:00+00:00",
        "updated_at": "2026-10-01T11:05:00+00:00"
    }
}

Get a page scan

GET /api/v1/scans/{id}

One scan with every image it found, newest first. Poll this while a scan runs: it is done once its status is no longer queued or scanning and images_in_progress is 0.

Path parameters

id
string (id) required
The scan's id.

Request

curl "https://www.iminify.com/api/v1/scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7" \
  -H "Authorization: Bearer $IMINIFY_API_KEY" \
  -H "Accept: application/json"

Response 200

JSON
{
    "data": {
        "id": "9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7",
        "url": "https://example.com/pricing",
        "status": "completed",
        "note": "Rendered in a headless browser. 14 image URLs found: 12 queued for compression, 1 duplicates of an image already queued, 1 in a format the compressor does not accept.",
        "settings": {
            "level": "smart",
            "format": "webp",
            "keep_metadata": false,
            "width": 1600,
            "height": null,
            "scale": null
        },
        "images_count": 12,
        "images_in_progress": 0,
        "images_finished": 12,
        "original_size": 18406331,
        "optimized_size": 2114902,
        "saved_bytes": 16291429,
        "saved_percent": 88.51,
        "download_url": "https://www.iminify.com/api/v1/scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7/download",
        "images": [
            {
                "id": "9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35",
                "status": "completed",
                "name": "team-offsite.webp",
                "original": {
                    "name": "team-offsite.jpg",
                    "format": "jpg",
                    "size": 2841205,
                    "width": 4032,
                    "height": 3024,
                    "download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/download?type=original"
                },
                "optimized": {
                    "name": "team-offsite.webp",
                    "format": "webp",
                    "size": 212877,
                    "width": 1600,
                    "height": 1200,
                    "download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/download"
                },
                "saved_bytes": 2628328,
                "saved_percent": 92.51,
                "settings": {
                    "level": "smart",
                    "keep_metadata": false,
                    "width": 1600,
                    "height": null,
                    "scale": null
                },
                "scan_id": "9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7",
                "share_url": null,
                "created_at": "2026-10-01T09:30:00+00:00",
                "updated_at": "2026-10-01T09:30:06+00:00"
            },
            {
                "id": "9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d",
                "status": "completed",
                "name": "hero-banner.webp",
                "original": {
                    "name": "hero-banner.jpg",
                    "format": "jpg",
                    "size": 2841205,
                    "width": 4032,
                    "height": 3024,
                    "download_url": "https://www.iminify.com/api/v1/images/9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d/download?type=original"
                },
                "optimized": {
                    "name": "hero-banner.webp",
                    "format": "webp",
                    "size": 212877,
                    "width": 1600,
                    "height": 1200,
                    "download_url": "https://www.iminify.com/api/v1/images/9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d/download"
                },
                "saved_bytes": 2628328,
                "saved_percent": 92.51,
                "settings": {
                    "level": "smart",
                    "keep_metadata": false,
                    "width": 1600,
                    "height": null,
                    "scale": null
                },
                "scan_id": "9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7",
                "share_url": null,
                "created_at": "2026-10-01T09:30:00+00:00",
                "updated_at": "2026-10-01T09:30:06+00:00"
            }
        ],
        "created_at": "2026-10-01T11:05:00+00:00",
        "updated_at": "2026-10-01T11:05:41+00:00"
    }
}

Delete a page scan

DELETE /api/v1/scans/{id}

Deletes the scan and every image it produced, with their files, for good. A scan that is still running, or has an image still compressing, is refused.

Path parameters

id
string (id) required
The scan's id.

Request

curl -X DELETE "https://www.iminify.com/api/v1/scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7" \
  -H "Authorization: Bearer $IMINIFY_API_KEY" \
  -H "Accept: application/json"

Response 204

No content.

Retry a page scan

POST /api/v1/scans/{id}/retry

Queues a failed or cancelled scan again with the settings it had. It counts as a scan.

Path parameters

id
string (id) required
The scan's id.

Request

curl -X POST "https://www.iminify.com/api/v1/scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7/retry" \
  -H "Authorization: Bearer $IMINIFY_API_KEY" \
  -H "Accept: application/json"

Response 202

JSON
{
    "data": {
        "id": "9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7",
        "url": "https://example.com/pricing",
        "status": "queued",
        "note": null,
        "settings": {
            "level": "smart",
            "format": "webp",
            "keep_metadata": false,
            "width": 1600,
            "height": null,
            "scale": null
        },
        "images_count": 0,
        "images_in_progress": 0,
        "images_finished": 0,
        "original_size": 0,
        "optimized_size": 0,
        "saved_bytes": 0,
        "saved_percent": null,
        "download_url": "https://www.iminify.com/api/v1/scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7/download",
        "created_at": "2026-10-01T11:05:00+00:00",
        "updated_at": "2026-10-01T11:05:00+00:00"
    }
}

Cancel a page scan

POST /api/v1/scans/{id}/cancel

Calls off a scan still waiting in the queue. Once the scanner has started, the scan runs to the end; its images can still be cancelled one by one.

Path parameters

id
string (id) required
The scan's id.

Request

curl -X POST "https://www.iminify.com/api/v1/scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7/cancel" \
  -H "Authorization: Bearer $IMINIFY_API_KEY" \
  -H "Accept: application/json"

Response 200

JSON
{
    "data": {
        "id": "9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7",
        "url": "https://example.com/pricing",
        "status": "cancelled",
        "note": null,
        "settings": {
            "level": "smart",
            "format": "webp",
            "keep_metadata": false,
            "width": 1600,
            "height": null,
            "scale": null
        },
        "images_count": 0,
        "images_in_progress": 0,
        "images_finished": 0,
        "original_size": 0,
        "optimized_size": 0,
        "saved_bytes": 0,
        "saved_percent": null,
        "download_url": "https://www.iminify.com/api/v1/scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7/download",
        "created_at": "2026-10-01T11:05:00+00:00",
        "updated_at": "2026-10-01T11:05:20+00:00"
    }
}

Download a page scan as a zip

GET /api/v1/scans/{id}/download

One zip with the optimized copy of every image of the scan that has finished, in the order the scanner found them. Images still compressing are left out, so wait for images_in_progress to reach 0 first.

Path parameters

id
string (id) required
The scan's id.

Request

curl --fail-with-body -o scan.zip "https://www.iminify.com/api/v1/scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7/download" \
  -H "Authorization: Bearer $IMINIFY_API_KEY"

Response 200

The file itself, not JSON.