# Change image aspect ratio (`akash9078/frame-image-converter`) Actor

Transform any image to your desired aspect ratio (square, widescreen, portrait, custom) with intelligent background filling and quality preservation. Supports multiple image formats and batch processing.

- **URL**: https://apify.com/akash9078/frame-image-converter.md
- **Developed by:** [Akash Kumar Naik](https://apify.com/akash9078) (community)
- **Categories:** Developer tools, Automation, Other
- **Stats:** 12 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $0.00005 / actor start

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Frame Image Converter — Smart Aspect Ratio Converter for Any Dimension

**Transform any image to your desired aspect ratio instantly** with professional-grade image processing. Convert photos, graphics, and artwork to perfect dimensions while maintaining quality and visual appeal — built as a production-ready [Apify Actor](https://apify.com/actors) with pay-per-event billing.

***

### 💰 Pricing — Pay Per Event

This actor uses Apify's **Pay Per Event** model. You are only charged for results you actually receive — no charge for failed images.

| Event | Price | When |
|-------|-------|------|
| `image-processed-success` | **$0.005** per image | Charged once per image successfully downloaded, converted, and stored |

> **Example:** Processing 100 images costs **$0.50**. Failed images are never charged.

This model is transparent, predictable, and fair — you pay only for value delivered. Learn more about [Apify Pay Per Event](https://docs.apify.com/platform/actors/publishing/monetize/pay-per-event).

***

### ✨ Key Features

- **Any Aspect Ratio** — Square (1:1), widescreen (16:9), portrait (4:5), vertical (9:16), ultrawide (21:9), or any custom ratio
- **Smart Background Fill** — White, black, transparent, blurred original, or custom hex color
- **Three Resize Modes** — Fit (pad), Fill (crop), Stretch
- **Batch Processing** — Up to 100 images per run with async concurrency control
- **Output Formats** — PNG, JPEG, WebP with quality control
- **Cloud Storage Support** — Google Drive, Dropbox, OneDrive, Box, GitHub, GitLab
- **Retry Logic** — Configurable exponential backoff for resilient downloads
- **Memory Safe** — Streaming downloads with configurable size limits (128–256 MB)

***

### 🚀 Quick Start

1. Go to the actor page on Apify and click **Try for free**
2. Paste your image URLs into **Image URLs**
3. Choose your **Aspect Ratio**, **Background**, and **Output Format**
4. Click **Start** — results appear in the **Dataset** tab

***

### ⚙️ Input Configuration

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `imageUrls` | array | — | **Required.** List of image URLs (max 100). Supports direct links and cloud storage share URLs. |
| `aspectRatio` | string | `1:1` | Target aspect ratio preset. Options: `1:1`, `4:3`, `3:4`, `16:9`, `9:16`, `3:2`, `2:3`, `4:5`, `5:4`, `21:9`, `custom` |
| `customAspectRatio` | string | `7:3` | Custom ratio in `W:H` format. Only used when `aspectRatio` is `custom`. |
| `outputSize` | integer | `1080` | Reference dimension in pixels (100–8000). Width for landscape/square; height for portrait. |
| `backgroundColor` | string | `white` | Background fill: `white`, `black`, `transparent`, `blur`, `custom` |
| `customColor` | string | `#FFFFFF` | Hex color for background when `backgroundColor` is `custom`. |
| `resizeMode` | string | `fit` | `fit` — pad with background · `fill` — crop to fill · `stretch` — distort to fill |
| `outputFormat` | string | `PNG` | Output image format: `PNG`, `JPEG`, `WEBP` |
| `jpegQuality` | integer | `90` | Compression quality for JPEG/WEBP (1–100) |
| `maxConcurrency` | integer | `5` | Max simultaneous downloads/processing (1–20) |
| `downloadTimeoutSecs` | integer | `60` | Per-image download timeout in seconds (5–300) |
| `maxImageSizeMb` | integer | `20` | Max input image size in MB (1–100). Larger images are skipped. |
| `retryPolicy` | object | `{maxRetries:3, backoffMultiplier:2.0, initialDelay:1.0}` | Retry configuration for failed downloads |
| `proxySettings` | object | — | Optional Apify proxy configuration |

***

### 📊 Output — Dataset Fields

Each processed image produces one dataset record:

| Field | Type | Description |
|-------|------|-------------|
| `index` | integer | Zero-based position in the input list |
| `originalUrl` | string | Source image URL |
| `processedUrl` | string | Public URL of the converted image in key-value store |
| `filename` | string | Generated filename for the output image |
| `processedKey` | string | Key-value store key for the processed image |
| `success` | boolean | `true` if processed successfully |
| `aspectRatio` | string | Applied aspect ratio (e.g. `16:9`) |
| `originalDimensions` | object | `{width, height}` of the source image |
| `finalDimensions` | object | `{width, height}` of the output image |
| `originalFormat` | string | Detected source format (JPEG, PNG, etc.) |
| `outputFormat` | string | Output format used |
| `originalSize` | integer | Source image size in bytes |
| `processedSize` | integer | Output image size in bytes |
| `compressionRatio` | number | `processedSize / originalSize` |
| `processingTime` | number | Image processing time in seconds (excl. download) |
| `totalProcessingTime` | number | Total time including download in seconds |
| `backgroundType` | string | Background type applied |
| `resizeMode` | string | Resize mode used |
| `imageHash` | string | Perceptual average hash for deduplication |
| `error` | string | Error message (only present when `success` is `false`) |
| `timestamp` | string | ISO 8601 UTC timestamp |

***

### 🎨 Aspect Ratio Presets

| Ratio | Use Case |
|-------|----------|
| `1:1` | Instagram feed, profile photos, product thumbnails |
| `4:3` | Presentations, classic TV, standard photography |
| `3:4` | Portrait standard |
| `16:9` | YouTube thumbnails, HD video, website banners |
| `9:16` | Instagram Reels, TikTok, Stories |
| `3:2` | DSLR / 35mm film photography |
| `2:3` | Portrait photography |
| `4:5` | Instagram portrait posts |
| `5:4` | Large format photography |
| `21:9` | Ultrawide cinematic banners |
| `custom` | Any ratio you define (e.g. `7:3`, `2.35:1`) |

***

### 🌐 Supported Cloud Storage

| Platform | Supported URL Format |
|----------|---------------------|
| Google Drive | `/file/d/FILE_ID/view` share links |
| Dropbox | `dropbox.com/s/…` share links |
| OneDrive | `onedrive.live.com` and `1drv.ms` links |
| Box | `box.com/s/…` share links |
| GitHub | `/blob/` URLs (auto-converted to raw) |
| GitLab | `/blob/` URLs (auto-converted to raw) |
| Direct URLs | Any publicly accessible image URL |

***

### 🔧 Background Modes

| Mode | Description |
|------|-------------|
| `white` | Clean white background (default) |
| `black` | Solid black background |
| `transparent` | Transparent fill — PNG output only |
| `blur` | Blurred version of the original image as background (artistic effect) |
| `custom` | Any hex color (e.g. `#FF5733`) |

***

### 📐 Resize Modes

| Mode | Behaviour |
|------|-----------|
| `fit` | Scales image to fit within the target frame; pads remaining space with background |
| `fill` | Crops the image to fill the entire target frame (center crop) |
| `stretch` | Stretches/distorts the image to exactly fill the target dimensions |

***

### 🏗️ Technical Architecture

- **Runtime**: Python 3.12 on `apify/actor-python:3.12`
- **Image Engine**: Pillow (PIL) with LANCZOS resampling for maximum quality
- **HTTP Client**: aiohttp with streaming downloads, DNS caching, and connection pooling
- **Concurrency**: asyncio + asyncio-throttle for controlled parallel processing
- **Retry**: Custom exponential backoff (configurable retries, multiplier, initial delay)
- **Memory**: Streaming chunk-based download with configurable size guard
- **Billing**: Apify Pay Per Event — `Actor.charge()` called only on successful image processing
- **Memory Limits**: 128 MB minimum, 256 MB maximum

***

### 📁 Project Structure

```
frame-image-converter/
├── .actor/
│   ├── actor.json          # Actor definition (memory limits, schema refs)
│   ├── input_schema.json   # Input schema with defaults & prefills
│   ├── dataset_schema.json # Dataset view & column definitions
│   ├── output_schema.json  # Output record schema
│   └── pay_per_event.json  # PPE event definitions
├── src/
│   └── main.py             # Actor entry point & processing logic
├── Dockerfile              # Container definition
├── requirements.txt        # Python dependencies
└── README.md
```

***

### ❓ FAQ

**Why Pay Per Event instead of compute-based pricing?**
PPE is fairer and more predictable. You pay $0.005 per successfully converted image — no charge for failures, no surprise bills from idle compute time.

**What happens if an image fails to download?**
The actor retries up to 3 times (configurable) with exponential backoff. If all retries fail, the failure is recorded in the dataset with an error message and **no charge is applied**.

**Can I process images from private cloud storage?**
Cloud storage URLs must be publicly accessible share links. Private files require you to generate a public share link first.

**What is the maximum image size?**
Default is 20 MB per image (configurable up to 100 MB). Images exceeding the limit are skipped with an error record.

**Does transparent background work with JPEG?**
No — JPEG does not support transparency. Use PNG or WEBP output format when selecting the transparent background option.

**How do I get the processed images?**
Each dataset record contains a `processedUrl` field — a direct public link to the converted image stored in the actor's key-value store.

# Actor input Schema

## `imageUrl` (type: `string`):

URL of the image to process. Supports direct image links and cloud storage URLs (Google Drive, Dropbox, OneDrive, GitHub).

## `aspectRatio` (type: `string`):

Target aspect ratio for the output image.

## `backgroundColor` (type: `string`):

Background fill used when the image doesn't cover the full canvas (fit mode).

## `resizeMode` (type: `string`):

How the image is resized to fit the target aspect ratio.

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

Image format for the processed output.

## Actor input object example

```json
{
  "imageUrl": "https://drive.google.com/file/d/1aifNMWYUMHTrrWNwWK7gKBOLr9cRhnH8/view?usp=drive_link",
  "aspectRatio": "1:1",
  "backgroundColor": "white",
  "resizeMode": "fit",
  "outputFormat": "PNG"
}
```

# Actor output Schema

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

Dataset containing metadata and processing statistics for each converted image (index, dimensions, format, size, timing, etc.).

## `processedImages` (type: `string`):

Key-value store containing the converted image files in the requested output format (PNG, JPEG, or WEBP).

# 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 = {
    "imageUrl": "https://drive.google.com/file/d/1aifNMWYUMHTrrWNwWK7gKBOLr9cRhnH8/view?usp=drive_link",
    "aspectRatio": "1:1",
    "backgroundColor": "white",
    "resizeMode": "fit",
    "outputFormat": "PNG"
};

// Run the Actor and wait for it to finish
const run = await client.actor("akash9078/frame-image-converter").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 = {
    "imageUrl": "https://drive.google.com/file/d/1aifNMWYUMHTrrWNwWK7gKBOLr9cRhnH8/view?usp=drive_link",
    "aspectRatio": "1:1",
    "backgroundColor": "white",
    "resizeMode": "fit",
    "outputFormat": "PNG",
}

# Run the Actor and wait for it to finish
run = client.actor("akash9078/frame-image-converter").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 '{
  "imageUrl": "https://drive.google.com/file/d/1aifNMWYUMHTrrWNwWK7gKBOLr9cRhnH8/view?usp=drive_link",
  "aspectRatio": "1:1",
  "backgroundColor": "white",
  "resizeMode": "fit",
  "outputFormat": "PNG"
}' |
apify call akash9078/frame-image-converter --silent --output-dataset

```

## MCP server setup

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

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/LW8qxOhvyvnWKgneJ/builds/8piU0NgMPrO6wNReC/openapi.json
