# AI Image Upscaler - 2x/4x Real-ESRGAN, CPU, from URLs (`tiger19935/image-upscaler`) Actor

Upscale images from URLs 2x or 4x with Real-ESRGAN (CPU, models bundled). Photo/general and anime models, PNG/JPG/WebP output, public download link per image. Pay only for images actually upscaled.

- **URL**: https://apify.com/tiger19935/image-upscaler.md
- **Developed by:** [Andrew Boiko](https://apify.com/tiger19935) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $16.00 / 1,000 image upscaleds

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

## AI Image Upscaler - 2x / 4x Real-ESRGAN from URLs

Upscale images 2x or 4x with **Real-ESRGAN neural networks running on CPU**, straight from a list of image URLs. Each result is stored in the run's key-value store and comes with a public download link in the dataset. No GPU, no API keys, no model downloads at run time: the networks are bundled in the Actor image.

**Pay only for images that were actually upscaled and stored.** Failed downloads, undecodable files, oversized inputs and duplicates are never charged. New Apify accounts can try it on the free plan credit.

### Who is it for

- E-commerce and marketplace sellers who need larger versions of small supplier or catalogue images.
- Designers, bloggers and agencies refreshing old low-resolution assets (logos, screenshots, illustrations, thumbnails).
- Developers who want an upscale step in a pipeline (Make, n8n, Zapier, your own code) without hosting a model.

### What it does and what it does not do

- It runs the open-source Real-ESRGAN family through `realesrgan-ncnn-py` (ncnn inference engine, CPU mode, `gpuid=-1`).
- It does **not** restore faces. There is no face-enhancement option, and none is claimed.
- It cannot add information that is not in the picture. Neural upscaling invents plausible detail; it can look sharper than a normal resize but may hallucinate textures or distort small text. Do not use it for evidence, medical or forensic images, or anywhere exact pixels matter.
- It is built for small and medium inputs. See "Limits" for the exact caps per model.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `imageUrls` | array of strings | required | Direct http(s) image URLs (JPEG, PNG, WebP, GIF, BMP, TIFF; max 30 MB each). Web pages are not crawled. |
| `scale` | 2 or 4 | 4 | Width and height multiplier. |
| `model` | `general`, `anime-hq`, `photo-hq` | `general` | See the model table below. |
| `denoise` | string "0".."1" | "0" | Optional non-local-means noise filter applied **before** upscaling (a pre-filter, not a model-based denoise). |
| `outputFormat` | `png`, `jpg`, `webp` | `png` | JPG has no transparency (transparent areas become white). |
| `quality` | 40-100 | 92 | JPG/WebP quality. |
| `maxInputPixels` | 1,000 - 2,000,000 | 1,000,000 | Inputs with more pixels (width x height) are rejected before any charge. |
| `maxImages` | 1-500 | 100 | Only the first N URLs are processed. |
| `includeFailed` | boolean | false | Add free `status: "failed"` rows for images that failed (never charged). |

#### Models

| `model` | Network | Speed (measured, 1 CPU core) | Max input | Best for |
|---|---|---|---|---|
| `general` | Real-ESRGAN `realesr-animevideov3` (compact, native 2x and 4x) | about 8 s for 512x512 -> 2048x2048 | 2 MP | Everything, default |
| `anime-hq` | Real-ESRGAN `x4plus-anime` | about 23 s for 256x256 -> 1024x1024 | 0.1 MP (about 316x316) | Line art, flat colors |
| `photo-hq` | Real-ESRGAN `x4plus` | about 45 s for 256x256 -> 1024x1024 | 0.04 MP (about 200x200) | Small photos, avatars |

For 2x with the HQ models the network runs at 4x and the result is scaled down with Lanczos, so it takes as long as 4x. The `general` model was trained mostly on anime video frames but is a general-purpose compact network; the HQ photo model gives better texture on real photos but is much slower, which is why its input cap is small.

### Output

One dataset row per upscaled image. The image itself is in the default key-value store; `upscaleUrl` is a public link to it.

```json
{
  "id": "cd6d73be58717af8",
  "originalUrl": "https://example.com/images/product-small.jpg",
  "inputWidth": 640,
  "inputHeight": 480,
  "inputFormat": "JPEG",
  "inputBytes": 48211,
  "outputWidth": 2560,
  "outputHeight": 1920,
  "outputFormat": "png",
  "outputBytes": 5912044,
  "scale": 4,
  "model": "general",
  "engineModel": "realesr-animevideov3",
  "denoise": 0.0,
  "upscaleUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/upscaled-cd6d73be58717af8-x4-general.png",
  "recordKey": "upscaled-cd6d73be58717af8-x4-general.png",
  "seconds": 9.4,
  "chargedEvents": 1,
  "note": null,
  "processedAt": "2026-10-14T10:43:41.062Z",
  "attribution": "Upscaled with Real-ESRGAN (BSD-3-Clause) via realesrgan-ncnn-py (MIT), CPU inference"
}
```

The run also writes an `OUTPUT` record (counts, charged events, every skipped URL with its reason, stop reason) and a short `RESULTS_MD` summary to the key-value store.

### Pricing

Pay-per-event, **pay only for images that were upscaled and stored**:

| Event | Price | When |
|---|---|---|
| `image` | **$0.02** | One image upscaled and stored, output up to 2048x2048 pixels (about 4.19 MP). Larger output counts as **2 events**. |

Not charged: failed downloads, HTTP errors, files that are not images, images over the input pixel limit, duplicate image content within a run, rows added by `includeFailed`, and anything beyond your maximum cost per run (the run stops softly and `OUTPUT.stoppedReason` is `budget`). Apify may apply Bronze/Silver/Gold plan discounts to the price.

A 4x upscale of a 1 MP image gives 16 MP output and therefore costs 2 events ($0.04).

### How it works

1. URLs are validated, de-duplicated and capped to `maxImages`.
2. Each image is downloaded with a timeout, retries on 429/5xx and a 30 MB size cap. URLs that resolve to private network addresses are blocked.
3. The image is decoded (EXIF orientation applied, first frame only for animations). Alpha channels are kept: the colors go through the network, the alpha channel is resized with Lanczos.
4. The network processes the image in tiles to keep memory low (typically 300-800 MB peak).
5. The result is encoded, stored in the key-value store, and only then charged. If the budget is not enough, the stored file is deleted and nothing is pushed.
6. Progress is saved after every image. If the run migrates or restarts, finished images are not processed or charged again.

### Limits and honest caveats

- CPU only: expect seconds for small images and a minute or more for the HQ models. Use `general` for anything bigger than a thumbnail.
- Maximum input 2 MP (2,000,000 pixels) for `general`, less for the HQ models. Larger images are rejected, not downscaled, so you stay in control.
- Output of a 2 MP image at 4x is 32 MP; large PNGs can be tens of MB. Use `jpg` or `webp` to keep files small. Very large records may hit key-value store record size limits.
- Images behind logins, signed URLs that expire, or hosts that block data-center IPs cannot be fetched.
- No face restoration, no inpainting, no colorization, no background removal.
- Results are subjective. Always look at a sample before processing thousands of images.

### Legal and ethical notes

- You are responsible for having the right to copy and modify the images you submit. Upscaling does not change copyright.
- Image URLs and files are fetched and stored only to produce the result; stored files live in your own Apify key-value store, subject to its retention settings.
- Images containing personal data (for example people's faces) are subject to GDPR and similar laws; make sure you have a lawful basis.
- Models: Real-ESRGAN (BSD-3-Clause) by Xintao Wang et al.; ncnn inference wrapper `realesrgan-ncnn-py` (MIT). The model weights are bundled in the package.

### FAQ

**Is this real AI upscaling or just a resize?**
Real neural-network upscaling (Real-ESRGAN, CPU). The output differs from a plain Lanczos resize; it adds plausible detail rather than just interpolating.

**Why is my photo rejected as too large?**
The `general` model accepts up to 2,000,000 pixels (about 1414x1414). Downscale first or crop; larger images would be slow and costly on CPU.

**Why does a 1000x1000 image at 4x cost 2 events?**
The output is 4000x4000 = 16 MP, above the 2048x2048 threshold of a single event.

**Can it enhance faces?**
No, face enhancement is not supported.

**How do I keep files small?**
Choose `jpg` or `webp` and set `quality` to 80-90.

**Does it keep transparency?**
Yes for PNG and WebP. JPG cannot, transparent areas become white.

**What happens if the run is interrupted or my budget runs out?**
Progress is saved after every image; a restarted run skips finished images and never charges them twice. When the maximum cost per run is reached, the run stops cleanly and lists the remaining images as not processed.

**Does denoise use AI?**
No. It is a classical non-local-means filter applied before upscaling. Use small values; large values smear detail.

### Use via API

Replace `username~image-upscaler` with this Actor's ID and `YOUR_TOKEN` with your Apify API token.

```bash
curl -X POST "https://api.apify.com/v2/acts/username~image-upscaler/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"imageUrls":["https://example.com/a.jpg"],"scale":4,"outputFormat":"webp"}'
```

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('username~image-upscaler').call({
    imageUrls: ['https://example.com/a.jpg'],
    scale: 4,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items[0].upscaleUrl);
```

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("username~image-upscaler").call(run_input={
    "imageUrls": ["https://example.com/a.jpg"],
    "scale": 4,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["upscaleUrl"])
```

### Integrations

Use the Apify integrations for Make, n8n, Zapier and Google Sheets: trigger the Actor with a list of image URLs, then read `upscaleUrl` from the dataset to download the result or write it to a sheet or CMS. Webhooks can notify you when a run finishes.

### Support

Open an issue from the Actor page with the run link and the failing URL. Please do not send images you do not have rights to.

# Actor input Schema

## `imageUrls` (type: `array`):

One direct http(s) image URL per row (JPEG, PNG, WebP, GIF, BMP, TIFF; max 30 MB each). Pages that merely contain images are not crawled. Images larger than 'Max input pixels' are rejected, not charged.

## `scale` (type: `integer`):

Width and height multiplier: 2 or 4 (4 = four times width and height). Output pixels grow with the square of this number (4x of a 1 MP image is 16 MP).

## `model` (type: `string`):

general = fast Real-ESRGAN 'animevideov3' compact network for photos, screenshots and illustrations, input up to 2 MP (default). anime-hq = Real-ESRGAN x4plus-anime, 10x slower, cleaner line art, input up to 0.1 MP (about 316x316). photo-hq = Real-ESRGAN x4plus, slowest, best texture on photos, input up to 0.04 MP (about 200x200, e.g. avatars or thumbnails). For 2x the HQ models run at 4x and are scaled down, so they take the same time as 4x.

## `denoise` (type: `string`):

Number from 0 to 1. 0 = off (default). Above 0 a non-local-means noise filter is applied BEFORE upscaling (not a model-based denoise); useful for grainy or JPEG-damaged inputs, too much removes fine detail.

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

png = lossless (large files), jpg = smallest, no transparency (transparent areas become white), webp = small with transparency.

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

40-100 (lower values are raised to 40). Used for JPG and WebP only. 92 is visually near-lossless.

## `maxInputPixels` (type: `integer`):

Reject inputs with more pixels than this (width x height), before any charge. Default 1,000,000 (about 1000x1000), maximum 2,000,000. HQ models have lower built-in limits. Output above 4 MP (2048x2048 = 4,194,304 px) counts as 2 'image' events.

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

Process at most this many URLs from the list (1-500). Protects your budget from huge lists.

## `includeFailed` (type: `boolean`):

If on, images that could not be downloaded or upscaled get a free row with status 'failed' and an error. They are never charged. The reasons are always listed in the OUTPUT record.

## Actor input object example

```json
{
  "imageUrls": [
    "https://raw.githubusercontent.com/python-pillow/Pillow/main/Tests/images/hopper.png"
  ],
  "scale": 4,
  "model": "general",
  "denoise": "0",
  "outputFormat": "png",
  "quality": 92,
  "maxInputPixels": 1000000,
  "maxImages": 100,
  "includeFailed": false
}
```

# Actor output Schema

## `results` (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 = {
    "imageUrls": [
        "https://raw.githubusercontent.com/python-pillow/Pillow/main/Tests/images/hopper.png"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("tiger19935/image-upscaler").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 = { "imageUrls": ["https://raw.githubusercontent.com/python-pillow/Pillow/main/Tests/images/hopper.png"] }

# Run the Actor and wait for it to finish
run = client.actor("tiger19935/image-upscaler").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 '{
  "imageUrls": [
    "https://raw.githubusercontent.com/python-pillow/Pillow/main/Tests/images/hopper.png"
  ]
}' |
apify call tiger19935/image-upscaler --silent --output-dataset

```

## MCP server setup

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

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/Z3U9flzs8Dgomxn8s/builds/QiRbsiaSl17dskZcY/openapi.json
