HEIC to JPG
An iPhone saves photos as HEIC by default, and the moment one of those files lands on a Windows machine, an old Android device, or a web upload form, it often just fails to open. This endpoint takes that HEIC file and returns a standard JPG, so the photo works everywhere without anyone changing a phone setting.
Run it online
Run this on our servers with your account. Free tools run in your browser; this one bills your KIT balance per the price above.
Why this keeps happening
Apple adopted HEIC (High Efficiency Image Container) as the default photo format starting with iOS 11, because it compresses roughly twice as efficiently as JPG at similar visual quality, which matters a lot when a phone is holding tens of thousands of photos. The tradeoff is compatibility: plenty of software outside Apple's ecosystem, from older browsers to certain print labs and e-commerce platforms, simply doesn't decode HEIC, and users are left staring at a broken thumbnail.
What the endpoint actually does
You send the HEIC file to /image/heic-to-jpg and it comes back as a JPG with the visual content intact, ready for anything that expects a conventional photo format. The call returns a task_id immediately; the decode-and-encode work happens asynchronously, and you're notified when it's done through a signed webhook or by pulling the file from a signed link that's valid for 24 hours.
Where the friction usually shows up
This conversion matters most at the exact points where a photo crosses ecosystems: a customer uploading a return photo to an online store, a real estate agent submitting listing images to an MLS system, an HR platform accepting ID photos, or a support ticket with an attached screenshot that nobody on the receiving end can open. In each case, the fix isn't asking users to change how their phone saves photos — it's converting on arrival.
How it fits into a pipeline
Because the request and response are plain JSON around a task_id, it slots directly into an upload handler: accept whatever the phone sends, check if it's HEIC, convert if needed, store the JPG. Failed conversions retry automatically up to three times before returning a clear error, and you're never billed for a task that doesn't complete, so a bad or corrupted upload doesn't quietly drain your balance.
Who this is for
It's the small but constant fix that photo-upload products, support desks, print services and marketplaces all eventually need, because a meaningful share of their users are on iPhones with HEIC turned on by default.
What you can do with it
Marketplace listing uploads
A resale marketplace accepts product photos straight from sellers' iPhones and converts any HEIC file to JPG before it reaches the listing page.
Support ticket attachments
A helpdesk tool converts HEIC screenshots and photos from mobile users so support agents can open them without extra software.
Print-on-demand orders
A photo printing service normalizes every uploaded HEIC file to JPG before sending it into its print production queue.
Real estate listing photos
An agent uploads HEIC photos taken on-site with an iPhone, and the MLS-facing system converts them to JPG automatically for compatibility.
FAQ
What does the HEIC to JPG API actually convert?
It takes an iPhone-generated HEIC image and returns a standard JPG with the same visual content, ready for software that doesn't support HEIC.
Is converting HEIC to JPG free?
There's no free tier — free tiers get abused and slow everyone down. Access runs on a prepaid ForHosting KIT balance: top up from $10.00 (it never expires) and each request is charged at its published price, so a call with no balance returns HTTP 402. No subscription, no tokens, no invented credits, and a failed task is never charged.
What's the price per image?
$0.002 per request plus $0.005 per image, and you're only charged for conversions that succeed.
Why won't my iPhone photo open on other devices?
iPhones save photos as HEIC by default since iOS 11, and many non-Apple apps and older browsers can't decode that format without conversion.
Does converting to JPG lose quality?
JPG is a lossy format like HEIC, so there's some re-encoding, but at standard quality settings the difference is not visually noticeable for everyday photos.
Is the conversion instant?
It's asynchronous: you get a task_id right away and the JPG arrives via signed webhook or a signed link valid for 24 hours.
Can I convert HEIC images in bulk?
You submit one image per request, but you can send many requests concurrently or automate it in a batch script; the queue handles the volume.
What happens if the HEIC file is corrupted?
The task retries automatically up to three times, and if it still fails you get a clear error with no charge for that task.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, and soon from our app, email and Telegram.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/image/heic-to-jpg \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"image":"https://ejemplo.com/imagen.jpg"}'const res = await fetch("https://api.kit.forhosting.com/image/heic-to-jpg", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"image": "https://ejemplo.com/imagen.jpg"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/image/heic-to-jpg",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"image": "https://ejemplo.com/imagen.jpg"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/image/heic-to-jpg", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"image":"https://ejemplo.com/imagen.jpg"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"image":"https://ejemplo.com/imagen.jpg"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/image/heic-to-jpg", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"image": "https://ejemplo.com/imagen.jpg"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "image.heic_to_jpg",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Limits
max_mb | 15 |
max_megapixels | 12 |
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |