# Bulk Image Downloader - Resize, Convert, ZIP, Dedupe (`scrapewise/bulk-image-downloader`) Actor

Download images in bulk from image links, web pages or an Apify dataset. Each file gets a public link, optional single ZIP, resize and convert to WebP, JPG or PNG, custom file names, SHA-256 dedupe and minimum size filter. Width, height, bytes and hash per image. Failed images are free.

- **URL**: https://apify.com/scrapewise/bulk-image-downloader.md
- **Developed by:** [Scrapewise Data](https://apify.com/scrapewise) (community)
- **Categories:** Developer tools, Automation, AI
- **Stats:** 2 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.50 / 1,000 image downloadeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Bulk Image Downloader

Bulk Image Downloader saves **every image from a list of image links, from web pages or from an Apify dataset** as real files: each one gets a **public download link**, and the whole run can come as **one ZIP**. On the way it can **resize**, **convert to WebP, JPG or PNG**, **rename files by a pattern**, **skip duplicates by SHA-256** and **drop images below a minimum size**. Every row tells you the width, height, bytes, format and hash of the saved file. No login, no API key, no proxy setup.

**You pay only for images that were saved. Failed downloads, duplicates, filtered-out images and web pages are free.**

### At a glance

- **Three sources in one run:** direct image links, web pages (all images in the HTML), and any Apify dataset with image links
- **Files, not just links:** each image in the key-value store with a public link, and optionally one `images.zip` with a `metadata.csv`
- **Resize and convert:** maximum width and height, WebP / JPG / PNG, quality setting
- **Clean output:** file name pattern, SHA-256 dedupe, minimum width, height and file size, format filter
- **Reads the real file:** format and dimensions come from the bytes, not from the link (JPG, PNG, WebP, GIF, AVIF, HEIC, BMP, TIFF, ICO, SVG)
- **Safe by default:** size limit per file, private and local network addresses are refused
- **Error rows:** free, with an `errorCode`

### One real row

Default input, the second link (collected on 2026-10-03):

```json
{
  "url": "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png",
  "status": "downloaded",
  "pageUrl": null,
  "fileUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/000002-PNG_transparency_demonstration_1.png?signature=<signature>",
  "key": "000002-PNG_transparency_demonstration_1.png",
  "fileName": "000002-PNG_transparency_demonstration_1.png",
  "zipFile": null,
  "format": "png",
  "contentType": "image/png",
  "width": 800,
  "height": 600,
  "bytes": 224566,
  "hash": "c4e870d358d64ce677e958c156d0e3fba466993d8e915fc81c5dc64937a9b696",
  "converted": false,
  "scrapedAt": "2026-10-03T13:20:17Z"
}
```

An image found on a web page also carries `pageUrl`, the `alt` text and where it was found (`foundIn`: `img`, `meta`, `background`, `poster`, `link`).

### Measured runs

Cloud runs on 2026-10-03, default memory (1 GB):

| Run | Result | Time |
|---|---|---|
| 240 image links, files + one ZIP | 230 saved (26 MB), 10 dead links listed as `NOT_FOUND`, free | 24 s |
| Dataset of a social media scraper (150 items, image field detected automatically), convert to JPG at 1,200 px, ZIP | 150 saved | 27 s |
| Dataset of a product scraper, field `images`, minimum width 300 px, ZIP only | 300 saved, 17 duplicates skipped | 26 s |
| 2 web pages, minimum width 100 px, ZIP | 47 saved, 16 small images filtered out | 10 s |
| 5 large photos (5 to 660 MB) with the default 25 MB limit | 4 saved (41 MB), the 660 MB file refused in under a second | 11 s |

### What you can do with it

- **AI and computer-vision datasets:** turn a list of links into same-format, same-size files with a hash per file and no duplicates.
- **Product catalogs:** point it at the dataset of your e-commerce scraper and get every product photo as WebP at 1,200 px, named your way.
- **Website migration and backup:** save every image of a list of pages before a redesign, with the page each one came from.
- **Content and SEO audits:** find oversized or broken images: the row has bytes, dimensions, format and an error code when the link is dead.

### Input

| Field | What it does |
|---|---|
| `urls` | Image links and web page links, mixed. A page is opened and all images in its HTML are downloaded |
| `datasetId` | An Apify dataset of your account (pick it in the form or paste its ID); image links are taken from every item |
| `datasetFields` | Fields that hold the links (`images`, `product.imageUrl`). Empty = detect automatically |
| `maxImages` | Stop after this many saved images |
| `maxImagesPerPage` | Image links taken from each page (default 500) |
| `createZip` | One `images.zip` with all files and a `metadata.csv` |
| `saveToKeyValueStore` | Each image as its own file with a public link (default on) |
| `fileNamePattern` | `{index}`, `{name}`, `{hash}`, `{host}`, `{page}`, `{width}`, `{height}`, `{ext}`. Default `{index}-{name}.{ext}` |
| `convertTo` | `original`, `jpg`, `png` or `webp` |
| `resizeMaxWidth`, `resizeMaxHeight` | Shrink larger images, keeping the aspect ratio. Never enlarges |
| `quality` | JPG / WebP quality, default 85 |
| `minWidth`, `minHeight`, `minFileSizeKB` | Skip small images (icons, tracking pixels). Free |
| `maxFileSizeMB` | Larger files are not downloaded (default 25, up to 200) |
| `formats` | Keep only these real formats, e.g. `jpg`, `png`, `webp` |
| `dedupeByHash` | Same file from different links is saved once (default on) |
| `urlMustMatch`, `urlMustNotMatch` | Regex filters for image links found on pages or in the dataset |
| `includeMetaImages`, `includeBackgroundImages`, `includeLinkedImages` | What counts as an image on a page: social preview, CSS background in `style`, full-size files linked from thumbnails |
| `maxConcurrency`, `downloadTimeoutSecs`, `proxyConfiguration` | Advanced |

#### Download a list of image links as WebP thumbnails

```json
{
  "urls": [
    "https://picsum.photos/id/237/1200/800",
    "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png",
    "https://images.unsplash.com/photo-1506744038136-46273834b3fb?w=1600"
  ],
  "convertTo": "webp",
  "resizeMaxWidth": 512,
  "resizeMaxHeight": 512,
  "fileNamePattern": "{hash}.{ext}"
}
```

#### Download all images from a web page to a ZIP

```json
{
  "urls": ["https://books.toscrape.com/"],
  "createZip": true,
  "minWidth": 100,
  "fileNamePattern": "{page}-{index}.{ext}"
}
```

#### Download the images of a dataset

```json
{
  "datasetId": "<dataset ID of a finished run>",
  "datasetFields": ["images"],
  "convertTo": "jpg",
  "resizeMaxWidth": 1200,
  "createZip": true
}
```

### Output

One row per image link that was tried.

| Field | Description |
|---|---|
| `url` | The source link |
| `status` | `downloaded`, `duplicate`, `filtered` or `failed` |
| `fileUrl` | Public link of the saved file in the key-value store |
| `fileName`, `key` | Name of the file (also its key in the key-value store and its name inside the ZIP) |
| `zipFile` | ZIP part that holds the file (`images.zip`, `images-part-2.zip`...) |
| `format`, `contentType` | Real format of the saved file |
| `width`, `height`, `bytes` | Of the saved file (after resize or conversion) |
| `hash` | SHA-256 of the source file |
| `converted`, `originalFormat`, `originalWidth`, `originalHeight`, `originalBytes` | When the file was resized or converted |
| `pageUrl`, `alt`, `foundIn` | For images found on a web page |
| `sourceField` | For images that came from a dataset |
| `finalUrl` | When the link redirected |
| `animated`, `warning`, `extraMegabytes` | Animated file; why a conversion was skipped; MB above the first 5 |
| `errorCode`, `error` | Why the image was not saved (never charged) |

**Where the files are:** the **Output** tab has the links to the files and to `images.zip`; the **Storage > Key-value store** tab lists every file. The record `SUMMARY` has the totals of the run.

#### Error codes (never charged)

| errorCode | Meaning |
|---|---|
| `NOT_FOUND` | The server answered 404 or 410 |
| `FORBIDDEN` | The site refused the download even with a referer and a proxy |
| `HTTP_ERROR`, `TIMEOUT`, `DOWNLOAD_FAILED` | The server failed or did not answer after retries |
| `NOT_AN_IMAGE`, `EMPTY_FILE` | The link returned something that is not an image |
| `TOO_LARGE` | Above `maxFileSizeMB`, or more than 160 megapixels |
| `TOO_SMALL`, `FORMAT_FILTERED` | Removed by your filters (`status: filtered`) |
| `DUPLICATE` | Same content as an image already saved in this run |
| `NO_IMAGES_FOUND` | A page or dataset without image links |
| `INVALID_URL`, `HOST_NOT_FOUND`, `BLOCKED_HOST` | Not an http(s) link, unknown host, or a private / local network address |
| `DATASET_NOT_FOUND` | The dataset ID does not exist in your account |
| `NOT_REACHED` | The run timeout or your maximum charge arrived; the list of pending links is in the record `NOT_PROCESSED` |

### Pricing

Pay per event, no monthly fee, no start fee, and **no Apify compute bill on top**.

| Event | Free plan | Bronze | Silver | Gold and above |
|---|---|---|---|---|
| Image downloaded (per 1,000) | US$ 45 | US$ 6.50 | US$ 5.50 | US$ 4.50 |
| Extra MB of a large image | US$ 0.001 | US$ 0.001 | US$ 0.001 | US$ 0.001 |

- Resize, conversion, public link and ZIP are included in the image price.
- The image price covers source files up to 5 MB, which is almost every image on the web. Only larger files pay the extra MB: a 7.2 MB photo is 3 extra MB (US$ 0.003).
- Failed downloads, duplicates, filtered-out images, web pages and links that are not images are free.
- On the Apify Free plan a run saves up to 100 images.

### How to use it from code, on a schedule and in integrations

- **API:** `POST https://api.apify.com/v2/acts/scrapewise~bulk-image-downloader/run-sync-get-dataset-items?token=<YOUR_TOKEN>` with the input JSON returns the rows when the run ends. Each `fileUrl` is a direct download link.
- **After another Actor:** add this Actor as an integration of your scraper and pass `{{resource.defaultDatasetId}}` as `datasetId`: every finished scrape gets its images downloaded. The Actor runs with limited permissions: it can read only the dataset you give it.
- **Schedule:** save the input as a task and schedule it in the Apify Console.
- **n8n, Make, Zapier:** use the Apify app, run this Actor and read the dataset items.

### FAQ

**How do I download all images at once?** Turn on `createZip` and open the ZIP link in the Output tab (also shown in the status message of the run).

**How long are the files kept?** As long as the run's storage is kept by your Apify plan (7 days for unnamed storages on most plans). Download the ZIP or copy the files before that.

**Does it open pages with a browser?** No. It reads the HTML the server sends, including lazy-load attributes, `srcset` (the largest version), social preview tags and inline CSS backgrounds. Images that only appear after JavaScript runs are not seen.

**Which image is taken from `srcset`?** The largest one.

**Are my files public?** The link works for anyone who has it, as with every Apify key-value store record. Nobody can list your store without your token.

**Can it keep the folder structure?** File names are flat. Use `{page}` or `{host}` in `fileNamePattern` to group files by source.

### Limitations

- No JavaScript rendering and no crawling: only the pages you list are read, one level.
- Animated GIF/WebP and SVG are saved as they are (no resize or conversion). Images above 80 megapixels are saved without conversion.
- Files above 4 MB are downloaded one at a time to keep memory low. For files above 50 MB, raise `maxFileSizeMB` and give the run 2 GB of memory or more.
- Resized or converted files lose EXIF metadata (the orientation is applied first).
- Sites that block datacenter servers may answer `FORBIDDEN`; choose a proxy in `proxyConfiguration` for those.
- Only download images you have the right to use. You are responsible for the copyright and the terms of the sources. This Actor reads public links only.
- Something broke? Open an issue on the Issues tab with the run ID; it is answered within hours.

### Changelog

- **2026-10-03, 0.1:** first release. Image links, web pages and datasets; key-value store files with public links; ZIP with metadata.csv; resize; WebP/JPG/PNG conversion; file name pattern; SHA-256 dedupe; size and format filters; free error rows.

# Actor input Schema

## `urls` (type: `array`):

Links to download from, one per line, mixed freely. A direct image link (jpg, png, webp, gif, avif, heic, svg...) is downloaded as is, for example https://picsum.photos/id/237/1200/800. A web page link, for example https://books.toscrape.com/, is opened and every image in its HTML is downloaded (up to 'Maximum images per web page'). The Actor tells them apart by what the server returns, not by the file extension. You pay only for images saved. Also accepts 'imageUrls' and 'startUrls'.

## `datasetId` (type: `string`):

Optional. An Apify dataset from your account to take image links from, picked from the list or pasted as an ID, for example the output of a product, real estate or social media scraper. Every item is scanned for image links (see 'Dataset fields'), and each saved row keeps the field it came from. Works together with the links above.

## `datasetFields` (type: `array`):

Optional. Names of the dataset fields that hold the image links, one per line, with dots for nested fields, for example images, product.imageUrl or media.0.url. Lists inside a field are expanded. Leave empty to detect image links automatically: fields named image, photo, picture, thumbnail and similar, plus any link ending in an image extension.

## `maxImages` (type: `integer`):

Stop after this many images are saved, for example 10 for a test or 5000 for a full catalog. Failed, duplicate and filtered-out images do not count and are free. Leave empty for no limit (every link and page in the input). Since you pay per image saved, this is also your cost cap.

## `maxImagesPerPage` (type: `integer`):

How many image links to take from each web page, in the order they appear in the HTML, from 1 to 5,000 (default 500). Lower it to grab only the first images of each page, such as the main product photos. Direct image links are not affected.

## `createZip` (type: `boolean`):

Off (default). On: also builds one ZIP archive of all saved images, stored as images.zip in the run's key-value store with a metadata.csv inside (link, file name, size, format, dimensions). Very large runs are split into parts: images-part-2.zip, images-part-3.zip and so on. No extra charge.

## `saveToKeyValueStore` (type: `boolean`):

On (default): each image is stored as its own file in the run's key-value store and gets a public link in 'fileUrl'. Turn off to keep only the ZIP, or turn both this and the ZIP off for a metadata-only check of your links (size, format, dimensions, SHA-256 hash) without storing files.

## `fileNamePattern` (type: `string`):

Name given to each saved file, built from placeholders: {index} (000001, order in the run), {name} (original file name), {hash} (first 12 characters of the SHA-256), {host} (site of the image), {page} (web page it was found on), {width}, {height} and {ext}. Default {index}-{name}.{ext}, for example 000001-photo.jpg. Use {hash}.{ext} to get stable names across runs. Names are made unique automatically.

## `convertTo` (type: `string`):

Convert every image to one format: JPG, PNG or WebP. Default 'Keep original format'. Transparent images converted to JPG get a white background. Animated GIF or WebP and SVG files are kept as they are. Conversion is included in the image price.

## `resizeMaxWidth` (type: `integer`):

Optional. Shrink images wider than this many pixels, keeping the aspect ratio, for example 1200. Accepts 16 to 20,000. Smaller images are never enlarged. Empty = keep the original width.

## `resizeMaxHeight` (type: `integer`):

Optional. Shrink images taller than this many pixels, keeping the aspect ratio, for example 1200. Accepts 16 to 20,000. Smaller images are never enlarged. Empty = keep the original height.

## `quality` (type: `integer`):

Compression quality from 1 to 100 (default 85) for JPG and WebP files, used only when an image is converted or resized. Lower numbers give smaller files; 75 to 85 is a good balance for the web.

## `minWidth` (type: `integer`):

Optional. Skip images narrower than this many pixels, for example 200 to leave out icons, tracking pixels and thumbnails. Skipped images get a free row and are never charged. Empty or 0 = no minimum.

## `minHeight` (type: `integer`):

Optional. Skip images shorter than this many pixels, for example 200. Skipped images get a free row and are never charged. Empty or 0 = no minimum.

## `minFileSizeKB` (type: `integer`):

Optional. Skip files smaller than this many kilobytes, for example 10 to drop placeholder and spacer images. Skipped images are free. Empty or 0 = no minimum.

## `maxFileSizeMB` (type: `integer`):

Files larger than this many megabytes are not downloaded and get a free TOO_LARGE row; the download stops as soon as the limit is passed. Accepts 1 to 200, default 25. The image price covers files up to 5 MB; each started megabyte above that is charged as 'Extra MB of a large image'.

## `formats` (type: `array`):

Optional. Keep only images whose real format (read from the file, not from the link) is in this list, one per line, for example jpg, png and webp. Other accepted values: gif, avif, heic, bmp, tiff, ico, svg. Other formats get a free row. Empty = all formats.

## `dedupeByHash` (type: `boolean`):

On (default): the same file reached through different links is saved once, compared by the SHA-256 of its content; duplicates get a free row. Turn off to save every copy.

## `urlMustMatch` (type: `string`):

Optional regular expression. Only image links found on web pages or in the dataset that match it are downloaded, for example /products/ or .jpe?g$. Links you paste directly are always downloaded.

## `urlMustNotMatch` (type: `string`):

Optional regular expression. Image links found on web pages or in the dataset that match it are ignored, for example sprite|logo|icon. Links you paste directly are always downloaded.

## `includeMetaImages` (type: `boolean`):

On (default): on web pages, also take the social preview images declared in the page head (og:image and twitter:image), which are often the main photo in full size. Turn off to take only the images in the page body.

## `includeBackgroundImages` (type: `boolean`):

On (default): on web pages, also take background-image links written in style attributes, common for banners and hero images. Turn off to skip them.

## `includeLinkedImages` (type: `boolean`):

Off (default). On: on web pages, also download image files that the page links to, typical for galleries where a small thumbnail links to the full-size file.

## `maxConcurrency` (type: `integer`):

How many files are downloaded at the same time, from 1 to 64 (default 16), with at most 8 per site so no single site is overloaded. Lower it if a site starts refusing downloads.

## `downloadTimeoutSecs` (type: `integer`):

Give up on a file that does not finish downloading in this many seconds, from 5 to 600 (default 60). Timed-out files get a free row and are not charged.

## `proxyConfiguration` (type: `object`):

Off by default and not needed for most sites: images are downloaded directly, and an Apify datacenter proxy is tried automatically when a site refuses the first attempt. Turn on to send every download through the proxy you choose, for example a residential group for a site that blocks datacenter IPs.

## Actor input object example

```json
{
  "urls": [
    "https://picsum.photos/id/237/1200/800",
    "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png",
    "https://books.toscrape.com/"
  ],
  "maxImages": 10,
  "maxImagesPerPage": 500,
  "createZip": false,
  "saveToKeyValueStore": true,
  "fileNamePattern": "{index}-{name}.{ext}",
  "convertTo": "original",
  "quality": 85,
  "maxFileSizeMB": 25,
  "dedupeByHash": true,
  "includeMetaImages": true,
  "includeBackgroundImages": true,
  "includeLinkedImages": false,
  "maxConcurrency": 16,
  "downloadTimeoutSecs": 60,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `resultsCsv` (type: `string`):

No description

## `files` (type: `string`):

No description

## `zip` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "urls": [
        "https://picsum.photos/id/237/1200/800",
        "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png",
        "https://books.toscrape.com/"
    ],
    "maxImages": 10,
    "fileNamePattern": "{index}-{name}.{ext}",
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapewise/bulk-image-downloader").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "urls": [
        "https://picsum.photos/id/237/1200/800",
        "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png",
        "https://books.toscrape.com/",
    ],
    "maxImages": 10,
    "fileNamePattern": "{index}-{name}.{ext}",
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("scrapewise/bulk-image-downloader").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "urls": [
    "https://picsum.photos/id/237/1200/800",
    "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png",
    "https://books.toscrape.com/"
  ],
  "maxImages": 10,
  "fileNamePattern": "{index}-{name}.{ext}",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call scrapewise/bulk-image-downloader --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapewise/bulk-image-downloader"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/vKBgbBi6W54t6Xo9p/builds/fwX9uS86Zgw8Nby9X/openapi.json
