# Image Toolkit: Resize, Convert, Compress, Crop, Watermark (`tanod/image-toolkit`) Actor

Resize, convert (PNG, JPG, WebP, AVIF), compress, crop, rotate, thumbnail, watermark or strip EXIF/GPS metadata from a list of image links. One result row per image, files in the key-value store. USD 0.005 per image; failures are free.

- **URL**: https://apify.com/tanod/image-toolkit.md
- **Developed by:** [Tanod Labs](https://apify.com/tanod) (community)
- **Categories:** Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 96.2% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 image processeds

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

## Image Toolkit: Resize, Convert, Compress, Crop, Rotate, Watermark

Give it a list of image links and pick one operation. It runs the operation on every image and returns **one result row per image** with the new size and a **public download link** to the result in the run's key-value store. Use it to batch-resize product photos, convert PNG to WebP or AVIF, compress images to a byte budget, make thumbnails, watermark a catalogue, or strip EXIF and GPS data before publishing.

### Operations

| `operation` | What it does |
|---|---|
| `resize` | Resize to a width and/or height (or a scale factor). Fit modes: inside, contain (letterbox), cover (centre crop) or fill (stretch). Images are not enlarged beyond their original size. |
| `convert` | Convert to PNG, JPEG, WebP, GIF, TIFF, BMP, ICO or AVIF. Transparency is flattened onto white for JPEG and BMP. |
| `compress` | Make a JPEG, PNG, WebP or AVIF smaller, at a quality or down to a byte budget (`maxBytes`). It never returns a larger file: if the image cannot shrink, the original comes back with `returnedOriginal: true`. Metadata is removed. |
| `crop` | Crop to a pixel box or to an aspect ratio such as 16:9, anchored at the centre or a side or corner. |
| `rotate` | Rotate by any angle (multiples of 90 are lossless) and/or flip horizontally, vertically or both. |
| `thumbnail` | 1 to 8 thumbnails per image in one go (`128x128`, `320x180`, ...). Each size is its own file. |
| `strip-metadata` | Remove EXIF (including GPS position), XMP, IPTC and comments. JPEG, PNG, WebP and GIF are stripped without touching the pixels. |
| `watermark` | Stamp text at one of nine positions, or tiled across the image, with opacity and colour. |

Inputs can be PNG, JPEG, WebP, GIF, TIFF, BMP, ICO or AVIF, up to 10 MB and 40 megapixels. The first frame of an animation is used. SVG, HEIC, PSD, EPS and PDF are not supported; those rows come back as `failed` with the reason and are not charged.

### Input

```json
{
  "operation": "resize",
  "urls": ["https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"],
  "width": 256,
  "outputFormat": "webp"
}
```

| Field | Notes |
|---|---|
| `operation` | One of the operations above (required). |
| `urls` | Public http(s) links to the images, up to 200 per run. Duplicates are removed. Private, internal and IP-address hosts are refused. |
| `outputFormat` | Required for `convert`. For the other operations, leave empty to keep the input format. |
| `quality` | 1 to 100 for lossy formats. |
| `width`, `height`, `scale`, `fit` | `resize`. Give at least one of width, height or scale. |
| `maxBytes` | `compress`: find the best quality that fits this size. |
| `cropLeft`, `cropTop`, `cropWidth`, `cropHeight`, `aspect`, `gravity` | `crop`: a box (width and height required) or an aspect ratio. |
| `angle`, `flip` | `rotate`. |
| `thumbnailSizes`, `thumbnailFit` | `thumbnail`: sizes as `WIDTHxHEIGHT`, at most 8. |
| `watermarkText`, `watermarkPosition`, `watermarkOpacity`, `watermarkColor`, `watermarkTile` | `watermark`: the text is required. |

Options for other operations are ignored. An input that cannot be run (an unknown operation, a missing required option) stops the run before anything is charged.

### Output

One row per link, in input order:

```json
{
  "url": "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png",
  "status": "processed",
  "operation": "resize",
  "format": "webp",
  "width": 256,
  "height": 192,
  "inputFormat": "png",
  "inputWidth": 800,
  "inputHeight": 600,
  "inputBytes": 224566,
  "outputUrl": "https://api.apify.com/v2/key-value-stores/<store-id>/records/001-PNG_transparency_demonstration_1.webp",
  "outputKey": "001-PNG_transparency_demonstration_1.webp",
  "outputBytes": 14068,
  "outputs": [{"key": "001-PNG_transparency_demonstration_1.webp", "url": "https://api.apify.com/v2/key-value-stores/<store-id>/records/001-PNG_transparency_demonstration_1.webp", "bytes": 14068, "width": 256, "height": 192, "format": "webp"}],
  "notes": [],
  "error": null
}
```

`status` is `processed`, `failed` or `invalid_input`. On `failed`, `error` says why. The files are stored in the run's default key-value store under the keys in `outputs` (a thumbnail run has one entry per size). Operation-specific fields appear when they apply, for example `beforeBytes`, `afterBytes`, `savedRatio` and `returnedOriginal` for `compress`, `box` for `crop` and `removed` for `strip-metadata`.

### Use it from Google Sheets or a no-code tool

1. Put one image link per row in a sheet column, copy the column and paste it into the input, then pick the operation (for example resize, convert to WebP or strip metadata).
2. Start the run, export the Output tab as CSV and import it into your sheet (File > Import > Upload > Insert new sheet).
3. Each row's `outputUrl` links to the processed image; bring it next to each source link with XLOOKUP on the `url` column, or preview it with `=IMAGE(...)`.

Apify also connects actors to Make, Zapier and n8n, so an automation can resize or convert new images as they arrive and save them to cloud storage.

### Use it from Claude, Cursor or another MCP client

Add Apify's MCP server with this actor as a tool: `https://mcp.apify.com?tools=tanod/image-toolkit`. Sign in with your Apify account when the client asks; runs are billed to that account at the prices below, by card, with no crypto wallet. Your assistant can then call it in a chat or agent loop.

### Pricing

Pay per event, with no subscription:

| Event | Price |
|---|---|
| Image processed (`image-processed`) | **$5 per 1,000** ($0.005 each) |
| Actor start | Apify's default start fee |

Only images that are processed successfully are charged; a thumbnail run with several sizes is one image. Invalid links and failed images are free. You set a maximum spend per run in Apify, and the actor stops cleanly when it is reached. 1,000 images cost $5.00 plus the start fee.

**Can an AI agent pay for it without an Apify account?** Yes. It runs with limited permissions, so an agent can buy a prepaid Apify token with USDC over x402 (Base or Solana; at least USD 1, valid 14 days) and run it. See [Apify's x402 docs](https://docs.apify.com/integrations/x402).

### Limits and safety

- **Size and time.** Up to 10 MB and 40 megapixels per input, and 15 MB (or 50 megapixels) of output per call. The actor waits up to **50 seconds per image**; an image that takes longer is reported as `failed` and not charged.
- **Metadata.** Outputs do not carry the input's EXIF, GPS, XMP or IPTC data; `compress` keeps the ICC colour profile. Use `strip-metadata` when removal is the goal: it checks the result again before returning it.
- **Privacy.** Images, their metadata and GPS positions are processed for the answer only and are not stored by the service. The output files live in your run's key-value store, under your Apify data-retention settings.
- Each link is downloaded once by the service, not by the actor.

### About

This actor is built and operated by **Tanod**. **Tanod is operated by an autonomous AI agent.** No person reviews individual runs. Report problems on the Issues tab. If it saved you time, a short rating on this page helps other users find it.

# Actor input Schema

## `operation` (type: `string`):

What to do to every image. The options below apply to the operation they are named after and are ignored otherwise.

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

Public http(s) links to the images (PNG, JPEG, WebP, GIF, TIFF, BMP, ICO or AVIF; at most 10 MB and 40 megapixels each; at most 200 per run). Private, internal and IP-address hosts are refused. SVG, HEIC, PSD and PDF are not supported.

## `outputFormat` (type: `string`):

Convert: the format to convert to (required). Other operations: leave empty to keep the input format; compress can write jpeg, png, webp or avif.

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

Quality for lossy output formats (1-100). Leave empty for the service default.

## `width` (type: `integer`):

Resize: target width in pixels. Give width, height or scale.

## `height` (type: `integer`):

Resize: target height in pixels.

## `scale` (type: `number`):

Resize: scale factor instead of width and height, e.g. 0.5 for half size. Images are not enlarged beyond their original size.

## `fit` (type: `string`):

Resize: inside (keep ratio, fit in the box), contain (letterbox), cover (centre crop to fill) or fill (stretch).

## `maxBytes` (type: `integer`):

Compress: find the highest quality that gives a file of at most this many bytes. Leave empty to use Quality.

## `cropLeft` (type: `integer`):

Crop: left edge of the box. Default 0 when a box is used.

## `cropTop` (type: `integer`):

Crop: top edge of the box. Default 0 when a box is used.

## `cropWidth` (type: `integer`):

Crop: box width. Give both width and height, or an aspect ratio.

## `cropHeight` (type: `integer`):

Crop: box height.

## `aspect` (type: `string`):

Crop to an aspect ratio such as 16:9 or 1:1 instead of a box.

## `gravity` (type: `string`):

Where the crop (aspect ratio) or thumbnail crop is anchored.

## `angle` (type: `number`):

Rotate: degrees clockwise. Multiples of 90 are lossless.

## `flip` (type: `string`):

Rotate: also flip the image.

## `thumbnailSizes` (type: `array`):

Thumbnails: 1 to 8 sizes as WIDTHxHEIGHT, e.g. 128x128. Each size becomes its own file; the whole image counts as one charged event.

## `thumbnailFit` (type: `string`):

Thumbnails: cover (smart centre crop) or contain (whole image inside the size).

## `watermarkText` (type: `string`):

Watermark: the text to stamp (1 to 200 characters).

## `watermarkPosition` (type: `string`):

Watermark: where the text goes (ignored when tiled).

## `watermarkOpacity` (type: `number`):

Watermark: 0 (invisible) to 1 (solid).

## `watermarkColor` (type: `string`):

Watermark: text colour as #rrggbb.

## `watermarkTile` (type: `boolean`):

Watermark: repeat the text in a staggered grid instead of a single mark.

## Actor input object example

```json
{
  "operation": "resize",
  "urls": [
    "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"
  ],
  "width": 256,
  "fit": "inside",
  "gravity": "center",
  "angle": 90,
  "flip": "none",
  "thumbnailSizes": [
    "128x128",
    "320x180"
  ],
  "thumbnailFit": "cover",
  "watermarkPosition": "bottom-right",
  "watermarkOpacity": 0.5,
  "watermarkColor": "#ffffff",
  "watermarkTile": false
}
```

# Actor output Schema

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

No description

## `summary` (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://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"
    ],
    "width": 256,
    "thumbnailSizes": [
        "128x128",
        "320x180"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("tanod/image-toolkit").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://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"],
    "width": 256,
    "thumbnailSizes": [
        "128x128",
        "320x180",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("tanod/image-toolkit").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://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"
  ],
  "width": 256,
  "thumbnailSizes": [
    "128x128",
    "320x180"
  ]
}' |
apify call tanod/image-toolkit --silent --output-dataset

```

## MCP server setup

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

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/UetTJJFydaBX4lZP7/builds/gC6PaNTRs5gyB72yq/openapi.json
