# HEIC to JPG Converter - HEIF & AVIF to JPEG (`marcel-rbro/heic-to-jpg`) Actor

Convert HEIC to JPG, PNG, WebP, AVIF or TIFF in seconds. Drop in one iPhone photo or a whole folder - up to 100 files a run, returned as a single ZIP. Reads .heic, .heif and .hif from Canon and Sony. Resize while converting, strip GPS and EXIF before you share, and pull every shot out of a burst.

- **URL**: https://apify.com/marcel-rbro/heic-to-jpg.md
- **Developed by:** [Marcel Rebro](https://apify.com/marcel-rbro) (community)
- **Categories:** Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

Convert **HEIC to JPG** online, plus HEIF and AVIF images to JPEG, PNG, WebP, AVIF or TIFF. Upload one photo or a whole folder, or paste URLs, and get the converted images back. No codec pack or Photoshop plugin needed.

Every iPhone since 2017 shoots HEIC by default. It is roughly half the size of JPEG at the same quality, which is why Apple picked it, and it is still not something you can reliably paste into a form, attach to a ticket or open on a Windows machine without buying a codec. This HEIC converter is the boring conversion step in between.

- 📱 **Converts the whole HEIF family** - `.heic` (iPhone), `.heif`, `.hif` (Canon and Sony) and `.avif`
- 🖼️ **Five output formats** - JPEG, PNG, WebP, AVIF and TIFF, at a quality you choose
- 📍 **Strips GPS and EXIF on request** - one switch before you share a photo publicly
- 📐 **Resizes while it converts** - cap the long edge without a second tool
- 🎞️ **Handles multi-image HEIC files** - bursts and Live Photo stills
- 📦 **Batch converts up to 100 files in one run** - upload the folder, download one ZIP
- 💸 **Costs well under a tenth of a cent per photo**
- 🔌 **Runs from the API, a schedule or an integration** - it is an Apify Actor, so Zapier, Make, n8n and MCP clients can call it

### How to convert HEIC to JPG

1. Click **Try for free** and sign in to Apify.
2. Upload your `.heic` photos in the **HEIC files** field, or paste direct URLs to them.
3. Leave **Convert to** on `jpeg`, or pick another format.
4. Click **Start**.
5. Download the JPGs from the **Storage** tab, each under its own filename, or from the download URLs in the run's dataset.

#### How to batch convert HEIC to JPG

Select every file at once in the **HEIC files** field, or paste a list of URLs into it. Up to 100 files per run, all converted with the same settings.

Each image is stored under its own filename, and the run also writes `converted-images.zip` holding all of them - a folder of holiday photos is one download rather than two hundred clicks. Two files that arrive with the same name do not overwrite each other; the second becomes `IMG_1234-2.jpg`.

**A batch and `allImages` combine.** Turning on `allImages` fans each multi-image file out into several photos, so a batch of bursts produces two levels of results. Inside the ZIP, any source file that yielded more than one image gets a folder of its own - `burst.heic` becomes `burst/burst-0.jpg`, `burst/burst-1.jpg` and so on - while ordinary single-image files stay at the top level. Key-value store keys cannot contain a slash, so each image is still keyed flat as `burst-0.jpg`, de-duplicated the same way as any other collision.

**One bad file does not sink the batch.** A `.mov` that got mixed in with the stills, or a URL that 404s, is reported in its own dataset row with the reason while every other file converts normally. The run only fails if nothing converted at all.

Batching is also the cheaper way to run this: container startup dominates the cost of a single photo, and a batch pays it once. See [the cost table](#how-much-does-it-cost-to-convert-heic-to-jpg).

#### How to convert HEIC to PNG

Same steps, but set **Convert to** to `png`. Pick PNG when the image has transparency or when you need lossless output; the file will be considerably larger than the JPG.

#### How to convert AVIF to JPG

Upload an `.avif` file instead. The Actor reads the container to work out which codec is inside, so AVIF input needs no separate setting. AVIF to PNG, AVIF to WebP and AVIF to TIFF work the same way.

**Transparency is handled properly.** JPEG has no alpha channel, so a transparent AVIF or HEIC is flattened onto white rather than the black that a naive conversion produces. Convert to PNG, WebP, AVIF or TIFF instead and the transparency is carried through untouched.

#### How to convert HEIF to JPG

`.heif` and `.hif` files - the ones that come off Canon and Sony bodies rather than an iPhone - go through the identical path. HEIC is one kind of HEIF, so there is nothing extra to configure.

### HEIC vs HEIF vs AVIF: what is the difference?

These are not competing formats, they are a container and its fillings.

**HEIF** (High Efficiency Image File Format, ISO/IEC 23008-12) is the container. It defines how an image file is laid out - the boxes, the metadata, how multiple images and their thumbnails, depth maps and alpha channels sit together. It says nothing about how the pixels themselves are compressed.

**HEIC** is HEIF with the pixels compressed using **HEVC** (H.265). This is what an iPhone writes, and it is the reason `.heic` files are so much smaller than JPEG. Apple chose the `.heic` extension for it; the generic `.heif` extension turns up on files from other cameras and on HEIF files using a different codec.

**AVIF** is the same container idea with the pixels compressed using **AV1** instead. It is royalty-free, which HEVC is not, and it is the reason browsers adopted AVIF while largely refusing HEIC.

So HEIC is one specific kind of HEIF, the way a `.docx` is one specific kind of zip file. **This Actor handles the whole family** - it reads the container's `ftyp` box to work out which codec is inside, and hands the file to libheif, which decodes HEVC, AV1 and AVC alike. In practice that means `.heic`, `.heif`, `.hif` and `.avif` all work, and you do not have to know or care which one you have.

### Input

Upload the files in the **HEIC files** field, or pass direct URLs to them.

| Field | Description |
|---|---|
| `files` | The images to convert. A multi-file upload, or a list of URLs. Up to 100 per run. |
| `outputFormat` | `jpeg` (default), `png`, `webp`, `avif` or `tiff`. |
| `quality` | 1 to 100, default 90. Used by JPEG, WebP and AVIF; ignored by the lossless formats. |
| `maxDimension` | Shrink so neither side exceeds this many pixels. Never enlarges. |
| `keepMetadata` | `true` by default. Set to `false` to strip EXIF, XMP and IPTC. |
| `allImages` | Convert every image in a multi-image container, not just the primary one. |
| `outputFilename` | Defaults to the input filename. Ignored when converting more than one file, since they would all end up with the same name. |
| `file` | A single image as a URL. The older single-file field, kept working for existing API callers; it can be combined with `files`. |
| `fileBase64`, `filename` | The file as base64, for API callers who prefer JSON to a URL. |
| `filesBase64` | The batch equivalent: an array of `{ "filename": "photo.heic", "data": "<base64>" }` objects. |
| `verboseLog` | Include the full ImageMagick output in the Actor log. |

#### Does the JPG keep EXIF, GPS and camera data?

`keepMetadata` is on by default, because silently discarding the date a photo was taken is the more surprising behaviour. Note what that means for phone photos: the EXIF block carries the camera model, the timestamp and, if location services were on, **the GPS coordinates where the photo was taken**. Turn `keepMetadata` off before sharing images publicly.

Stripping metadata deliberately keeps the ICC colour profile. Removing that too would visibly shift the colours of wide-gamut photos, which is not what anyone means by "strip the metadata".

### Output

Every converted image is written to the run's key-value store under its own filename, so it is downloadable from the **Storage** tab.

`OUTPUT` is the record the `run-sync` API endpoint returns in its HTTP response body, and what it holds depends on how much the run converted:

| Run produced | `OUTPUT` |
|---|---|
| One image | The image itself |
| Several images | `converted-images.zip`, holding all of them |
| More than 192 MB of images | A JSON list of every key and URL - too much data to bundle into one archive |

The dataset holds **one record per source file**: source format, dimensions, file sizes and download URLs, plus an `ok` flag and an `error` message for any file that did not convert. A source that produced several images names the primary one in `outputFilename` and lists them all in `allFiles`.

### How much does it cost to convert HEIC to JPG?

| | Run time | Cost |
|---|---|---|
| One photo | 6 to 12 seconds | **$0.0003 to $0.0006** |
| A folder of 100 | one run | **roughly a cent** |

Measured on the 3.0 MB, 12 MP photo used to test this Actor, at the default 1024 MB.

**A photo costs well under a tenth of a cent**, and Apify's [free plan](https://apify.com/pricing) includes $5 of usage a month - thousands of photos.

Nearly all of that is container startup rather than conversion, which is under a second. That is the argument for putting a whole folder in one run: 100 photos as 100 runs pays the startup 100 times, as one run it is paid once.

Leave the memory at the default 1024 MB unless you convert very large images to PNG or TIFF, where the uncompressed pixel buffer is what constrains you.

### How to convert HEIC to JPG with the API

`run-sync` returns the image in the response body:

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/marcel-rbro~heic-to-jpg/run-sync?token=<TOKEN>" \
  -H 'Content-Type: application/json' \
  -d '{"file": "https://example.com/photo.heic", "outputFormat": "jpeg", "quality": 90}' \
  --output photo.jpg
```

If you hold the bytes rather than a URL, pass them as base64 in `fileBase64`. The Apify API requires Actor input to be `application/json`, so posting the image as a raw request body is rejected with HTTP 400 before the run starts:

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/marcel-rbro~heic-to-jpg/run-sync?token=<TOKEN>" \
  -H 'Content-Type: application/json' \
  -d "{\"fileBase64\": \"$(base64 < photo.heic)\", \"filename\": \"photo.heic\"}" \
  --output photo.jpg
```

Base64 adds about a third to the payload, so prefer a URL for large files.

#### How to batch convert HEIC to JPG with the API

Pass a list in `files` and the response body is a ZIP of the converted images:

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/marcel-rbro~heic-to-jpg/run-sync?token=<TOKEN>" \
  -H 'Content-Type: application/json' \
  -d '{"files": ["https://example.com/a.heic", "https://example.com/b.heic"], "outputFormat": "jpeg"}' \
  --output photos.zip
```

`filesBase64` takes the same list as base64 payloads, as an array of `{"filename": "photo.heic", "data": "<base64>"}` objects.

`run-sync` waits up to 300 seconds, which no single photo will come close to using but a hundred of them can. For large batches, start the run with [`/runs`](https://docs.apify.com/api/v2/act-runs-post) and poll it, or use the [client libraries for JavaScript and Python](https://docs.apify.com/api/client). The same run can be triggered on a schedule or from Zapier, Make, n8n, Slack or an MCP client.

### Limitations

- **One run has a budget.** 100 files, 512 MB of input in total, 100 MB for any single file, 100 MP for any single image and 4 GP of decoded pixels across the whole run - which is a folder of 100 photos at 40 MP each, comfortably past what the Actor is for. A run that would exceed one of these says which limit it hit and what number it hit it at, rather than timing out halfway. Only the file that hits a limit fails; the rest of the batch still converts. Split the batch, or use `maxDimension` to shrink while converting.
- **The ZIP stops at 192 MB of images.** The archive is assembled in memory, so past that the run skips it and `OUTPUT` becomes a JSON list of the individual records instead. Every image is still stored under its own key.
- **One setting for the whole batch.** Format, quality, resizing and metadata apply to every file in the run. Different settings mean different runs.
- **No Live Photo motion.** A Live Photo is a HEIC plus a separate MOV. This Actor converts the still half; upload the `.mov` somewhere else if you want the video. Passing the MOV here fails with an explanation rather than a delegate error, since HEIC and MOV share the same container format.
- **Depth maps and auxiliary images are dropped.** Portrait-mode depth data does not survive to JPEG, which has nowhere to put it.
- **No PDF output.** HEIC to PDF is not supported yet.
- **Format support and transparency are both asserted at build time.** Every input format the Actor reads and every one of the five it writes is checked, and the build also proves that a transparent image flattens onto white rather than black and that alpha survives a round trip through PNG, WebP, AVIF and TIFF. A Debian package that quietly loses a codec fails the build rather than every run.

### Other file converters

| Actor | Converts | Use it for |
|---|---|---|
| **HEIC to JPG converter** (this one) | HEIC, HEIF, AVIF → JPEG, PNG, WebP, AVIF, TIFF | **iPhone photos**, bursts and Live Photo stills |
| [PNG to JPG converter](https://apify.com/marcel-rbro/png-to-jpg) | PNG, JPG, JFIF, WebP, AVIF, BMP, TIFF, GIF → JPEG, PNG, WebP, AVIF, TIFF, BMP | Web and desktop image formats |
| [PDF to JPG converter](https://apify.com/marcel-rbro/pdf-to-jpg) | PDF → JPEG, PNG, WebP, TIFF | Turning pages into images |
| [JPG to PDF converter](https://apify.com/marcel-rbro/jpg-to-pdf) | JPEG, PNG, WebP, TIFF, HEIC → PDF | Turning images into a document |
| [Word to PDF converter](https://apify.com/marcel-rbro/word-to-pdf) | Word, Excel, PowerPoint, OpenDocument, RTF → PDF | Office documents |
| [MOBI to EPUB converter](https://apify.com/marcel-rbro/mobi-to-epub) | MOBI, PRC, AZW, AZW3 → EPUB | Kindle ebooks |
| [EPUB to PDF converter](https://apify.com/marcel-rbro/epub-to-pdf) | EPUB, AZW3, MOBI, FB2, CBZ → PDF | Ebooks for printing |

### FAQ

#### Is this HEIC to JPG converter free?

Apify's free plan includes $5 of platform usage per month and needs no card. At roughly $0.0005 a photo, that covers several thousand conversions before you pay anything.

#### Why can't Windows open HEIC files?

HEIC stores its pixels with HEVC, which is patent-encumbered. Microsoft ships the HEIF extension free but charges for the HEVC codec that actually decodes the image, so a stock Windows install shows you a placeholder. Converting to JPG sidesteps the whole question.

#### Does converting HEIC to JPG lose quality?

Yes, slightly - JPEG is lossy, and re-encoding an already-compressed image always costs something. At the default quality of 90 the difference is invisible at normal viewing sizes. Choose PNG or TIFF output if you need the decode to be lossless, and expect files several times larger.

#### Is the JPG bigger than the HEIC?

It depends on the quality you pick, and by less than people expect. On the 3.0 MB test photo:

| Quality | JPG size | vs HEIC |
|---|---|---|
| 80 | 2.3 MB | 23% smaller |
| 85 | 2.8 MB | 7% smaller |
| **90** (default) | 3.9 MB | 31% bigger |
| 95 | 5.1 MB | 71% bigger |
| 100 | 10.4 MB | 3.5x bigger |

HEVC does compress better than JPEG at matched quality, so you are broadly paying file size for compatibility - but the default of 90 costs about a third more, not several times more. Drop to 85 if size matters more to you than the last sliver of detail. Quality 100 is worth avoiding: it more than triples the file for no visible gain.

#### Can I convert HEIC to PDF?

Not yet. Convert to JPEG or PNG here and put the result into a PDF tool.

#### Can I batch convert a folder of HEIC files?

Yes. Select the whole folder in the **HEIC files** field, up to 100 files per run, and download `converted-images.zip` when it finishes. Every file gets the same conversion settings, and a file that fails is reported in the dataset without stopping the rest.

That is separate from `allImages`, which is for a *single* HEIC that contains several images inside it, such as a burst.

#### Can an AI agent call this converter?

Yes. Every Apify Actor is exposed through the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp), so Claude, Cursor and other MCP clients can run this conversion as a tool.

#### The conversion failed. What now?

Turn on `verboseLog` and re-run: the Actor log will carry ImageMagick's own error. The common causes are a URL that returns HTML rather than an image, a `.mov` from a Live Photo passed in place of the still, and a truncated upload.

### Development

```bash
npm install
npm test      # HEIF header inspector, filename keying, input normalization, ZIP writer and batch budgets; no ImageMagick needed
apify run     # needs ImageMagick with HEIC support and heif-info on your PATH
```

Conversion runs ImageMagick in a Debian trixie container (ImageMagick 7.1.1, libheif 1.19.8), with `heif-info` reading the image properties and `heif-convert` extracting the frames of a multi-image file. ImageMagick 7 renamed `convert` to `magick` and Debian ships both names; the Actor detects which is present at startup, so it also runs against an older local install.

Trixie rather than bookworm for a specific reason: bookworm's libheif 1.15.1 silently drops the alpha channel across the whole HEIF family, returning a black image where a transparent HEIC or AVIF should flatten onto white - with exit code 0 and every format check passing. The Dockerfile asserts the pixels, not the format table, so that class of regression fails the build.

This Actor's code is MIT licensed. ImageMagick and libheif run as separate processes or shared libraries under their own licences - see [THIRD\_PARTY\_NOTICES.md](THIRD_PARTY_NOTICES.md).

# Actor input Schema

## `files` (type: `array`):

The images to convert. Upload one file or a whole folder of them, or paste direct URLs. Accepts .heic, .heif, .hif and .avif, up to 100 files per run. Every file is converted with the same settings below.

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

JPEG opens everywhere and is the right answer for photos. Choose PNG if the image has transparency or you need a lossless copy.

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

Compression quality from 1 to 100, used by JPEG, WebP and AVIF. 90 is visually indistinguishable from the original for most photos. Ignored for PNG and TIFF, which are lossless.

## `maxDimension` (type: `integer`):

Shrink the image so neither side exceeds this many pixels, keeping the aspect ratio. Leave empty to keep the original size. Images smaller than this are never enlarged.

## `keepMetadata` (type: `boolean`):

Keeps the EXIF data, which includes the camera, the date the photo was taken and, on phone photos, the GPS location. Turn this off to strip it before sharing the file. The colour profile is kept either way.

## `allImages` (type: `boolean`):

Some HEIC files hold more than one image, such as a burst. Converts all of them instead of only the primary image. Has no effect on ordinary single-image photos.

## `outputFilename` (type: `string`):

Name of the resulting file, without the extension. Defaults to the input filename. Ignored when converting more than one file, since they would all end up with the same name.

## `file` (type: `string`):

One file as a URL. Kept for API callers written against the single-file input; "files" does the same thing and takes more than one. If both are given, both are converted.

## `fileBase64` (type: `string`):

Alternative to the file field for API callers: the source file encoded as base64. Adds about 33% to the payload size.

## `filename` (type: `string`):

Original filename to use when the file is passed as base64.

## `filesBase64` (type: `array`):

The batch equivalent of "fileBase64": an array of { "filename": "photo.heic", "data": "<base64>" } objects, or of bare base64 strings. Prefer URLs for anything large.

## `verboseLog` (type: `boolean`):

Includes the full ImageMagick output in the Actor log. Useful when a conversion produces odd output.

## Actor input object example

```json
{
  "outputFormat": "jpeg",
  "quality": 90,
  "keepMetadata": true,
  "allImages": false,
  "verboseLog": false
}
```

# Actor output Schema

## `convertedImage` (type: `string`):

One converted image, or a ZIP of all of them when the run converted more than one. This is the same record that the run-sync API endpoint returns in its HTTP response body.

## `allFiles` (type: `string`):

Every record in the run's key-value store, including each converted image under its own filename.

## `conversionDetails` (type: `string`):

One record per source file: source format, codec, dimensions, file sizes, download URLs, and the reason for any file that failed.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("marcel-rbro/heic-to-jpg").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("marcel-rbro/heic-to-jpg").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 '{}' |
apify call marcel-rbro/heic-to-jpg --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,marcel-rbro/heic-to-jpg"
        }
    }
}

```

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/mb3NEQNA9bJlt3L2V/builds/3P50A6PEj04LwMbRf/openapi.json
