Weekly site checks API
Have a crawled site crawled again every week, compressing only its new or changed images, with an email and a zip when a check finds some. Every weekly site checks endpoint with samples.
A weekly site check crawls a site you've crawled again every week, the same way, and compresses only the images that are new or changed since the last check. When a check finds some, it emails you a link to a zip of them.
Weekly site checks are a Pro feature, on the website's scan page as well as here; another plan is answered with pro_required. Listing your checks, turning their emails off and stopping them work on every plan, so a check set up on Pro can still be stopped after Pro ends.
Starting one
POST /monitors takes the id of one of your site crawls in crawl_id. The check keeps that crawl's sitemap, which it reads again every week, or its list of pages, and its compression settings. The crawl has to have scanned at least one page: one still reading its sitemap, or one that failed before it scanned any, is answered with crawl_has_no_pages.
Nothing runs straight away and nothing is counted. The first check is a week later, in next_run_at, and is compared with the crawl you named. A second request for the same crawl answers the check already made from it, with 200 rather than 201.
Your plan sets how many sites can be checked at once. The numbers for every plan are on the pricing page, and yours is in GET /account under limits.monitors. Once every one is in use, a new check is refused with monitor_limit_reached until you stop one.
Each week's check
Each week the check runs as a site crawl of its own, named by the check's id in monitor_id. It reads the sitemap again or takes the same pages, scans each one as a page scan, and compresses only the images whose files no earlier check, and not the crawl it was made from, compressed. An image it already has is fetched, found unchanged and listed in its page's skipped with the reason unchanged, so a moved image, or one taken down and put back, isn't news.
A check doesn't count against your daily site crawls, and its pages and images don't count against your daily page scans or images. Its images get an AI alt text when the crawl asked for one, each counting against your daily AI alt texts, as a crawl's do.
A check runs only while your account is on Pro. While it isn't, paused is true and the check moves on a week at a time; it carries on by itself once you're back on Pro.
Reading what a check found
A check's last_run is its newest run, a site crawl in the shape the crawl list gives. Get it with GET /crawls/{id} for its pages and the site's report, and download its zip, which holds only the images that run compressed: the new or changed ones.
The runs stay out of the crawl list, because their check lists them. Deleting a run with DELETE /crawls/{id} removes it and its images for good, and those images count as new again on the check's next run.
The email
A check that compressed something emails the account's address with the pages that had new or changed images and a link to a zip of them. The link works for 14 days without signing in, so anyone holding it can download the zip. A check that couldn't scan any page, because the sitemap couldn't be read or every page failed, emails you why. A week with nothing new sends nothing.
Every email has a link that stops that site's emails. PATCH /monitors/{id} with emails set to false does the same, and true turns them back on. The check runs either way.
Stopping one
DELETE /monitors/{id} stops the check for good and deletes every run it made, with their pages, images and files. The crawl it was made from stays. A check whose run is still at work is refused with scan_in_progress; stop it once the run has finished.
Webhooks
A check sends no event of its own. On Pro, each page of each run sends scan.completed or scan.failed like any scan, with the run's id in crawl_id, and each image sends its own image event. See Webhooks.
Through an AI agent
The MCP server's monitor_site starts a check from a crawl, list_monitors lists the checks with their newest runs, and stop_monitor stops one. get_crawl and create_zip with crawl_id read and download a run. See the MCP server.
The Monitor object
-
idstring - The weekly check's id.
-
urlstring - The sitemap each run reads again, or the first of the pages each run takes.
-
sourcestring -
Where each run gets its pages:
sitemap, read again every week, orpages, the same list every week. -
crawl_idstring or null -
The site crawl the check was made from, which its first run is compared with, or
nullonce that crawl is deleted. -
settingsobject - The settings every image of every run is compressed with: the crawl's own.
-
settings.levelstring - The compression level.
-
settings.formatstring or null -
The format every image is converted to, or
nullto keep each one's own. -
settings.keep_metadataboolean - Whether EXIF metadata is kept.
-
settings.widthinteger or null -
The width every image is resized to, or
null. -
settings.heightinteger or null -
The height every image is resized to, or
null. -
settings.scaleinteger or null -
The percentage every image is scaled to, or
null. -
emailsboolean - Whether a run that finds new or changed images, or scans no page at all, emails you. The email also carries a link that turns this off.
-
pausedboolean -
truewhile your plan has no weekly site checks: the check is kept and runs again once you are back on Pro. -
next_run_attimestamp - When the next run is due. A run starts soon after it, and while the check is paused it moves on a week at a time.
-
last_runstring or null -
The newest run, a site crawl in the shape the crawl list gives, or
nullbefore the first one. Get it with its id for its pages and the site's report; its zip holds only the images it compressed, the new or changed ones. -
created_attimestamp - When the check was started.
List weekly site checks
/api/v1/monitors
Every weekly site check on your account, oldest first, each with its newest run. Your plan sets how many there can be, so the list is never paged.
Request
curl "https://www.iminify.com/api/v1/monitors" \
--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}/monitors",
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('monitors');
print_r(json_decode((string) $response->getBody(), true));
const response = await fetch('https://www.iminify.com/api/v1/monitors', {
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": "9d3c4e3b-8d9e-4f0a-b1c2-d3e4f5a6b7c8",
"url": "https://example.com/sitemap.xml",
"source": "sitemap",
"crawl_id": "9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6",
"settings": {
"level": "smart",
"format": "webp",
"keep_metadata": false,
"width": 1600,
"height": null,
"scale": null
},
"emails": true,
"paused": false,
"next_run_at": "2026-10-15T11:04:55+00:00",
"last_run": {
"id": "9d3c6a14-2b3c-4d5e-8f6a-7b8c9d0e1f23",
"url": "https://example.com/sitemap.xml",
"source": "sitemap",
"monitor_id": "9d3c4e3b-8d9e-4f0a-b1c2-d3e4f5a6b7c8",
"status": "completed",
"note": null,
"settings": {
"level": "smart",
"format": "webp",
"keep_metadata": false,
"width": 1600,
"height": null,
"scale": null
},
"pages_count": 2,
"pages_in_progress": 0,
"pages_failed": 0,
"pages_left_out": 0,
"images_count": 1,
"images_in_progress": 0,
"images_finished": 1,
"original_size": 1204118,
"optimized_size": 151009,
"saved_bytes": 1053109,
"saved_percent": 87.46,
"download_url": "https://www.iminify.com/api/v1/crawls/9d3c6a14-2b3c-4d5e-8f6a-7b8c9d0e1f23/download",
"created_at": "2026-10-08T11:09:00+00:00",
"updated_at": "2026-10-08T11:09:04+00:00"
},
"created_at": "2026-10-01T11:06:12+00:00"
}
]
}
Check a site every week
/api/v1/monitors
A Pro feature. Turns one of your site crawls into a weekly check of its site. Nothing runs now and nothing is counted: the first run is a week from now and is compared with the crawl you name, and each run after that with every earlier one, so a run compresses only the images whose files no earlier run or that crawl compressed. Runs don't count against your daily site crawls.
It answers 201 with the new check, or 200 with the check already made from that crawl. A crawl that is itself a check's run answers its check.
Body (JSON)
-
crawl_idstring (id) required - The id of one of your site crawls that scanned at least one page. The check crawls the site the same way every week: its sitemap read again, or the same pages, with the same settings.
Request
curl -X POST "https://www.iminify.com/api/v1/monitors" \
--connect-timeout 10 --max-time 30 \
-H "Authorization: Bearer $IMINIFY_API_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"crawl_id":"9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6"}'
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}/monitors",
headers=HEADERS,
timeout=(10, 30),
json={
"crawl_id": "9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6",
},
)
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('monitors', [
'json' => [
'crawl_id' => '9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6',
],
]);
print_r(json_decode((string) $response->getBody(), true));
const response = await fetch('https://www.iminify.com/api/v1/monitors', {
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({
crawl_id: '9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6',
}),
});
if (!response.ok) {
throw new Error(`${response.status}: ${(await response.json()).message}`);
}
console.log(await response.json());
Response 201
{
"data": {
"id": "9d3c4e3b-8d9e-4f0a-b1c2-d3e4f5a6b7c8",
"url": "https://example.com/sitemap.xml",
"source": "sitemap",
"crawl_id": "9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6",
"settings": {
"level": "smart",
"format": "webp",
"keep_metadata": false,
"width": 1600,
"height": null,
"scale": null
},
"emails": true,
"paused": false,
"next_run_at": "2026-10-08T11:06:12+00:00",
"last_run": null,
"created_at": "2026-10-01T11:06:12+00:00"
}
}
Get a weekly site check
/api/v1/monitors/{id}
One weekly check with its newest run. A run is a site crawl: get it with last_run.id for its pages and the site's report, and download its zip, which holds only the images that run compressed.
Path parameters
-
idstring (id) required - The weekly check's id.
Request
curl "https://www.iminify.com/api/v1/monitors/9d3c4e3b-8d9e-4f0a-b1c2-d3e4f5a6b7c8" \
--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}/monitors/9d3c4e3b-8d9e-4f0a-b1c2-d3e4f5a6b7c8",
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('monitors/9d3c4e3b-8d9e-4f0a-b1c2-d3e4f5a6b7c8');
print_r(json_decode((string) $response->getBody(), true));
const response = await fetch('https://www.iminify.com/api/v1/monitors/9d3c4e3b-8d9e-4f0a-b1c2-d3e4f5a6b7c8', {
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": "9d3c4e3b-8d9e-4f0a-b1c2-d3e4f5a6b7c8",
"url": "https://example.com/sitemap.xml",
"source": "sitemap",
"crawl_id": "9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6",
"settings": {
"level": "smart",
"format": "webp",
"keep_metadata": false,
"width": 1600,
"height": null,
"scale": null
},
"emails": true,
"paused": false,
"next_run_at": "2026-10-15T11:04:55+00:00",
"last_run": {
"id": "9d3c6a14-2b3c-4d5e-8f6a-7b8c9d0e1f23",
"url": "https://example.com/sitemap.xml",
"source": "sitemap",
"monitor_id": "9d3c4e3b-8d9e-4f0a-b1c2-d3e4f5a6b7c8",
"status": "completed",
"note": null,
"settings": {
"level": "smart",
"format": "webp",
"keep_metadata": false,
"width": 1600,
"height": null,
"scale": null
},
"pages_count": 2,
"pages_in_progress": 0,
"pages_failed": 0,
"pages_left_out": 0,
"images_count": 1,
"images_in_progress": 0,
"images_finished": 1,
"original_size": 1204118,
"optimized_size": 151009,
"saved_bytes": 1053109,
"saved_percent": 87.46,
"download_url": "https://www.iminify.com/api/v1/crawls/9d3c6a14-2b3c-4d5e-8f6a-7b8c9d0e1f23/download",
"created_at": "2026-10-08T11:09:00+00:00",
"updated_at": "2026-10-08T11:09:04+00:00"
},
"created_at": "2026-10-01T11:06:12+00:00"
}
}
Update a weekly site check
/api/v1/monitors/{id}
Turns the check's emails off or back on. The check runs either way.
Path parameters
-
idstring (id) required - The weekly check's id.
Body (JSON)
-
emailsboolean required -
falsestops the check's emails and keeps the check running;trueturns them back on.
Request
curl -X PATCH "https://www.iminify.com/api/v1/monitors/9d3c4e3b-8d9e-4f0a-b1c2-d3e4f5a6b7c8" \
--connect-timeout 10 --max-time 30 \
-H "Authorization: Bearer $IMINIFY_API_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"emails":false}'
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}/monitors/9d3c4e3b-8d9e-4f0a-b1c2-d3e4f5a6b7c8",
headers=HEADERS,
timeout=(10, 30),
json={
"emails": False,
},
)
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('monitors/9d3c4e3b-8d9e-4f0a-b1c2-d3e4f5a6b7c8', [
'json' => [
'emails' => false,
],
]);
print_r(json_decode((string) $response->getBody(), true));
const response = await fetch('https://www.iminify.com/api/v1/monitors/9d3c4e3b-8d9e-4f0a-b1c2-d3e4f5a6b7c8', {
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({
emails: false,
}),
});
if (!response.ok) {
throw new Error(`${response.status}: ${(await response.json()).message}`);
}
console.log(await response.json());
Response 200
{
"data": {
"id": "9d3c4e3b-8d9e-4f0a-b1c2-d3e4f5a6b7c8",
"url": "https://example.com/sitemap.xml",
"source": "sitemap",
"crawl_id": "9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6",
"settings": {
"level": "smart",
"format": "webp",
"keep_metadata": false,
"width": 1600,
"height": null,
"scale": null
},
"emails": false,
"paused": false,
"next_run_at": "2026-10-15T11:04:55+00:00",
"last_run": {
"id": "9d3c6a14-2b3c-4d5e-8f6a-7b8c9d0e1f23",
"url": "https://example.com/sitemap.xml",
"source": "sitemap",
"monitor_id": "9d3c4e3b-8d9e-4f0a-b1c2-d3e4f5a6b7c8",
"status": "completed",
"note": null,
"settings": {
"level": "smart",
"format": "webp",
"keep_metadata": false,
"width": 1600,
"height": null,
"scale": null
},
"pages_count": 2,
"pages_in_progress": 0,
"pages_failed": 0,
"pages_left_out": 0,
"images_count": 1,
"images_in_progress": 0,
"images_finished": 1,
"original_size": 1204118,
"optimized_size": 151009,
"saved_bytes": 1053109,
"saved_percent": 87.46,
"download_url": "https://www.iminify.com/api/v1/crawls/9d3c6a14-2b3c-4d5e-8f6a-7b8c9d0e1f23/download",
"created_at": "2026-10-08T11:09:00+00:00",
"updated_at": "2026-10-08T11:09:04+00:00"
},
"created_at": "2026-10-01T11:06:12+00:00"
}
}
Stop a weekly site check
/api/v1/monitors/{id}
Stops the check for good and deletes every run it made, with their pages, images and files. The crawl it was made from stays. A check whose run is still at work is refused; stop it once the run has finished.
Path parameters
-
idstring (id) required - The weekly check's id.
Request
curl -X DELETE "https://www.iminify.com/api/v1/monitors/9d3c4e3b-8d9e-4f0a-b1c2-d3e4f5a6b7c8" \
--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}/monitors/9d3c4e3b-8d9e-4f0a-b1c2-d3e4f5a6b7c8",
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('monitors/9d3c4e3b-8d9e-4f0a-b1c2-d3e4f5a6b7c8');
echo $response->getStatusCode(), PHP_EOL; // 204, nothing in the body
const response = await fetch('https://www.iminify.com/api/v1/monitors/9d3c4e3b-8d9e-4f0a-b1c2-d3e4f5a6b7c8', {
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.