Site crawls API
Crawl several pages of one site at once, from its sitemap or a list, with one report and one zip for the whole site. Every site crawls endpoint with samples.
A site crawl scans several pages of one site in one go. Give it the site's sitemap, or a list of its pages, and each page is scanned as a page scan of its own, with the settings you send. An image the pages share is compressed once, and the crawl gives you one report and one zip for the whole site.
Site crawls are a Pro feature, on the website's scan page as well as here; another plan is answered with pro_required. A crawl started on Pro can still be read, downloaded and deleted after Pro ends.
Starting one
POST /crawls takes either sitemap or pages, not both, plus the same compression settings as a scan:
sitemapis the address of an XML sitemap, a sitemap index or a text file with one address a line, gzipped or not. The crawl reads up to 10 MB of it and takes its first pages in the sitemap's order, only those on the sitemap's own site (with or withoutwww.). From a sitemap index it opens at most 5 of the sitemaps it lists, in order, until it has enough pages, and passes over one it can't read.pagesis a list of page addresses on one site. An address listed twice is crawled once, andhttps://can be left out of each. A list longer than your plan's pages per crawl is refused rather than cut, and so is one that mixes two sites.
Each crawl counts one against your daily site crawls as soon as it's queued, even if its sitemap turns out to be unreadable. Its pages don't count against your daily page scans, and their images don't count against your daily images. Each page stops at your plan's images per scan, as any scan does. The numbers for every plan are on the pricing page, and yours are in GET /account under limits.crawls_per_day and limits.pages_per_crawl.
A sitemap with more pages than the crawl takes says how many it left out in pages_left_out. For a sitemap index that's a floor, because the crawl stops reading once it has enough.
The life of a crawl
queued ──► reading ──► scanning ──► completed
│ │
└───────────┴──► failed
reading means the sitemap is being fetched, and a crawl from a list passes through it in a moment. In scanning its pages have been handed to the scanner, and they and their images are still at work. It turns completed once every page has finished, images included. failed means the crawl couldn't start its pages, and note says why, such as a sitemap that couldn't be fetched or read, or one that listed no page on its own site.
Poll GET /crawls/{id} every five seconds or so. The crawl is done once its status is completed or failed and both pages_in_progress and images_in_progress are 0. A failed crawl can still have pages it handed over before it stopped; they finish on their own.
Its pages
A single crawl lists its pages in pages, each with its status and image counts. Each one is a page scan: get it for its images, the addresses it passed over and its own page report, and list its images with the image list's scan filter. A page scan names its crawl in crawl_id.
The pages stay out of the scan list, because the crawl lists them. A page can be retried or cancelled like any scan, and a retry counts against your daily page scans. It can't be deleted on its own: the pages share the images they have in common, so a page goes with its crawl, and deleting one is answered with part_of_crawl.
When two pages show the same image, the first page to reach it compresses it, and the other lists it in skipped as a duplicate of that address. Its report entry points at the image the other page compressed (image_id). Two pages that reach the same image at the same moment can both compress it.
The site's report
Once nothing in the crawl is at work, a single crawl carries the site's report in audit: how many image addresses the pages had and how many to fix, each finding's count, what the oversized images would shed, and the bytes a browser downloaded. An image the pages share counts once. Its findings depend on the page showing it (the size it's shown at, the first screen), so it counts under a finding when any page gave it that finding, and its waste is the most any page measured. Each entry in pages also says how many of that page's images to fix, in images_to_fix.
Each page's own report, with every image and what's worth fixing about it, is on its scan, and on Pro it can be shared or exported as CSV or PDF the way any scan's can.
The zip
GET /crawls/{id}/download gives one zip with a folder for each page, named after the page's path: https://example.com/blog/hello-world becomes blog-hello-world/, and the front page home/. Each folder is laid out like that page's own scan zip, with its own manifest.csv.
An image the pages share sits only in the folder of the page that compressed it, and the other pages' manifests point at it there, every path given from the zip's root. The zip is ready once the crawl is done; until then it's refused with scan_in_progress.
Checking the site every week
On Pro, a crawl can become a weekly site check: POST /monitors with its id, and every week the site is crawled again the same way, compressing only the images that are new or changed. Each week's check is a crawl of its own, named by its check in monitor_id, and stays out of the crawl list.
Webhooks
A crawl sends no event of its own. On Pro, each of its pages sends scan.completed or scan.failed like any scan, with the crawl's id in crawl_id, and each image sends its own image event. See Webhooks.
Through an AI agent
The MCP server's crawl_site starts a crawl, get_crawl follows it and carries the site's report, and create_zip with crawl_id downloads the zip. See the MCP server.
The Crawl object
-
idstring - The crawl's id.
-
urlstring - The sitemap the crawl read, or the first of the pages it was given.
-
sourcestring -
Where its pages came from:
sitemaporpages, a list. -
monitor_idstring or null -
The weekly site check this crawl is a run of, or
nullfor a crawl you started. A check's runs are left out of the crawl list; the check names its newest. -
statusstring -
Where the crawl is.
queued: waiting to start.reading: its sitemap is being read.scanning: its pages have been handed to the scanner and they, or their images, are still at work.completed: every page has finished, its images included.failed: the crawl could not start its pages (notesays why); a failed crawl may still have pages it started, which finish on their own. -
notestring or null -
Why the crawl failed, or
null. -
settingsobject - The settings every image of every page is compressed with.
-
settings.levelstring - The compression level.
-
settings.formatstring or null -
The format every image is converted to, or
nullto keep each one's own. -
settings.keep_metadataboolean - Whether EXIF metadata is kept.
-
settings.widthinteger or null -
The width every image is resized to, or
null. -
settings.heightinteger or null -
The height every image is resized to, or
null. -
settings.scaleinteger or null -
The percentage every image is scaled to, or
null. -
pages_countinteger - How many pages the crawl took. 0 until its sitemap has been read.
-
pages_in_progressinteger - How many of them are still queued or being scanned.
-
pages_failedinteger -
How many of them failed. Each one's
notesays why. -
pages_left_outinteger - How many more pages the sitemap or the list had past your plan's pages per crawl, which were not scanned. For a sitemap index, a floor: it stops reading once it has enough.
-
images_countinteger - How many images the pages queued. An image the pages share is compressed once, so it counts once.
-
images_in_progressinteger -
How many of them are still queued or being compressed. The crawl is done once its status is
completedorfailedand bothpages_in_progressand this are 0. -
images_finishedinteger - How many of them have finished.
-
original_sizeinteger - Bytes of the finished images before compression.
-
optimized_sizeinteger - Bytes of the same images after compression.
-
saved_bytesinteger - The difference between the two.
-
saved_percentnumber or null -
The same saving as a percentage, or
nullwhile no image has finished. -
download_urlstring - Where to download every page's images as one zip, a folder per page with its manifest, once the crawl is done.
-
pagesarray - The pages, in the order the crawl took them, each a page scan of its own. Only on a single crawl, not in a list.
-
auditstring or null -
The site's report: every page's audit added up, an image the pages share counted once.
nullwhile any page or image is at work, and for a crawl with no page. Only on a single crawl, not in a list. -
created_attimestamp - When the crawl was queued.
-
updated_attimestamp - When it last changed.
The CrawlPage object
-
idstring -
The page scan's id, for
GET /scans/{id}, its report, and its images in the image list'sscan. -
urlstring - The page.
-
statusstring - Where the scanner is, as on a page scan.
-
notestring or null -
What the scanner wrote when it finished, or why it failed.
nulluntil then. -
images_countinteger - How many images the page queued. An image another page of the crawl compressed first is not among them.
-
images_in_progressinteger - How many of them are still queued or being compressed.
-
images_to_fixinteger or null -
How many image addresses the page's report found something worth fixing about, or
nulluntil the crawl's report exists.
The CrawlAudit object
-
pages_auditedinteger - How many of the pages have a report. A page that failed has none.
-
images_foundinteger - How many image addresses the pages had, each counted once however many pages show it.
-
images_to_fixinteger - How many of them at least one page found something worth fixing about.
-
findingsobject - How many image addresses have each finding on at least one page. An image's findings depend on the page showing it (its size there, the first screen), so one page can flag an image another doesn't.
-
findings.oversizedinteger - Images whose file is more than twice as wide and twice as tall as the space they are shown in.
-
findings.without_dimensionsinteger - Images shown without width and height attributes (and no CSS aspect ratio).
-
findings.lazy_above_foldinteger - Images in the first screen that are lazy loaded.
-
findings.eager_below_foldinteger - Images below the first screen that are not lazy loaded.
-
findings.missing_altinteger - Images without alt text.
-
findings.lcp_not_prioritizedinteger -
Images that are a page's largest paint and lack
fetchpriority="high". -
oversized_bytesinteger - About how many bytes the oversized images would shed if each were served at twice the size it is shown, each address once, at the most any page measured.
-
bytesinteger - The bytes a browser downloaded for the images, each address once.
List site crawls
/api/v1/crawls
Every site crawl on your account, newest first, each with its counts and totals. The pages themselves come with a single crawl. The crawls a weekly site check runs by itself are not listed.
Query parameters
-
per_pageinteger - Crawls 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/crawls?per_page=5" \
--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}/crawls",
headers=HEADERS,
timeout=(10, 30),
params={
"per_page": 5,
},
)
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('crawls', [
'query' => [
'per_page' => 5,
],
]);
print_r(json_decode((string) $response->getBody(), true));
const response = await fetch('https://www.iminify.com/api/v1/crawls?per_page=5', {
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": "9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6",
"url": "https://example.com/sitemap.xml",
"source": "sitemap",
"monitor_id": null,
"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": 12,
"images_in_progress": 0,
"images_finished": 12,
"original_size": 18406331,
"optimized_size": 2114902,
"saved_bytes": 16291429,
"saved_percent": 88.51,
"download_url": "https://www.iminify.com/api/v1/crawls/9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6/download",
"created_at": "2026-10-01T11:04:50+00:00",
"updated_at": "2026-10-01T11:04:55+00:00"
}
],
"links": {
"first": "https://www.iminify.com/api/v1/crawls?page=1",
"last": "https://www.iminify.com/api/v1/crawls?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/crawls?page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": null,
"label": "Next »",
"page": null,
"active": false
}
],
"path": "https://www.iminify.com/api/v1/crawls",
"per_page": 15,
"to": 1,
"total": 1
}
}
Crawl a site
/api/v1/crawls
A Pro feature. Reads the site's sitemap, or takes the pages you list, and scans each page as a page scan of its own, with the settings you send. An image the pages share is compressed once, unless two pages reach it at the same moment. It answers straight away with the status queued; a crawl takes a few minutes. Each crawl counts one against your daily site crawls, even when its sitemap turns out to be unreadable; its pages don't count against your daily page scans, nor their images against your daily images.
Each page stops at your plan's images per scan, as a scan does. A sitemap on a private network is refused, and so is a page on one.
Body (JSON)
-
sitemapstring (URL) -
The address of the site's sitemap: an XML sitemap, a sitemap index or a text file with one address a line, gzipped or not. The crawl takes its first pages on the sitemap's own site, with or without
www., up to your plan's pages per crawl.https://can be left out. Send this orpages, not both. -
pagesarray of strings -
The pages to crawl, all on one site (with or without
www.), up to your plan's pages per crawl.https://can be left out of each, and an address listed twice is crawled once. A longer list is refused, not cut. Send this orsitemap, not both. -
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 -
alt_textboolean -
Write an AI alt text for each image a page shows with no alt attribute at all, from its compressed copy, for that page's report (
suggested_alton its scan). On by default; sendfalseto leave it out. Each one counts against your daily AI alt texts. 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
Request
curl -X POST "https://www.iminify.com/api/v1/crawls" \
--connect-timeout 10 --max-time 30 \
-H "Authorization: Bearer $IMINIFY_API_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"sitemap":"https://example.com/sitemap.xml","level":"smart","format":"webp"}'
import os
import requests
API = "https://www.iminify.com/api/v1"
HEADERS = {
"Authorization": f"Bearer {os.environ['IMINIFY_API_KEY']}",
"Accept": "application/json",
}
response = requests.post(
f"{API}/crawls",
headers=HEADERS,
timeout=(10, 30),
json={
"sitemap": "https://example.com/sitemap.xml",
"level": "smart",
"format": "webp",
},
)
response.raise_for_status()
print(response.json())
<?php
require __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Client;
$client = new Client([
'base_uri' => 'https://www.iminify.com/api/v1/',
'connect_timeout' => 10,
'timeout' => 30,
'headers' => [
'Authorization' => 'Bearer ' . getenv('IMINIFY_API_KEY'),
'Accept' => 'application/json',
],
]);
$response = $client->post('crawls', [
'json' => [
'sitemap' => 'https://example.com/sitemap.xml',
'level' => 'smart',
'format' => 'webp',
],
]);
print_r(json_decode((string) $response->getBody(), true));
const response = await fetch('https://www.iminify.com/api/v1/crawls', {
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({
sitemap: 'https://example.com/sitemap.xml',
level: 'smart',
format: 'webp',
}),
});
if (!response.ok) {
throw new Error(`${response.status}: ${(await response.json()).message}`);
}
console.log(await response.json());
Response 202
{
"data": {
"id": "9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6",
"url": "https://example.com/sitemap.xml",
"source": "sitemap",
"monitor_id": null,
"status": "queued",
"note": null,
"settings": {
"level": "smart",
"format": "webp",
"keep_metadata": false,
"width": 1600,
"height": null,
"scale": null
},
"pages_count": 0,
"pages_in_progress": 0,
"pages_failed": 0,
"pages_left_out": 0,
"images_count": 0,
"images_in_progress": 0,
"images_finished": 0,
"original_size": 0,
"optimized_size": 0,
"saved_bytes": 0,
"saved_percent": null,
"download_url": "https://www.iminify.com/api/v1/crawls/9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6/download",
"created_at": "2026-10-01T11:04:50+00:00",
"updated_at": "2026-10-01T11:04:50+00:00"
}
}
Get a site crawl
/api/v1/crawls/{id}
One crawl with its pages and, once nothing is at work, the site's report. Poll this while a crawl runs: it is done once its status is completed or failed and both pages_in_progress and images_in_progress are 0. Each page is a page scan: get it for its images, its skipped addresses and its own report.
Path parameters
-
idstring (id) required - The crawl's id.
Request
curl "https://www.iminify.com/api/v1/crawls/9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6" \
--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}/crawls/9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6",
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('crawls/9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6');
print_r(json_decode((string) $response->getBody(), true));
const response = await fetch('https://www.iminify.com/api/v1/crawls/9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6', {
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": "9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6",
"url": "https://example.com/sitemap.xml",
"source": "sitemap",
"monitor_id": null,
"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": 12,
"images_in_progress": 0,
"images_finished": 12,
"original_size": 18406331,
"optimized_size": 2114902,
"saved_bytes": 16291429,
"saved_percent": 88.51,
"download_url": "https://www.iminify.com/api/v1/crawls/9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6/download",
"pages": [
{
"id": "9d3c4f2a-1b2c-4d3e-8f4a-5b6c7d8e9f01",
"url": "https://example.com/",
"status": "completed",
"note": "Rendered in a headless browser. 1 image URL found: 1 in a format the compressor does not accept.",
"images_count": 0,
"images_in_progress": 0,
"images_to_fix": 0
},
{
"id": "9d3c4f12-7a8b-4c9d-a0e1-f2a3b4c5d6e7",
"url": "https://example.com/pricing",
"status": "completed",
"note": "Rendered in a headless browser. 14 image URLs found: 12 queued for compression, 1 duplicates of an image already queued, 1 in a format the compressor does not accept.",
"images_count": 12,
"images_in_progress": 0,
"images_to_fix": 2
}
],
"audit": {
"pages_audited": 2,
"images_found": 4,
"images_to_fix": 2,
"findings": {
"oversized": 1,
"without_dimensions": 1,
"lazy_above_fold": 0,
"eager_below_fold": 1,
"missing_alt": 1,
"lcp_not_prioritized": 1
},
"oversized_bytes": 180384,
"bytes": 899380
},
"created_at": "2026-10-01T11:04:50+00:00",
"updated_at": "2026-10-01T11:04:55+00:00"
}
}
Delete a site crawl
/api/v1/crawls/{id}
Deletes the crawl, every page it scanned and every image they produced, with their files, for good. The pages share the images they have in common, so they go with their crawl, never one by one. A crawl with a page or an image still at work is refused.
Path parameters
-
idstring (id) required - The crawl's id.
Request
curl -X DELETE "https://www.iminify.com/api/v1/crawls/9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6" \
--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}/crawls/9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6",
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('crawls/9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6');
echo $response->getStatusCode(), PHP_EOL; // 204, nothing in the body
const response = await fetch('https://www.iminify.com/api/v1/crawls/9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6', {
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 a site crawl as a zip
/api/v1/crawls/{id}/download
One zip with a folder for each page, named after the page's path (blog-hello-world, and home for the front page). Each folder is laid out like that page's own scan zip, with its manifest.csv. An image the pages share is in the folder of the page that compressed it, and the other pages' manifests point at it there, every path from the zip's root. It is ready once the crawl is done, so a crawl still at work is refused.
Path parameters
-
idstring (id) required - The crawl's id.
Request
curl --fail-with-body -o crawl.zip "https://www.iminify.com/api/v1/crawls/9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6/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}/crawls/9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6/download",
headers=HEADERS,
timeout=(10, 300),
)
response.raise_for_status()
with open("crawl.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('crawls/9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6/download', [
'sink' => 'crawl.zip',
'timeout' => 1800,
]);
echo 'Saved crawl.zip', PHP_EOL;
import { writeFile } from 'node:fs/promises';
const response = await fetch('https://www.iminify.com/api/v1/crawls/9d3c4e07-5c6d-4e7f-9a0b-c1d2e3f4a5b6/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('crawl.zip', Buffer.from(await response.arrayBuffer()));
Response 200
The file itself, not JSON.