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
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"
"""Scan a page, wait for the scanner and every image it found, save them as one zip.
Needs Python 3.8 or newer and the requests package (pip install requests).
"""
import os
import time
import requests
API = "https://www.iminify.com/api/v1"
session = requests.Session()
session.headers.update({
"Authorization": f"Bearer {os.environ['IMINIFY_API_KEY']}",
"Accept": "application/json",
})
def running(scan):
return scan["status"] in ("queued", "scanning") or (scan["status"] == "completed" and scan["images_in_progress"] > 0)
# 1. Queue the scan with the settings every image gets.
response = session.post(f"{API}/scans", json={"url": "https://example.com/pricing", "format": "webp"})
response.raise_for_status()
scan = response.json()["data"]
# 2. Wait for the scanner, then for the images it queued.
while running(scan):
time.sleep(5)
response = session.get(f"{API}/scans/{scan['id']}")
response.raise_for_status()
scan = response.json()["data"]
if scan["status"] != "completed":
raise SystemExit(scan["note"] or f"The scan ended as {scan['status']}")
print(f"{scan['images_finished']} images, {scan['saved_percent']}% smaller")
# 3. Download every finished image as one zip.
response = session.get(scan["download_url"])
response.raise_for_status()
with open("scan.zip", "wb") as out:
out.write(response.content)
<?php
declare(strict_types=1);
// Scan a page, wait for the scanner and every image it found, save them as one zip.
// Needs PHP 8.1 or newer and Guzzle (composer require guzzlehttp/guzzle).
require __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Client;
$client = new Client([
'base_uri' => 'https://www.iminify.com/api/v1/',
'headers' => [
'Authorization' => 'Bearer ' . getenv('IMINIFY_API_KEY'),
'Accept' => 'application/json',
],
]);
$running = fn (array $scan): bool => in_array($scan['status'], ['queued', 'scanning'], true)
|| ('completed' === $scan['status'] && $scan['images_in_progress'] > 0);
// 1. Queue the scan with the settings every image gets.
$response = $client->post('scans', [
'json' => ['url' => 'https://example.com/pricing', 'format' => 'webp'],
]);
$scan = json_decode((string) $response->getBody(), true)['data'];
// 2. Wait for the scanner, then for the images it queued.
while ($running($scan)) {
sleep(5);
$response = $client->get("scans/{$scan['id']}");
$scan = json_decode((string) $response->getBody(), true)['data'];
}
if ('completed' !== $scan['status']) {
fwrite(STDERR, ($scan['note'] ?? "The scan ended as {$scan['status']}") . "\n");
exit(1);
}
echo "{$scan['images_finished']} images, {$scan['saved_percent']}% smaller\n";
// 3. Download every finished image as one zip.
$client->get("scans/{$scan['id']}/download", ['sink' => 'scan.zip']);
// Scan a page, wait for the scanner and every image it found, save them as one zip.
// Needs Node 20 or newer. Save it as scan.mjs and run: node scan.mjs
import { writeFile } from 'node:fs/promises';
import { setTimeout as sleep } from 'node:timers/promises';
const API = 'https://www.iminify.com/api/v1';
const call = async (path, options = {}) => {
const response = await fetch(`${API}${path}`, {
...options,
headers: {
Authorization: `Bearer ${process.env.IMINIFY_API_KEY}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`${response.status}: ${(await response.json()).message}`);
}
return response;
};
const running = (scan) =>
['queued', 'scanning'].includes(scan.status) || (scan.status === 'completed' && scan.images_in_progress > 0);
// 1. Queue the scan with the settings every image gets.
const queued = await call('/scans', {
method: 'POST',
body: JSON.stringify({ url: 'https://example.com/pricing', format: 'webp' }),
});
let { data: scan } = await queued.json();
// 2. Wait for the scanner, then for the images it queued.
while (running(scan)) {
await sleep(5000);
({ data: scan } = await (await call(`/scans/${scan.id}`)).json());
}
if (scan.status !== 'completed') {
throw new Error(scan.note ?? `The scan ended as ${scan.status}`);
}
console.log(`${scan.images_finished} images, ${scan.saved_percent}% smaller`);
// 3. Download every finished image as one zip.
const download = await call(`/scans/${scan.id}/download`);
await writeFile('scan.zip', Buffer.from(await download.arrayBuffer()));
The Scan object
-
idstring - The scan's id.
-
urlstring - The page that was scanned.
-
statusstring -
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 aftercompleted; watchimages_in_progress. -
notestring or null -
What the scanner wrote when it finished: how many images it found and what became of each, or why it failed.
nulluntil then. -
settingsobject - The settings every image the scan found is compressed with.
-
settings.levelstring - The compression level.
-
settings.formatstring or null -
The format every image is converted to, or
nullto keep each one's own. -
settings.keep_metadataboolean - Whether EXIF metadata is kept.
-
settings.widthinteger or null -
The width every image is resized to, or
null. -
settings.heightinteger or null -
The height every image is resized to, or
null. -
settings.scaleinteger or null -
The percentage every image is scaled to, or
null. -
images_countinteger - How many images the scan queued.
-
images_in_progressinteger - How many of them are still queued or being compressed. The scan is done once its status has ended and this is 0.
-
images_finishedinteger - How many of them have finished.
-
original_sizeinteger - Bytes of the finished images before compression.
-
optimized_sizeinteger - Bytes of the same images after compression.
-
saved_bytesstring - The difference between the two.
-
saved_percentnumber or null -
The same saving as a percentage, or
nullwhile no image has finished. -
download_urlstring - Where to download every finished image as one zip.
-
imagesarray - The scan's images, newest first. Only on a single scan, not in a list.
-
created_attimestamp - When the scan was queued.
-
updated_attimestamp - When it last changed.
List page scans
/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
-
statusstring -
Only scans with this status.
queuedscanningcompletedfailedno-image-foundcancelled -
per_pageinteger - Scans a page, 1 to 100. 1 to 100 Default: 15
-
pageinteger - 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"
import os
import requests
API = "https://www.iminify.com/api/v1"
HEADERS = {
"Authorization": f"Bearer {os.environ['IMINIFY_API_KEY']}",
"Accept": "application/json",
}
response = requests.get(
f"{API}/scans",
headers=HEADERS,
params={
"status": "completed",
},
)
response.raise_for_status()
print(response.json())
<?php
require __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Client;
$client = new Client([
'base_uri' => 'https://www.iminify.com/api/v1/',
'headers' => [
'Authorization' => 'Bearer ' . getenv('IMINIFY_API_KEY'),
'Accept' => 'application/json',
],
]);
$response = $client->get('scans', [
'query' => [
'status' => 'completed',
],
]);
print_r(json_decode((string) $response->getBody(), true));
const response = await fetch('https://www.iminify.com/api/v1/scans?status=completed', {
headers: {
Authorization: `Bearer ${process.env.IMINIFY_API_KEY}`,
Accept: 'application/json',
},
});
if (!response.ok) {
throw new Error(`${response.status}: ${(await response.json()).message}`);
}
console.log(await response.json());
Response 200
{
"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": "« 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 »",
"page": null,
"active": false
}
],
"path": "https://www.iminify.com/api/v1/scans",
"per_page": 15,
"to": 1,
"total": 1
}
}
Scan a page
/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)
-
urlstring (URL) required -
The page to scan.
https://can be left out. -
levelstring -
How hard to compress.
smartfinds the lowest quality that still looks like your upload,ultragoes further and softens fine texture a little,losslesschanges no pixel,noneonly applies the format, the resize and the metadata choice. See Compression settings.
Default: smartnonelosslessultrasmart -
formatstring -
Convert to this format.
autoencodes 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.jpegis read asjpg.autowebpavifpngjpg -
keep_metadataboolean - 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
-
widthinteger -
Resize to this width in pixels. Alone, the height follows the aspect ratio. With
heighttoo, the image is resized to exactly that size, and its proportions change if they differ. Up to 65535. 1 to 65535 -
heightinteger -
Resize to this height in pixels, the same way as
width. Up to 65535. 1 to 65535 -
scaleinteger -
Resize to this percentage of the original, 1 to 100. Cannot be combined with
widthorheight. 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"}'
import os
import requests
API = "https://www.iminify.com/api/v1"
HEADERS = {
"Authorization": f"Bearer {os.environ['IMINIFY_API_KEY']}",
"Accept": "application/json",
}
response = requests.post(
f"{API}/scans",
headers=HEADERS,
json={
"url": "https://example.com/pricing",
"level": "smart",
"format": "webp",
},
)
response.raise_for_status()
print(response.json())
<?php
require __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Client;
$client = new Client([
'base_uri' => 'https://www.iminify.com/api/v1/',
'headers' => [
'Authorization' => 'Bearer ' . getenv('IMINIFY_API_KEY'),
'Accept' => 'application/json',
],
]);
$response = $client->post('scans', [
'json' => [
'url' => 'https://example.com/pricing',
'level' => 'smart',
'format' => 'webp',
],
]);
print_r(json_decode((string) $response->getBody(), true));
const response = await fetch('https://www.iminify.com/api/v1/scans', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.IMINIFY_API_KEY}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://example.com/pricing',
level: 'smart',
format: 'webp',
}),
});
if (!response.ok) {
throw new Error(`${response.status}: ${(await response.json()).message}`);
}
console.log(await response.json());
Response 202
{
"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
/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
-
idstring (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"
import os
import requests
API = "https://www.iminify.com/api/v1"
HEADERS = {
"Authorization": f"Bearer {os.environ['IMINIFY_API_KEY']}",
"Accept": "application/json",
}
response = requests.get(
f"{API}/scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7",
headers=HEADERS,
)
response.raise_for_status()
print(response.json())
<?php
require __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Client;
$client = new Client([
'base_uri' => 'https://www.iminify.com/api/v1/',
'headers' => [
'Authorization' => 'Bearer ' . getenv('IMINIFY_API_KEY'),
'Accept' => 'application/json',
],
]);
$response = $client->get('scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7');
print_r(json_decode((string) $response->getBody(), true));
const response = await fetch('https://www.iminify.com/api/v1/scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7', {
headers: {
Authorization: `Bearer ${process.env.IMINIFY_API_KEY}`,
Accept: 'application/json',
},
});
if (!response.ok) {
throw new Error(`${response.status}: ${(await response.json()).message}`);
}
console.log(await response.json());
Response 200
{
"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
/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
-
idstring (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"
import os
import requests
API = "https://www.iminify.com/api/v1"
HEADERS = {
"Authorization": f"Bearer {os.environ['IMINIFY_API_KEY']}",
"Accept": "application/json",
}
response = requests.delete(
f"{API}/scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7",
headers=HEADERS,
)
response.raise_for_status()
print(response.status_code) # 204, nothing in the body
<?php
require __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Client;
$client = new Client([
'base_uri' => 'https://www.iminify.com/api/v1/',
'headers' => [
'Authorization' => 'Bearer ' . getenv('IMINIFY_API_KEY'),
'Accept' => 'application/json',
],
]);
$response = $client->delete('scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7');
echo $response->getStatusCode(), PHP_EOL; // 204, nothing in the body
const response = await fetch('https://www.iminify.com/api/v1/scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.IMINIFY_API_KEY}`,
Accept: 'application/json',
},
});
if (!response.ok) {
throw new Error(`${response.status}: ${(await response.json()).message}`);
}
console.log(response.status); // 204, nothing in the body
Response 204
No content.
Retry a page scan
/api/v1/scans/{id}/retry
Queues a failed or cancelled scan again with the settings it had. It counts as a scan.
Path parameters
-
idstring (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"
import os
import requests
API = "https://www.iminify.com/api/v1"
HEADERS = {
"Authorization": f"Bearer {os.environ['IMINIFY_API_KEY']}",
"Accept": "application/json",
}
response = requests.post(
f"{API}/scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7/retry",
headers=HEADERS,
)
response.raise_for_status()
print(response.json())
<?php
require __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Client;
$client = new Client([
'base_uri' => 'https://www.iminify.com/api/v1/',
'headers' => [
'Authorization' => 'Bearer ' . getenv('IMINIFY_API_KEY'),
'Accept' => 'application/json',
],
]);
$response = $client->post('scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7/retry');
print_r(json_decode((string) $response->getBody(), true));
const response = await fetch('https://www.iminify.com/api/v1/scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7/retry', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.IMINIFY_API_KEY}`,
Accept: 'application/json',
},
});
if (!response.ok) {
throw new Error(`${response.status}: ${(await response.json()).message}`);
}
console.log(await response.json());
Response 202
{
"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
/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
-
idstring (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"
import os
import requests
API = "https://www.iminify.com/api/v1"
HEADERS = {
"Authorization": f"Bearer {os.environ['IMINIFY_API_KEY']}",
"Accept": "application/json",
}
response = requests.post(
f"{API}/scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7/cancel",
headers=HEADERS,
)
response.raise_for_status()
print(response.json())
<?php
require __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Client;
$client = new Client([
'base_uri' => 'https://www.iminify.com/api/v1/',
'headers' => [
'Authorization' => 'Bearer ' . getenv('IMINIFY_API_KEY'),
'Accept' => 'application/json',
],
]);
$response = $client->post('scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7/cancel');
print_r(json_decode((string) $response->getBody(), true));
const response = await fetch('https://www.iminify.com/api/v1/scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7/cancel', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.IMINIFY_API_KEY}`,
Accept: 'application/json',
},
});
if (!response.ok) {
throw new Error(`${response.status}: ${(await response.json()).message}`);
}
console.log(await response.json());
Response 200
{
"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
/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
-
idstring (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"
import os
import requests
API = "https://www.iminify.com/api/v1"
HEADERS = {
"Authorization": f"Bearer {os.environ['IMINIFY_API_KEY']}",
"Accept": "application/json",
}
response = requests.get(
f"{API}/scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7/download",
headers=HEADERS,
)
response.raise_for_status()
with open("scan.zip", "wb") as out:
out.write(response.content)
<?php
require __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Client;
$client = new Client([
'base_uri' => 'https://www.iminify.com/api/v1/',
'headers' => [
'Authorization' => 'Bearer ' . getenv('IMINIFY_API_KEY'),
'Accept' => 'application/json',
],
]);
$response = $client->get('scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7/download', [
'sink' => 'scan.zip',
]);
echo 'Saved scan.zip', PHP_EOL;
import { writeFile } from 'node:fs/promises';
const response = await fetch('https://www.iminify.com/api/v1/scans/9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7/download', {
headers: {
Authorization: `Bearer ${process.env.IMINIFY_API_KEY}`,
Accept: 'application/json',
},
});
if (!response.ok) {
throw new Error(`${response.status}: ${(await response.json()).message}`);
}
await writeFile('scan.zip', Buffer.from(await response.arrayBuffer()));
Response 200
The file itself, not JSON.