Images API
Compress an image from a file or a URL, poll it, download it, rename it, share it, run it again or delete it. Every images endpoint with samples.
An image is one compression: the file you sent, the settings you chose, and once it has finished, the optimized copy. Every image on your account is here, the ones you uploaded through the website included.
The life of an image
queued ──► optimizing ──► completed
│ ├──► already-optimized
│ └──► failed
└──► cancelled
An image is queued the moment you send it, and a worker usually picks it up within seconds. It ends in one of three ways: completed with a smaller copy, already-optimized when nothing came out smaller and the upload itself is handed back, or failed. While it's still queued you can cancel it. A failed or cancelled image can be retried, and any image that isn't running can be compressed again with other settings, which makes a new image and leaves the old one as it is.
Poll GET /images/{id} until the status is no longer queued or optimizing. Once it has finished, optimized holds the copy and download_url fetches it with your key.
Files and names
An image has two files. The original is exactly what you uploaded and keeps the name it came with. The optimized copy is named after it, with the extension of the format it was delivered in, until you rename it. Both stay in storage until you delete the image.
The Image object
-
idstring - The image's id.
-
statusstring -
Where the image is.
queued: waiting for a worker.optimizing: being compressed.completed: finished, the optimized copy is ready.already-optimized: finished, but nothing came out smaller than the upload, so the upload itself is handed back as the optimized copy.failed: it could not be compressed.cancelled: called off before it started. -
namestring -
The name the optimized copy downloads under: the uploaded name, or the one you gave it, with the delivered format's extension. While an
autoconversion is still queued its format isn't chosen, so this is the uploaded name until it finishes. -
originalobject - The file as it was uploaded.
-
original.namestring - The file name it downloads under.
-
original.formatstring -
The file's format, as its extension:
jpg,png,webp,avif,gif,heic,tiffand so on. -
original.sizeinteger - Its size in bytes.
-
original.widthinteger - Its width in pixels, the way it displays: a phone photo stored on its side is measured upright.
-
original.heightinteger - Its height in pixels, the way it displays.
-
original.download_urlstring - Where to download it with your key.
-
optimizedobject or null -
The optimized copy, or
nulluntil the image has finished. -
optimized.namestring - The file name it downloads under.
-
optimized.formatstring -
The file's format, as its extension:
jpg,png,webp,avif,gif,heic,tiffand so on. -
optimized.sizeinteger - Its size in bytes.
-
optimized.widthinteger - Its width in pixels, the way it displays: a phone photo stored on its side is measured upright.
-
optimized.heightinteger - Its height in pixels, the way it displays.
-
optimized.download_urlstring - Where to download it with your key.
-
saved_bytesinteger or null -
Bytes the optimized copy saves against the original. Negative when a conversion or an enlargement made the file bigger.
nulluntil the image has finished. -
saved_percentnumber or null -
The same saving as a percentage of the original, to two decimal places.
nulluntil the image has finished. -
settingsobject -
The settings the image was compressed with. The format it was converted to is
optimized.format. -
settings.levelstring - The compression level.
-
settings.keep_metadataboolean - Whether the EXIF metadata was kept.
-
settings.widthinteger or null -
The width it was resized to, or
null. -
settings.heightinteger or null -
The height it was resized to, or
null. -
settings.scaleinteger or null -
The percentage it was scaled to, or
null. -
scan_idstring or null -
The page scan that found the image, or
nullfor an upload. -
share_urlstring or null -
The public page while the image is shared, or
null. -
created_attimestamp - When the image was queued.
-
updated_attimestamp - When anything about it last changed, its status included.
List images
/api/v1/images
Every image on your account, newest first: your uploads, the images your page scans found, and every re-run. The website's results table lists the same images. Follow links.next for the next page.
Query parameters
-
statusstring -
Only images with this status.
queuedoptimizingcompletedfailedalready-optimizedcancelled -
sourcestring -
uploadfor images you sent (and their re-runs),scanfor the ones page scans found.uploadscan -
scanstring (id) - Only the images of this page scan.
-
per_pageinteger - Images 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/images?status=completed&per_page=20" \
-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}/images",
headers=HEADERS,
params={
"status": "completed",
"per_page": 20,
},
)
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('images', [
'query' => [
'status' => 'completed',
'per_page' => 20,
],
]);
print_r(json_decode((string) $response->getBody(), true));
const response = await fetch('https://www.iminify.com/api/v1/images?status=completed&per_page=20', {
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": "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": null,
"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": "queued",
"name": "IMG_4471.heic",
"original": {
"name": "IMG_4471.heic",
"format": "heic",
"size": 3402118,
"width": 4032,
"height": 3024,
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d/download?type=original"
},
"optimized": null,
"saved_bytes": null,
"saved_percent": null,
"settings": {
"level": "ultra",
"keep_metadata": true,
"width": null,
"height": null,
"scale": null
},
"scan_id": null,
"share_url": null,
"created_at": "2026-10-01T09:31:12+00:00",
"updated_at": "2026-10-01T09:31:12+00:00"
}
],
"links": {
"first": "https://www.iminify.com/api/v1/images?page=1",
"last": "https://www.iminify.com/api/v1/images?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/images?page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": null,
"label": "Next »",
"page": null,
"active": false
}
],
"path": "https://www.iminify.com/api/v1/images",
"per_page": 15,
"to": 2,
"total": 2
}
}
Compress an image
/api/v1/images
Send the image itself as a multipart file, or its address as url and Iminify fetches it. The answer comes straight back with the status queued and a Location header; the compression runs in the background, usually for a few seconds, so poll the image until its status is completed, already-optimized, failed or cancelled. Each image counts one against your daily images and your compressions a minute, whether it came through here or through the website.
The file goes through the same checks as an upload on the website: a format Iminify reads (PNG, JPG, JPEG, WEBP, GIF, HEIC, HEIF, TIFF, TIF), your plan's file size, a name of at most 128 bytes, and at most 64 megapixels, before and after any resize. An address has to be public http or https: Iminify fetches it from its own servers, refuses private networks on the address and on every redirect, follows up to five redirects, gives up after 30 seconds and stops as soon as the file passes your plan's size. The file is named after the address. Iminify fetches up to 2 addresses for you at a time; one more sent before those finish is refused with rate_limited.
Body (multipart/form-data, or JSON for a url)
-
filefile -
The image. Send either this or
url. -
urlstring (URL) -
The public address of the image to fetch. Send either this or
file. -
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/images" \
-H "Authorization: Bearer $IMINIFY_API_KEY" \
-H "Accept: application/json" \
-F "[email protected]" \
-F "level=smart" \
-F "format=webp" \
-F "width=1600"
import os
import requests
API = "https://www.iminify.com/api/v1"
HEADERS = {
"Authorization": f"Bearer {os.environ['IMINIFY_API_KEY']}",
"Accept": "application/json",
}
with open("photo.jpg", "rb") as file:
response = requests.post(
f"{API}/images",
headers=HEADERS,
data={
"level": "smart",
"format": "webp",
"width": "1600",
},
files={"file": file},
)
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('images', [
'multipart' => [
['name' => 'file', 'contents' => fopen('photo.jpg', 'r'), 'filename' => 'photo.jpg'],
['name' => 'level', 'contents' => 'smart'],
['name' => 'format', 'contents' => 'webp'],
['name' => 'width', 'contents' => '1600'],
],
]);
print_r(json_decode((string) $response->getBody(), true));
import { openAsBlob } from 'node:fs';
const form = new FormData();
form.append('file', await openAsBlob('photo.jpg'), 'photo.jpg');
form.append('level', 'smart');
form.append('format', 'webp');
form.append('width', '1600');
const response = await fetch('https://www.iminify.com/api/v1/images', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.IMINIFY_API_KEY}`,
Accept: 'application/json',
},
body: form,
});
if (!response.ok) {
throw new Error(`${response.status}: ${(await response.json()).message}`);
}
console.log(await response.json());
Response 202
{
"data": {
"id": "9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d",
"status": "queued",
"name": "IMG_4471.heic",
"original": {
"name": "IMG_4471.heic",
"format": "heic",
"size": 3402118,
"width": 4032,
"height": 3024,
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d/download?type=original"
},
"optimized": null,
"saved_bytes": null,
"saved_percent": null,
"settings": {
"level": "ultra",
"keep_metadata": true,
"width": null,
"height": null,
"scale": null
},
"scan_id": null,
"share_url": null,
"created_at": "2026-10-01T09:31:12+00:00",
"updated_at": "2026-10-01T09:31:12+00:00"
}
}
Get an image
/api/v1/images/{id}
One image, with its status. This is the call to poll while an image is compressing; once a second or two apart is plenty.
Path parameters
-
idstring (id) required - The image's id.
Request
curl "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35" \
-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}/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35",
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('images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35');
print_r(json_decode((string) $response->getBody(), true));
const response = await fetch('https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35', {
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": "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": null,
"share_url": null,
"created_at": "2026-10-01T09:30:00+00:00",
"updated_at": "2026-10-01T09:30:06+00:00"
}
}
Rename an image
/api/v1/images/{id}
Changes the name the optimized copy downloads under. The original keeps the name it was uploaded with. The extension is always the delivered format's, so a typed one (hero.webp) is dropped, and characters that don't belong in a file name become a hyphen. Only a finished image can be renamed.
Path parameters
-
idstring (id) required - The image's id.
Body (JSON)
-
namestring required - The new name, without an extension. With the extension it has to fit in 128 bytes.
Request
curl -X PATCH "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35" \
-H "Authorization: Bearer $IMINIFY_API_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"name":"team-offsite"}'
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.patch(
f"{API}/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35",
headers=HEADERS,
json={
"name": "team-offsite",
},
)
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->patch('images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35', [
'json' => [
'name' => 'team-offsite',
],
]);
print_r(json_decode((string) $response->getBody(), true));
const response = await fetch('https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.IMINIFY_API_KEY}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'team-offsite',
}),
});
if (!response.ok) {
throw new Error(`${response.status}: ${(await response.json()).message}`);
}
console.log(await response.json());
Response 200
{
"data": {
"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": null,
"share_url": null,
"created_at": "2026-10-01T09:30:00+00:00",
"updated_at": "2026-10-01T09:30:06+00:00"
}
}
Delete an image
/api/v1/images/{id}
Deletes the image and both of its files from storage for good. There is no trash and no undo. An image that is still queued or compressing is refused; cancel it while it is queued, or wait for it to finish.
Path parameters
-
idstring (id) required - The image's id.
Request
curl -X DELETE "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35" \
-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}/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35",
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('images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35');
echo $response->getStatusCode(), PHP_EOL; // 204, nothing in the body
const response = await fetch('https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35', {
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.
Download an image
/api/v1/images/{id}/download
The optimized copy, or with type=original the file exactly as it was uploaded. The answer is the file itself, with its type in Content-Type and its name in Content-Disposition. The original is there from the moment the image is queued; the optimized copy once it has finished.
Path parameters
-
idstring (id) required - The image's id.
Query parameters
-
typestring -
Which file: the optimized copy, or the original as it was uploaded.
Default: optimizedoptimizedoriginal
Request
curl --fail-with-body -o team-offsite.webp "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/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}/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/download",
headers=HEADERS,
)
response.raise_for_status()
with open("team-offsite.webp", "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('images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/download', [
'sink' => 'team-offsite.webp',
]);
echo 'Saved team-offsite.webp', PHP_EOL;
import { writeFile } from 'node:fs/promises';
const response = await fetch('https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/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('team-offsite.webp', Buffer.from(await response.arrayBuffer()));
Response 200
The file itself, not JSON.
Compress an image again
/api/v1/images/{id}/recompress
Runs the stored original through again with new settings and answers with a new image; the one you started from stays as it is. Nothing is uploaded again, but it counts as a compression like an upload does. It is how you try ultra after smart, or AVIF after WebP. Any image that isn't still running can go again, a failed or cancelled one included. A resize that would take the image past 64 megapixels is refused with the message under errors.image.
Path parameters
-
idstring (id) required - The image's id.
Body (JSON)
-
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/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/recompress" \
-H "Authorization: Bearer $IMINIFY_API_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"level":"ultra","format":"avif"}'
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}/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/recompress",
headers=HEADERS,
json={
"level": "ultra",
"format": "avif",
},
)
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('images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/recompress', [
'json' => [
'level' => 'ultra',
'format' => 'avif',
],
]);
print_r(json_decode((string) $response->getBody(), true));
const response = await fetch('https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/recompress', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.IMINIFY_API_KEY}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
level: 'ultra',
format: 'avif',
}),
});
if (!response.ok) {
throw new Error(`${response.status}: ${(await response.json()).message}`);
}
console.log(await response.json());
Response 202
{
"data": {
"id": "9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d",
"status": "queued",
"name": "IMG_4471.heic",
"original": {
"name": "IMG_4471.heic",
"format": "heic",
"size": 3402118,
"width": 4032,
"height": 3024,
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d/download?type=original"
},
"optimized": null,
"saved_bytes": null,
"saved_percent": null,
"settings": {
"level": "ultra",
"keep_metadata": true,
"width": null,
"height": null,
"scale": null
},
"scan_id": null,
"share_url": null,
"created_at": "2026-10-01T09:31:12+00:00",
"updated_at": "2026-10-01T09:31:12+00:00"
}
}
Retry an image
/api/v1/images/{id}/retry
Queues a failed or cancelled image again with the settings it had. It counts as a compression.
Path parameters
-
idstring (id) required - The image's id.
Request
curl -X POST "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/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}/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/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('images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/retry');
print_r(json_decode((string) $response->getBody(), true));
const response = await fetch('https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/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": "9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d",
"status": "queued",
"name": "IMG_4471.heic",
"original": {
"name": "IMG_4471.heic",
"format": "heic",
"size": 3402118,
"width": 4032,
"height": 3024,
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d/download?type=original"
},
"optimized": null,
"saved_bytes": null,
"saved_percent": null,
"settings": {
"level": "ultra",
"keep_metadata": true,
"width": null,
"height": null,
"scale": null
},
"scan_id": null,
"share_url": null,
"created_at": "2026-10-01T09:31:12+00:00",
"updated_at": "2026-10-01T09:31:12+00:00"
}
}
Cancel an image
/api/v1/images/{id}/cancel
Calls off an image still waiting in the queue. Once a worker has picked it up it runs to the end.
Path parameters
-
idstring (id) required - The image's id.
Request
curl -X POST "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/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}/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/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('images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/cancel');
print_r(json_decode((string) $response->getBody(), true));
const response = await fetch('https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/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": "9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d",
"status": "cancelled",
"name": "IMG_4471.heic",
"original": {
"name": "IMG_4471.heic",
"format": "heic",
"size": 3402118,
"width": 4032,
"height": 3024,
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d/download?type=original"
},
"optimized": null,
"saved_bytes": null,
"saved_percent": null,
"settings": {
"level": "ultra",
"keep_metadata": true,
"width": null,
"height": null,
"scale": null
},
"scan_id": null,
"share_url": null,
"created_at": "2026-10-01T09:31:12+00:00",
"updated_at": "2026-10-01T09:31:40+00:00"
}
}
Share an image
/api/v1/images/{id}/share
Gives a finished image a public page at an address nobody can guess, returned as share_url. Anyone with the link can see the optimized copy, compare it with the original, download either one, and run the original through Iminify with their own settings. The page is kept out of search engines. Sharing an image that is already shared keeps its link.
Path parameters
-
idstring (id) required - The image's id.
Request
curl -X POST "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/share" \
-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}/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/share",
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('images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/share');
print_r(json_decode((string) $response->getBody(), true));
const response = await fetch('https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/share', {
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": "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": null,
"share_url": "https://www.iminify.com/s/k3Xw9QmT2vLp8RzN4bYc6HdJ",
"created_at": "2026-10-01T09:30:00+00:00",
"updated_at": "2026-10-01T09:30:06+00:00"
}
}
Stop sharing an image
/api/v1/images/{id}/share
Switches the public link off. The page and both of its files answer 404 from the next request on. Sharing the image again later gives it a new link.
Path parameters
-
idstring (id) required - The image's id.
Request
curl -X DELETE "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/share" \
-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}/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/share",
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('images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/share');
echo $response->getStatusCode(), PHP_EOL; // 204, nothing in the body
const response = await fetch('https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/share', {
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.
Rename several images
/api/v1/images/rename
Gives every finished image in ids the same name followed by its place in upload order, oldest first: trip-01, trip-02. The number has at least two digits, more when the count needs them. A single image gets the name alone. Images that haven't finished are passed over, and if none has, the call is refused. It is all or nothing: when one of the names would be too long, none changes. Every id has to be one of your images; the ones that aren't are named in errors.
Body (JSON)
-
idsarray of ids required - The images, by id. 1 to 150.
-
namestring required - The name they share, without an extension.
Request
curl -X POST "https://www.iminify.com/api/v1/images/rename" \
-H "Authorization: Bearer $IMINIFY_API_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"ids":["9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35","9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d"],"name":"team-offsite"}'
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}/images/rename",
headers=HEADERS,
json={
"ids": ["9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35", "9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d"],
"name": "team-offsite",
},
)
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('images/rename', [
'json' => [
'ids' => ['9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35', '9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d'],
'name' => 'team-offsite',
],
]);
print_r(json_decode((string) $response->getBody(), true));
const response = await fetch('https://www.iminify.com/api/v1/images/rename', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.IMINIFY_API_KEY}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
ids: ['9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35', '9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d'],
name: 'team-offsite',
}),
});
if (!response.ok) {
throw new Error(`${response.status}: ${(await response.json()).message}`);
}
console.log(await response.json());
Response 200
{
"data": [
{
"id": "9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35",
"status": "completed",
"name": "team-offsite-01.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-01.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": null,
"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": "team-offsite-02.webp",
"original": {
"name": "IMG_4471.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": "team-offsite-02.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": null,
"share_url": null,
"created_at": "2026-10-01T09:30:00+00:00",
"updated_at": "2026-10-01T09:30:06+00:00"
}
]
}
Download several images as a zip
/api/v1/images/download
One zip holding the optimized copy of every finished image in ids, oldest first. Two images with the same name are kept apart as name.webp and name (2).webp. Images that haven't finished are left out, and if none has, the call is refused.
Body (JSON)
-
idsarray of ids required - The images, by id. 1 to 150.
Request
curl -X POST --fail-with-body -o images.zip "https://www.iminify.com/api/v1/images/download" \
-H "Authorization: Bearer $IMINIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ids":["9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35","9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d"]}'
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}/images/download",
headers=HEADERS,
json={
"ids": ["9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35", "9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d"],
},
)
response.raise_for_status()
with open("images.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->post('images/download', [
'json' => [
'ids' => ['9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35', '9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d'],
],
'sink' => 'images.zip',
]);
echo 'Saved images.zip', PHP_EOL;
import { writeFile } from 'node:fs/promises';
const response = await fetch('https://www.iminify.com/api/v1/images/download', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.IMINIFY_API_KEY}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
ids: ['9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35', '9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d'],
}),
});
if (!response.ok) {
throw new Error(`${response.status}: ${(await response.json()).message}`);
}
await writeFile('images.zip', Buffer.from(await response.arrayBuffer()));
Response 200
The file itself, not JSON.