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. The alt text comes a few seconds after a completed or already-optimized image, so if you want it, keep polling that image until alt_text.status is no longer pending too. A failed or cancelled image's alt text stays pending until the image is retried, so stop there.
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, or until about 24 hours after it finished for an image you sent as ephemeral.
Folders
On a paid plan, an image can carry the folder it belongs in. Send folder with the upload, like holiday/beach, and POST /images/download puts the image there in the zip, so a folder you send image by image comes back the way it was. Images sent without one sit at the top of the zip, and two files with the same name in one folder are kept apart as name.webp and name (2).webp.
The website sends the same folder for a folder picked with Upload a folder, and for a zip dropped on the upload box, which the browser opens and uploads image by image. The API takes no zip: unzip it on your side and send each file with the folder it sat in.
Either slash separates folders. Characters a file name can't hold become a hyphen, and a . or .. folder is dropped, so the image's folder is exactly what the zip uses. On the free plan an upload with a folder is refused with pro_required. An image keeps its folder if the plan lapses, and so does a copy made by compressing it again.
Responsive sizes
On a paid plan, an image can also come back at several widths, for an <img srcset> that lets each screen download the size it needs. Send responsive_widths with the upload, or with a recompress, like [480, 768, 1200, 1600], the website's default: up to 8 widths, each from 16 to 65535 pixels. In a multipart form, repeat responsive_widths[] or send them in one field as 480, 768, 1200, 1600.
Each width is made from your upload, at the same level and in the format the image is delivered in, so every file is one step of compression from the original. Your width, height, scale and size_limit shape the optimized copy alone. Nothing is enlarged: a width wider than the upload is made at the upload's own width, and only once, however many of your widths are wider.
The sizes are made a few seconds after the image finishes, under responsive in the image:
"responsive": {
"status": "ready",
"files": [
{
"width": 480,
"height": 360,
"size": 24118,
"name": "team-offsite-480w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/480"
}
],
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive.zip",
"html": "<img\n src=\"team-offsite-1600w.webp\"\n srcset=\"team-offsite-480w.webp 480w, ...\"\n ..."
}
status is pending while they're being made, ready once every width is, failed when one couldn't be made (none is kept; compress the image again with the widths to try again), and skipped for an animation, which gets none. responsive itself is null for an image sent without widths.
- Files: each one is named after the image with its width, like
team-offsite-768w.webp, and downloads with your key from itsdownload_url(one size), or all of them in one zip fromresponsive.download_url(every size). They follow a rename of the image.POST /images/downloadzips the optimized copies alone. - The snippet:
htmlis an<img>with asrcsetover the files, itswidthandheightfrom the widest, and the image's alt text. It names each file by itsname, so paste it beside the files, or put their folder's path in front, and changesizesto the width the image takes in your layout. - Waiting: poll the image until
responsive.statusis no longerpending, or on a paid plan let a webhook tell you:image.responsive_finishedarrives once they're done. An image can't be deleted while its sizes are being made. - Plans: sent on the free plan, an upload with
responsive_widthsis refused withpro_required. Sizes already made stay downloadable if the plan lapses.
Alt text and file names
Every image you compress also gets an alt text written by AI: one sentence describing what it shows, for its alt attribute, so screen readers and search engines know what's there. It comes with a suggested file name, a few descriptive words joined by hyphens. Send alt_text as false to leave both out.
They're written from a small copy of the image, at most 768 pixels on its longest side, which goes to Anthropic, whose Claude model writes them. They arrive a few seconds after the image finishes, under alt_text in the image:
"alt_text": {
"status": "written",
"text": "Six colleagues laughing around a picnic table on a lakeside lawn",
"language": "en",
"suggested_name": "colleagues-picnic-table-lakeside"
}
status is pending while it's being written, written once it's ready, failed when the model gave no usable answer, and skipped when your daily AI alt texts were used up as the image was queued. alt_text itself is null for an image sent with it switched off.
- Language:
alt_text_languagetakes one of these tags:en(English),es(Spanish),fr(French),de(German),it(Italian),pt(Portuguese),nl(Dutch),pl(Polish),sv(Swedish),tr(Turkish),ru(Russian),uk(Ukrainian),ar(Arabic),fa(Persian),he(Hebrew),hi(Hindi),id(Indonesian),vi(Vietnamese),th(Thai),ja(Japanese),ko(Korean),zh-Hans(Simplified Chinese),zh-Hant(Traditional Chinese). English is the default. The suggested name follows the language when it's written in the Latin alphabet, and is in English otherwise, so it stays a safe file name. - The name: the suggestion doesn't rename anything on its own. Pass it to rename the image, or send
use_suggested_nameastruewith the upload and the optimized copy takes it as soon as it arrives, sonamechanges after you may already have read it. An image that already has a new name by then, yours or an earlier suggestion, keeps it. - Counting: each alt text counts against your plan's daily AI alt texts, the same count the website spends. An image whose bytes Iminify has already described in that language gets the same text back at no cost. Once the day's alt texts are used up, images still compress, without one.
- Later: an image sent without one, whose alt text failed, or that was skipped can get one with
POST /images/{id}/alt-text. A written one can be asked for again in another language.
The text is written by AI, so read it before you publish it. Like any description, it can be wrong about what it can't see clearly, a count of small objects for example.
Deleting images once saved
An image stays on your account until you delete it. A program that keeps its own copy of every result, like a plugin that writes the smaller file back into a site, can send ephemeral as true with the upload instead, then send DELETE /images/{id} once it has written the result.
Anything you leave is deleted for you about 24 hours after the image finished, whether it completed, failed or was cancelled, with its original and its optimized copy. A download deletes nothing, so a result you failed to save can still be fetched again until then. Until it goes, an ephemeral image is listed, downloaded and counted like any other, on the website too. As with every delete, only a note of what the compression measured stays, with no file or name.
An upload group takes ephemeral the same way, and goes with every file it made and its source about 24 hours after its last file finished.
Bulk work
Send priority as background with work nobody is waiting on, such as a whole library. A background image waits until no image sent without it is waiting, from any account on any plan, so someone watching an upload is never held up by a bulk run. On a busy day that can be a long wait. It counts against your daily and per-minute limits the same as any other image, and comes out the same.
Leave priority out, or send interactive, for everything a person waits on. An upload group takes priority too, for every file it makes.
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. An
autoconversion has no format until it finishes, so until then the extension is the uploaded file's own. -
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, as they were sent.
-
settings.levelstring - The compression level.
-
settings.formatstring or null -
The format that was asked for, or
nullwhen none was sent.autostaysautoafter the image finishes; the format the optimized copy came out in isoptimized.format. -
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. -
settings.size_limitinteger or null -
The most the optimized copy may weigh, in kilobytes of 1,000 bytes, or
nullwhen no limit was sent. -
settings.responsive_widthsarray or null -
The widths of the responsive sizes asked for, narrowest first, or
nullwhen none were. -
size_limit_statusstring or null -
How the optimized copy came out against
settings.size_limit.under: at or under it.resized: under it, made smaller in pixels to get there, sooptimized.widthandoptimized.heightare smaller than the size you asked for, or than the upload's own.over: still over it after everything Iminify could try; an animation is never fitted.nullwhen no limit was sent, and until the image has finished. -
content_credentialsobject or null -
The Content Credentials the upload carried, a signed C2PA record of where the image came from, and what became of them.
nullwhen it carried none, and until the image has finished. See Content Credentials. -
content_credentials.statusstring - Whether the optimized copy still carries them.
-
content_credentials.generative_aiboolean - Whether they say generative AI made the image, wholly or in part. Iminify reads what they state and doesn't check their signature.
-
alt_textobject or null -
The AI alt text and suggested file name, or
nullwhen none was asked for. See Alt text and file names. -
alt_text.statusstring -
pending: being written; it arrives a few seconds after the image finishes, and waits for a retry while the image is failed or cancelled.written: ready.failed: the model gave no usable answer, or something broke; ask again with the alt text call.skipped: your daily AI alt texts were used up when the image was queued. -
alt_text.textstring or null -
One sentence for the image's
altattribute, ornulluntil it is written. -
alt_text.languagestring or null - The language tag it was written in.
-
alt_text.suggested_namestring or null -
A short descriptive file name without an extension, ready for the rename call, or
nulluntil it is written or when the model offered none. -
responsiveobject or null -
The image's responsive sizes (paid plans), or
nullwhen none were asked for. See Responsive sizes. -
responsive.statusstring -
pending: being made, a few seconds after the image finishes; it waits for a retry while the image is failed or cancelled.ready: every width is made.failed: a width could not be made, so none was kept; compress the image again withresponsive_widthsto try again.skipped: an animation, which gets none. -
responsive.filesarray -
Each width once it is
ready, narrowest first, empty until then: itswidthandheightin pixels, itssizein bytes, thenameit downloads under (the image's name, then the width, liketeam-offsite-768w.webp) and where to download it with your key. A width wider than the upload was made at the upload's own width. -
responsive.download_urlstring or null -
Where to download every width in one zip with your key, or
nulluntil they areready. -
responsive.htmlstring or null -
An
<img>with asrcsetover the files, ready to paste beside them, ornulluntil they areready. It names each file by itsname, so put the folder's path in front when the files sit in another folder, and changesizesto the width the image takes in your layout. -
scan_idstring or null -
The page scan that found the image, or
nullfor an upload. -
source_urlstring or null -
The address the image was fetched from: the image's address on the scanned page, or the
urlyou sent instead of a file.nullfor a file you sent. -
folderstring or null -
The folder the image was uploaded from, inside the folder you picked, like
holiday/beach: the zip of several images puts it there.nullfor an image sent on its own. Keeping folders comes with a paid plan. See Folders. -
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. 1 to 10000000 Default: 1
Request
curl "https://www.iminify.com/api/v1/images?status=completed&per_page=20" \
--connect-timeout 10 --max-time 30 \
-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,
timeout=(10, 30),
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/',
'connect_timeout' => 10,
'timeout' => 30,
'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', {
signal: AbortSignal.timeout(30_000),
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",
"format": "webp",
"keep_metadata": false,
"width": 1600,
"height": null,
"scale": null,
"size_limit": 250,
"responsive_widths": [
480,
768,
1200,
1600
]
},
"size_limit_status": "under",
"content_credentials": {
"status": "removed",
"generative_ai": false
},
"alt_text": {
"status": "written",
"text": "Six colleagues laughing around a picnic table on a lakeside lawn",
"language": "en",
"suggested_name": "colleagues-picnic-table-lakeside"
},
"responsive": {
"status": "ready",
"files": [
{
"width": 480,
"height": 360,
"size": 24118,
"name": "team-offsite-480w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/480"
},
{
"width": 768,
"height": 576,
"size": 55904,
"name": "team-offsite-768w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/768"
},
{
"width": 1200,
"height": 900,
"size": 128330,
"name": "team-offsite-1200w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/1200"
},
{
"width": 1600,
"height": 1200,
"size": 212877,
"name": "team-offsite-1600w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/1600"
}
],
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive.zip",
"html": "<img\n src=\"team-offsite-1600w.webp\"\n srcset=\"team-offsite-480w.webp 480w, team-offsite-768w.webp 768w, team-offsite-1200w.webp 1200w, team-offsite-1600w.webp 1600w\"\n sizes=\"(max-width: 1600px) 100vw, 1600px\"\n width=\"1600\"\n height=\"1200\"\n alt=\"Six colleagues laughing around a picnic table on a lakeside lawn\"\n style=\"max-width: 100%; height: auto\"\n decoding=\"async\"\n>"
},
"scan_id": null,
"source_url": null,
"folder": null,
"share_url": null,
"created_at": "2026-10-01T09:30:00+00:00",
"updated_at": "2026-10-01T09:30:09+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",
"format": "auto",
"keep_metadata": true,
"width": null,
"height": null,
"scale": null,
"size_limit": null,
"responsive_widths": [
480,
768,
1200,
1600
]
},
"size_limit_status": null,
"content_credentials": null,
"alt_text": {
"status": "pending",
"text": null,
"language": "de",
"suggested_name": null
},
"responsive": {
"status": "pending",
"files": [],
"download_url": null,
"html": null
},
"scan_id": null,
"source_url": null,
"folder": 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 animated WebP or PNG can't be resized: with width, height or scale it is refused with animation_not_resizable, and one sent without them, in its own format, comes back with every frame. 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 and gives up after 30 seconds. A file larger than your plan's size is refused, and Iminify keeps no more of it than that 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. -
folderstring -
The folder the image belongs in, like
holiday/beach, so a zip of several images (POST /images/download) puts it there. Either slash separates folders, and characters a file name can't hold become a hyphen. Keeping folders comes with a paid plan; sent on another plan, the image is refused withpro_required. -
ephemeralboolean -
Delete the image once you've saved it, for a caller that keeps its own copy. Send
DELETE /images/{id}once you've written the result; anything you leave is deleted for you about 24 hours after the image finished, its files with it. Off by default, which keeps the image until you delete it. See Deleting images once saved. Default: false -
prioritystring -
backgroundfor bulk work nobody waits on, such as a whole library: it runs after every image sent without it, on every plan, and counts against your limits the same. See Bulk work.
Default: interactiveinteractivebackground -
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 unless asize_limitneeds it to,noneonly applies the format, the resize, the size limit 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 -
size_limitinteger -
The most the optimized copy may weigh, in kilobytes of 1,000 bytes, from 5 KB up. Iminify lowers the quality until the file fits, and makes the image smaller in pixels when the lowest quality is still over it. The limit comes before the level: under
losslessornone, a file over it is encoded again with loss. A PNG or GIF kept in its own format that is over the limit comes back in a format with a quality to lower (JPG, WebP or AVIF), sopngas theformatis refused; an animation is never fitted.size_limit_statuson the finished image says how it came out. See Size limit. -
responsive_widthsarray of strings -
Paid plans: also make the image at each of these widths in pixels, for an
<img srcset>, like[480, 768, 1200, 1600], the website's default. Up to 8 widths, each from 16 to 65535. Each width is made from the upload, at the same level and in the format the image is delivered in, and never enlarged: a width wider than the upload is made at the upload's own width, once. In a multipart form, repeatresponsive_widths[]or send them in one field, separated by commas. They are made a few seconds after the image finishes;responsiveon the image lists them. Sent on another plan, the image is refused withpro_required. See Responsive sizes. -
alt_textboolean -
Write an alt text and a suggested file name for the image with AI. On by default; send
falseto leave it out. A small copy of the image goes to Anthropic, whose Claude model writes both, and they arrive a few seconds after the image finishes. Each one counts against your daily AI alt texts; once those are used up the image still compresses, without one. See Alt text and file names. Default: true -
alt_text_languagestring -
The language to write the alt text in, as one of these tags:
en(English),es(Spanish),fr(French),de(German),it(Italian),pt(Portuguese),nl(Dutch),pl(Polish),sv(Swedish),tr(Turkish),ru(Russian),uk(Ukrainian),ar(Arabic),fa(Persian),he(Hebrew),hi(Hindi),id(Indonesian),vi(Vietnamese),th(Thai),ja(Japanese),ko(Korean),zh-Hans(Simplified Chinese),zh-Hant(Traditional Chinese). The suggested file name follows it when the language is written in the Latin alphabet, and is in English otherwise.
Default: enenesfrdeitptnlplsvtrruukarfahehiidvithjakozh-Hanszh-Hant -
use_suggested_nameboolean - Rename the optimized copy to the suggested file name as soon as the alt text arrives, unless it already has a new name by then: yours, or an earlier suggestion. Default: false
Request
curl -X POST "https://www.iminify.com/api/v1/images" \
--connect-timeout 10 --max-time 1800 \
-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,
timeout=(10, 300),
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/',
'connect_timeout' => 10,
'timeout' => 30,
'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'],
],
'timeout' => 1800,
]);
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',
signal: AbortSignal.timeout(1800_000),
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",
"format": "auto",
"keep_metadata": true,
"width": null,
"height": null,
"scale": null,
"size_limit": null,
"responsive_widths": [
480,
768,
1200,
1600
]
},
"size_limit_status": null,
"content_credentials": null,
"alt_text": {
"status": "pending",
"text": null,
"language": "de",
"suggested_name": null
},
"responsive": {
"status": "pending",
"files": [],
"download_url": null,
"html": null
},
"scan_id": null,
"source_url": null,
"folder": 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" \
--connect-timeout 10 --max-time 30 \
-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,
timeout=(10, 30),
)
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/',
'connect_timeout' => 10,
'timeout' => 30,
'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', {
signal: AbortSignal.timeout(30_000),
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",
"format": "webp",
"keep_metadata": false,
"width": 1600,
"height": null,
"scale": null,
"size_limit": 250,
"responsive_widths": [
480,
768,
1200,
1600
]
},
"size_limit_status": "under",
"content_credentials": {
"status": "removed",
"generative_ai": false
},
"alt_text": {
"status": "written",
"text": "Six colleagues laughing around a picnic table on a lakeside lawn",
"language": "en",
"suggested_name": "colleagues-picnic-table-lakeside"
},
"responsive": {
"status": "ready",
"files": [
{
"width": 480,
"height": 360,
"size": 24118,
"name": "team-offsite-480w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/480"
},
{
"width": 768,
"height": 576,
"size": 55904,
"name": "team-offsite-768w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/768"
},
{
"width": 1200,
"height": 900,
"size": 128330,
"name": "team-offsite-1200w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/1200"
},
{
"width": 1600,
"height": 1200,
"size": 212877,
"name": "team-offsite-1600w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/1600"
}
],
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive.zip",
"html": "<img\n src=\"team-offsite-1600w.webp\"\n srcset=\"team-offsite-480w.webp 480w, team-offsite-768w.webp 768w, team-offsite-1200w.webp 1200w, team-offsite-1600w.webp 1600w\"\n sizes=\"(max-width: 1600px) 100vw, 1600px\"\n width=\"1600\"\n height=\"1200\"\n alt=\"Six colleagues laughing around a picnic table on a lakeside lawn\"\n style=\"max-width: 100%; height: auto\"\n decoding=\"async\"\n>"
},
"scan_id": null,
"source_url": null,
"folder": null,
"share_url": null,
"created_at": "2026-10-01T09:30:00+00:00",
"updated_at": "2026-10-01T09:30:09+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" \
--connect-timeout 10 --max-time 30 \
-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,
timeout=(10, 30),
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/',
'connect_timeout' => 10,
'timeout' => 30,
'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',
signal: AbortSignal.timeout(30_000),
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",
"format": "webp",
"keep_metadata": false,
"width": 1600,
"height": null,
"scale": null,
"size_limit": 250,
"responsive_widths": [
480,
768,
1200,
1600
]
},
"size_limit_status": "under",
"content_credentials": {
"status": "removed",
"generative_ai": false
},
"alt_text": {
"status": "written",
"text": "Six colleagues laughing around a picnic table on a lakeside lawn",
"language": "en",
"suggested_name": "colleagues-picnic-table-lakeside"
},
"responsive": {
"status": "ready",
"files": [
{
"width": 480,
"height": 360,
"size": 24118,
"name": "team-offsite-480w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/480"
},
{
"width": 768,
"height": 576,
"size": 55904,
"name": "team-offsite-768w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/768"
},
{
"width": 1200,
"height": 900,
"size": 128330,
"name": "team-offsite-1200w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/1200"
},
{
"width": 1600,
"height": 1200,
"size": 212877,
"name": "team-offsite-1600w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/1600"
}
],
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive.zip",
"html": "<img\n src=\"team-offsite-1600w.webp\"\n srcset=\"team-offsite-480w.webp 480w, team-offsite-768w.webp 768w, team-offsite-1200w.webp 1200w, team-offsite-1600w.webp 1600w\"\n sizes=\"(max-width: 1600px) 100vw, 1600px\"\n width=\"1600\"\n height=\"1200\"\n alt=\"Six colleagues laughing around a picnic table on a lakeside lawn\"\n style=\"max-width: 100%; height: auto\"\n decoding=\"async\"\n>"
},
"scan_id": null,
"source_url": null,
"folder": null,
"share_url": null,
"created_at": "2026-10-01T09:30:00+00:00",
"updated_at": "2026-10-01T09:30:09+00:00"
}
}
Delete an image
/api/v1/images/{id}
Deletes the image and both of its files from storage for good, its responsive sizes too. There is no trash and no undo. An image that is still queued or compressing, or whose responsive sizes are still being made, 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" \
--connect-timeout 10 --max-time 30 \
-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,
timeout=(10, 30),
)
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/',
'connect_timeout' => 10,
'timeout' => 30,
'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',
signal: AbortSignal.timeout(30_000),
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" \
--connect-timeout 10 --max-time 1800 \
-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,
timeout=(10, 300),
)
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/',
'connect_timeout' => 10,
'timeout' => 30,
'headers' => [
'Authorization' => 'Bearer ' . getenv('IMINIFY_API_KEY'),
'Accept' => 'application/json',
],
]);
$response = $client->get('images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/download', [
'sink' => 'team-offsite.webp',
'timeout' => 1800,
]);
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', {
signal: AbortSignal.timeout(1800_000),
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.
Download one responsive size
/api/v1/images/{id}/responsive/{width}
One width of the image's responsive sizes, under the name it has in responsive.files, like team-offsite-768w.webp. The answer is the file itself, with its type in Content-Type and its name in Content-Disposition. The sizes can be downloaded once responsive.status is ready, and stay downloadable if the plan lapses. See Responsive sizes.
Path parameters
-
idstring (id) required - The image's id.
-
widthinteger required -
One of the widths in
responsive.files.
Request
curl --fail-with-body -o team-offsite-768w.webp "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/768" \
--connect-timeout 10 --max-time 1800 \
-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/responsive/768",
headers=HEADERS,
timeout=(10, 300),
)
response.raise_for_status()
with open("team-offsite-768w.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/',
'connect_timeout' => 10,
'timeout' => 30,
'headers' => [
'Authorization' => 'Bearer ' . getenv('IMINIFY_API_KEY'),
'Accept' => 'application/json',
],
]);
$response = $client->get('images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/768', [
'sink' => 'team-offsite-768w.webp',
'timeout' => 1800,
]);
echo 'Saved team-offsite-768w.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/responsive/768', {
signal: AbortSignal.timeout(1800_000),
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-768w.webp', Buffer.from(await response.arrayBuffer()));
Response 200
The file itself, not JSON.
Download every responsive size
/api/v1/images/{id}/responsive.zip
Every width of the image's responsive sizes in one zip, each file under the name it has in responsive.files. The sizes can be downloaded once responsive.status is ready. See Responsive sizes.
Path parameters
-
idstring (id) required - The image's id.
Request
curl --fail-with-body -o team-offsite-sizes.zip "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive.zip" \
--connect-timeout 10 --max-time 1800 \
-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/responsive.zip",
headers=HEADERS,
timeout=(10, 300),
)
response.raise_for_status()
with open("team-offsite-sizes.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/',
'connect_timeout' => 10,
'timeout' => 30,
'headers' => [
'Authorization' => 'Bearer ' . getenv('IMINIFY_API_KEY'),
'Accept' => 'application/json',
],
]);
$response = $client->get('images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive.zip', [
'sink' => 'team-offsite-sizes.zip',
'timeout' => 1800,
]);
echo 'Saved team-offsite-sizes.zip', PHP_EOL;
import { writeFile } from 'node:fs/promises';
const response = await fetch('https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive.zip', {
signal: AbortSignal.timeout(1800_000),
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-sizes.zip', 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 unless asize_limitneeds it to,noneonly applies the format, the resize, the size limit 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 -
size_limitinteger -
The most the optimized copy may weigh, in kilobytes of 1,000 bytes, from 5 KB up. Iminify lowers the quality until the file fits, and makes the image smaller in pixels when the lowest quality is still over it. The limit comes before the level: under
losslessornone, a file over it is encoded again with loss. A PNG or GIF kept in its own format that is over the limit comes back in a format with a quality to lower (JPG, WebP or AVIF), sopngas theformatis refused; an animation is never fitted.size_limit_statuson the finished image says how it came out. See Size limit. -
responsive_widthsarray of strings -
Paid plans: also make the image at each of these widths in pixels, for an
<img srcset>, like[480, 768, 1200, 1600], the website's default. Up to 8 widths, each from 16 to 65535. Each width is made from the upload, at the same level and in the format the image is delivered in, and never enlarged: a width wider than the upload is made at the upload's own width, once. In a multipart form, repeatresponsive_widths[]or send them in one field, separated by commas. They are made a few seconds after the image finishes;responsiveon the image lists them. Sent on another plan, the image is refused withpro_required. See Responsive sizes. -
alt_textboolean -
Write an alt text and a suggested file name for the image with AI. On by default; send
falseto leave it out. A small copy of the image goes to Anthropic, whose Claude model writes both, and they arrive a few seconds after the image finishes. Each one counts against your daily AI alt texts; once those are used up the image still compresses, without one. See Alt text and file names. Default: true -
alt_text_languagestring -
The language to write the alt text in, as one of these tags:
en(English),es(Spanish),fr(French),de(German),it(Italian),pt(Portuguese),nl(Dutch),pl(Polish),sv(Swedish),tr(Turkish),ru(Russian),uk(Ukrainian),ar(Arabic),fa(Persian),he(Hebrew),hi(Hindi),id(Indonesian),vi(Vietnamese),th(Thai),ja(Japanese),ko(Korean),zh-Hans(Simplified Chinese),zh-Hant(Traditional Chinese). The suggested file name follows it when the language is written in the Latin alphabet, and is in English otherwise.
Default: enenesfrdeitptnlplsvtrruukarfahehiidvithjakozh-Hanszh-Hant -
use_suggested_nameboolean - Rename the optimized copy to the suggested file name as soon as the alt text arrives, unless it already has a new name by then: yours, or an earlier suggestion. Default: false
Request
curl -X POST "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/recompress" \
--connect-timeout 10 --max-time 30 \
-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,
timeout=(10, 30),
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/',
'connect_timeout' => 10,
'timeout' => 30,
'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',
signal: AbortSignal.timeout(30_000),
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",
"format": "auto",
"keep_metadata": true,
"width": null,
"height": null,
"scale": null,
"size_limit": null,
"responsive_widths": [
480,
768,
1200,
1600
]
},
"size_limit_status": null,
"content_credentials": null,
"alt_text": {
"status": "pending",
"text": null,
"language": "de",
"suggested_name": null
},
"responsive": {
"status": "pending",
"files": [],
"download_url": null,
"html": null
},
"scan_id": null,
"source_url": null,
"folder": 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" \
--connect-timeout 10 --max-time 30 \
-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,
timeout=(10, 30),
)
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/',
'connect_timeout' => 10,
'timeout' => 30,
'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',
signal: AbortSignal.timeout(30_000),
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",
"format": "auto",
"keep_metadata": true,
"width": null,
"height": null,
"scale": null,
"size_limit": null,
"responsive_widths": [
480,
768,
1200,
1600
]
},
"size_limit_status": null,
"content_credentials": null,
"alt_text": {
"status": "pending",
"text": null,
"language": "de",
"suggested_name": null
},
"responsive": {
"status": "pending",
"files": [],
"download_url": null,
"html": null
},
"scan_id": null,
"source_url": null,
"folder": 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" \
--connect-timeout 10 --max-time 30 \
-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,
timeout=(10, 30),
)
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/',
'connect_timeout' => 10,
'timeout' => 30,
'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',
signal: AbortSignal.timeout(30_000),
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",
"format": "auto",
"keep_metadata": true,
"width": null,
"height": null,
"scale": null,
"size_limit": null,
"responsive_widths": [
480,
768,
1200,
1600
]
},
"size_limit_status": null,
"content_credentials": null,
"alt_text": {
"status": "pending",
"text": null,
"language": "de",
"suggested_name": null
},
"responsive": {
"status": "pending",
"files": [],
"download_url": null,
"html": null
},
"scan_id": null,
"source_url": null,
"folder": null,
"share_url": null,
"created_at": "2026-10-01T09:31:12+00:00",
"updated_at": "2026-10-01T09:31:40+00:00"
}
}
Write an image's alt text
/api/v1/images/{id}/alt-text
Asks AI for the alt text and suggested file name of a finished image: one sent with alt_text off, compressed before alt text existed, whose alt text failed, or skipped on a day that was used up. A written one can be asked again in another language. It counts one against your daily AI alt texts, unless an image with the same bytes already has one in that language, which is copied at once and costs nothing. The answer is the image; poll it until alt_text.status is no longer pending. While one is being written, asking again changes nothing.
Path parameters
-
idstring (id) required - The image's id.
Body (JSON)
-
alt_text_languagestring -
The language to write the alt text in, as one of these tags:
en(English),es(Spanish),fr(French),de(German),it(Italian),pt(Portuguese),nl(Dutch),pl(Polish),sv(Swedish),tr(Turkish),ru(Russian),uk(Ukrainian),ar(Arabic),fa(Persian),he(Hebrew),hi(Hindi),id(Indonesian),vi(Vietnamese),th(Thai),ja(Japanese),ko(Korean),zh-Hans(Simplified Chinese),zh-Hant(Traditional Chinese). The suggested file name follows it when the language is written in the Latin alphabet, and is in English otherwise.
Default: enenesfrdeitptnlplsvtrruukarfahehiidvithjakozh-Hanszh-Hant -
use_suggested_nameboolean - Rename the optimized copy to the suggested file name as soon as the alt text arrives, unless it already has a new name by then: yours, or an earlier suggestion. Default: false
Request
curl -X POST "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/alt-text" \
--connect-timeout 10 --max-time 30 \
-H "Authorization: Bearer $IMINIFY_API_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"alt_text_language":"fr","use_suggested_name":true}'
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/alt-text",
headers=HEADERS,
timeout=(10, 30),
json={
"alt_text_language": "fr",
"use_suggested_name": True,
},
)
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/',
'connect_timeout' => 10,
'timeout' => 30,
'headers' => [
'Authorization' => 'Bearer ' . getenv('IMINIFY_API_KEY'),
'Accept' => 'application/json',
],
]);
$response = $client->post('images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/alt-text', [
'json' => [
'alt_text_language' => 'fr',
'use_suggested_name' => true,
],
]);
print_r(json_decode((string) $response->getBody(), true));
const response = await fetch('https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/alt-text', {
method: 'POST',
signal: AbortSignal.timeout(30_000),
headers: {
Authorization: `Bearer ${process.env.IMINIFY_API_KEY}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
alt_text_language: 'fr',
use_suggested_name: true,
}),
});
if (!response.ok) {
throw new Error(`${response.status}: ${(await response.json()).message}`);
}
console.log(await response.json());
Response 202
{
"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",
"format": "webp",
"keep_metadata": false,
"width": 1600,
"height": null,
"scale": null,
"size_limit": 250,
"responsive_widths": [
480,
768,
1200,
1600
]
},
"size_limit_status": "under",
"content_credentials": {
"status": "removed",
"generative_ai": false
},
"alt_text": {
"status": "pending",
"text": null,
"language": "fr",
"suggested_name": null
},
"responsive": {
"status": "ready",
"files": [
{
"width": 480,
"height": 360,
"size": 24118,
"name": "team-offsite-480w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/480"
},
{
"width": 768,
"height": 576,
"size": 55904,
"name": "team-offsite-768w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/768"
},
{
"width": 1200,
"height": 900,
"size": 128330,
"name": "team-offsite-1200w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/1200"
},
{
"width": 1600,
"height": 1200,
"size": 212877,
"name": "team-offsite-1600w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/1600"
}
],
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive.zip",
"html": "<img\n src=\"team-offsite-1600w.webp\"\n srcset=\"team-offsite-480w.webp 480w, team-offsite-768w.webp 768w, team-offsite-1200w.webp 1200w, team-offsite-1600w.webp 1600w\"\n sizes=\"(max-width: 1600px) 100vw, 1600px\"\n width=\"1600\"\n height=\"1200\"\n alt=\"\"\n style=\"max-width: 100%; height: auto\"\n decoding=\"async\"\n>"
},
"scan_id": null,
"source_url": null,
"folder": null,
"share_url": null,
"created_at": "2026-10-01T09:30:00+00:00",
"updated_at": "2026-10-01T09:30:09+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" \
--connect-timeout 10 --max-time 30 \
-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,
timeout=(10, 30),
)
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/',
'connect_timeout' => 10,
'timeout' => 30,
'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',
signal: AbortSignal.timeout(30_000),
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",
"format": "webp",
"keep_metadata": false,
"width": 1600,
"height": null,
"scale": null,
"size_limit": 250,
"responsive_widths": [
480,
768,
1200,
1600
]
},
"size_limit_status": "under",
"content_credentials": {
"status": "removed",
"generative_ai": false
},
"alt_text": {
"status": "written",
"text": "Six colleagues laughing around a picnic table on a lakeside lawn",
"language": "en",
"suggested_name": "colleagues-picnic-table-lakeside"
},
"responsive": {
"status": "ready",
"files": [
{
"width": 480,
"height": 360,
"size": 24118,
"name": "team-offsite-480w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/480"
},
{
"width": 768,
"height": 576,
"size": 55904,
"name": "team-offsite-768w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/768"
},
{
"width": 1200,
"height": 900,
"size": 128330,
"name": "team-offsite-1200w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/1200"
},
{
"width": 1600,
"height": 1200,
"size": 212877,
"name": "team-offsite-1600w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/1600"
}
],
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive.zip",
"html": "<img\n src=\"team-offsite-1600w.webp\"\n srcset=\"team-offsite-480w.webp 480w, team-offsite-768w.webp 768w, team-offsite-1200w.webp 1200w, team-offsite-1600w.webp 1600w\"\n sizes=\"(max-width: 1600px) 100vw, 1600px\"\n width=\"1600\"\n height=\"1200\"\n alt=\"Six colleagues laughing around a picnic table on a lakeside lawn\"\n style=\"max-width: 100%; height: auto\"\n decoding=\"async\"\n>"
},
"scan_id": null,
"source_url": null,
"folder": null,
"share_url": "https://www.iminify.com/s/k3Xw9QmT2vLp8RzN4bYc6HdJ",
"created_at": "2026-10-01T09:30:00+00:00",
"updated_at": "2026-10-01T09:30:09+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" \
--connect-timeout 10 --max-time 30 \
-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,
timeout=(10, 30),
)
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/',
'connect_timeout' => 10,
'timeout' => 30,
'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',
signal: AbortSignal.timeout(30_000),
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" \
--connect-timeout 10 --max-time 30 \
-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,
timeout=(10, 30),
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/',
'connect_timeout' => 10,
'timeout' => 30,
'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',
signal: AbortSignal.timeout(30_000),
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",
"format": "webp",
"keep_metadata": false,
"width": 1600,
"height": null,
"scale": null,
"size_limit": 250,
"responsive_widths": [
480,
768,
1200,
1600
]
},
"size_limit_status": "under",
"content_credentials": {
"status": "removed",
"generative_ai": false
},
"alt_text": {
"status": "written",
"text": "Six colleagues laughing around a picnic table on a lakeside lawn",
"language": "en",
"suggested_name": "colleagues-picnic-table-lakeside"
},
"responsive": {
"status": "ready",
"files": [
{
"width": 480,
"height": 360,
"size": 24118,
"name": "team-offsite-01-480w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/480"
},
{
"width": 768,
"height": 576,
"size": 55904,
"name": "team-offsite-01-768w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/768"
},
{
"width": 1200,
"height": 900,
"size": 128330,
"name": "team-offsite-01-1200w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/1200"
},
{
"width": 1600,
"height": 1200,
"size": 212877,
"name": "team-offsite-01-1600w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive/1600"
}
],
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b8e-6f0a-4c1e-9b7d-2a4e8f1c6b35/responsive.zip",
"html": "<img\n src=\"team-offsite-01-1600w.webp\"\n srcset=\"team-offsite-01-480w.webp 480w, team-offsite-01-768w.webp 768w, team-offsite-01-1200w.webp 1200w, team-offsite-01-1600w.webp 1600w\"\n sizes=\"(max-width: 1600px) 100vw, 1600px\"\n width=\"1600\"\n height=\"1200\"\n alt=\"Six colleagues laughing around a picnic table on a lakeside lawn\"\n style=\"max-width: 100%; height: auto\"\n decoding=\"async\"\n>"
},
"scan_id": null,
"source_url": null,
"folder": null,
"share_url": null,
"created_at": "2026-10-01T09:30:00+00:00",
"updated_at": "2026-10-01T09:30:09+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",
"format": "webp",
"keep_metadata": false,
"width": 1600,
"height": null,
"scale": null,
"size_limit": 250,
"responsive_widths": [
480,
768,
1200,
1600
]
},
"size_limit_status": "under",
"content_credentials": {
"status": "removed",
"generative_ai": false
},
"alt_text": {
"status": "written",
"text": "Six colleagues laughing around a picnic table on a lakeside lawn",
"language": "en",
"suggested_name": "colleagues-picnic-table-lakeside"
},
"responsive": {
"status": "ready",
"files": [
{
"width": 480,
"height": 360,
"size": 24118,
"name": "team-offsite-02-480w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d/responsive/480"
},
{
"width": 768,
"height": 576,
"size": 55904,
"name": "team-offsite-02-768w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d/responsive/768"
},
{
"width": 1200,
"height": 900,
"size": 128330,
"name": "team-offsite-02-1200w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d/responsive/1200"
},
{
"width": 1600,
"height": 1200,
"size": 212877,
"name": "team-offsite-02-1600w.webp",
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d/responsive/1600"
}
],
"download_url": "https://www.iminify.com/api/v1/images/9d3c5b91-0b2e-4a7f-8c1d-5e6f7a8b9c0d/responsive.zip",
"html": "<img\n src=\"team-offsite-02-1600w.webp\"\n srcset=\"team-offsite-02-480w.webp 480w, team-offsite-02-768w.webp 768w, team-offsite-02-1200w.webp 1200w, team-offsite-02-1600w.webp 1600w\"\n sizes=\"(max-width: 1600px) 100vw, 1600px\"\n width=\"1600\"\n height=\"1200\"\n alt=\"Six colleagues laughing around a picnic table on a lakeside lawn\"\n style=\"max-width: 100%; height: auto\"\n decoding=\"async\"\n>"
},
"scan_id": null,
"source_url": null,
"folder": null,
"share_url": null,
"created_at": "2026-10-01T09:30:00+00:00",
"updated_at": "2026-10-01T09:30:09+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. An image uploaded with a folder goes in that folder, and one without at the top of the zip. Two images with the same name in one folder 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" \
--connect-timeout 10 --max-time 1800 \
-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,
timeout=(10, 300),
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/',
'connect_timeout' => 10,
'timeout' => 30,
'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',
'timeout' => 1800,
]);
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',
signal: AbortSignal.timeout(1800_000),
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.