EXR to TGA API
POST an .exr to /v2/convert/exr/to/tga and get a type 2 Targa: uncompressed true color with 8 bits each for red, green, blue and alpha, stored from the top-left corner.
- Endpoint
- POST /v2/
convert/ exr/ to/ tga - Accepts
- exr
- Sample timing
- 23 ms median of 5 runsHow we measure
- Request size
- Up to 10 MB on the Free plan or 100 MB on paid plans, all files together
Request and response
Every sample below ran against the API before this page was published. Set PDFCONVERSIONAPI_KEY to your key and run it as is.
curl -sS -X POST \
https://api.pdfconversionapi.com/v2/convert/exr/to/tga \
-H "Authorization: Bearer $PDFCONVERSIONAPI_KEY" \
-F File=@glowing-bulb-render.exr \
-o response.json
# FileData is base64. Save the first file in converted/,
# or print the JSON error.
if jq -e '.Files' response.json > /dev/null; then
mkdir -p converted
name=$(jq -r '.Files[0].FileName' response.json)
jq -r '.Files[0].FileData' response.json \
| base64 --decode > "converted/$name"
else
cat response.json
fiimport base64
import os
import requests
key = os.environ["PDFCONVERSIONAPI_KEY"]
response = requests.post(
"https://api.pdfconversionapi.com/v2/convert/exr/to/tga",
headers={"Authorization": f"Bearer {key}"},
files=[("File", open("glowing-bulb-render.exr", "rb"))],
timeout=180,
)
result = response.json()
if not response.ok:
raise SystemExit(f"{result['Code']}: {result['Message']}")
os.makedirs("converted", exist_ok=True)
for file in result["Files"]:
path = os.path.join("converted", file["FileName"])
with open(path, "wb") as out:
out.write(base64.b64decode(file["FileData"]))
print(path, file["FileSize"], "bytes")
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { join } from "node:path";
const form = new FormData();
const file = "glowing-bulb-render.exr";
form.append("File", new Blob([await readFile(file)]), file);
const url = "https://api.pdfconversionapi.com/v2/convert/exr/to/tga";
const key = process.env.PDFCONVERSIONAPI_KEY;
const response = await fetch(url, {
method: "POST",
headers: { Authorization: `Bearer ${key}` },
body: form,
});
const result = await response.json();
if (!response.ok) {
throw new Error(`${result.Code}: ${result.Message}`);
}
await mkdir("converted", { recursive: true });
for (const file of result.Files) {
const path = join("converted", file.FileName);
await writeFile(path, Buffer.from(file.FileData, "base64"));
console.log(path, file.FileSize, "bytes");
}
{
"ConversionCost": 1,
"ConversionTime": 0.023,
"Files": [
{
"FileName": "glowing-bulb-render.tga",
"FileExt": "tga",
"FileSize": 288018,
"FileData": "AAACAAAAAAAAAAAA8AAsASAo…"
}
]
}Response from our test run on 2026-11-03. ConversionTime shows the median of 5 runs. FileData is shortened here; the real response carries the whole file as base64.
The sample, converted
The same file the code sends. Download the input and the output and compare them yourself.

- Input
- glowing-bulb-render.exr (187 KB)
- Output
- glowing-bulb-render.tga (281 KB)
- Source
- A glowing light bulb we drew in code and saved as a 240 × 300 OpenEXR render: half-float RGBA, premultiplied alpha, ZIP compression, values up to 12.0. (CC0-1.0). How it was made
- Made by
- 2026-11-04, API build
9eeeb0640aba
Parameters
Send these as form fields next to the file. Names are case-insensitive. If you set one of the documented options below that we don't support yet, the request fails with a 400 that names it instead of returning a file made without it.
| Parameter | What it does |
|---|---|
ImageWidthinteger | Target width in pixels; ImageWidth=120 gave a 120 × 150 Targa of 72,018 bytes.Values: 1–20000 |
ImageHeightinteger | Target height; ImageHeight=100 came back 80 × 100. With both, the render is fitted inside them.Values: 1–20000 |
ScaleProportionsboolean, default true | false stretches to the requested box: 200 × 200 gave 160,018 bytes. |
ScaleIfLargerboolean, default false | true keeps 240 × 300 when a bigger size is requested; ImageWidth=800 by itself wrote 3,200,018 bytes. |
ScaleImageboolean, default true | false cuts the requested size from the top-left corner instead of scaling. |
ColorSpaceenum, default default | Still a 32-bit Targa with the render's alpha, holding gray values: gray and monochrome are both 288,018 bytes, as nothing is compressed, and monochrome leaves only 0 and 255 in the color bytes.Values: default, rgb, gray, monochrome; cmyk returns 400 |
TransparentColorstring | Sets alpha to 0 for one exact color. #FFFFFF cleared the 2,232 pixels in the bulb's core that are white at 8 bits.Values: #RRGGBB, R,G,B or a color name |
FileNamestring | Name for the result, glowing-bulb-render.tga by default. |
StoreFileboolean, default false | Returns Files[].Url, valid for 3 hours. |
What carries over, and what doesn't
Kept
- Straight 8-bit alpha next to each color: the Targa equals the BMP route's pixels on every value
- Row order from the top-left, flagged in the image descriptor (0x28: top-left origin, 8 alpha bits)
- A plain layout: an 18-byte header, then 4 bytes per pixel with no packets to decode
Lost or not yet supported
- Compactness. Nothing is compressed, so the Targa is 1.5 times the 191,302-byte EXR.
- The 16-bit half-float depth.
- Values brighter than 1.0, which become 255.
- Correct soft edges, since colors keep their premultiplied values.
Limits
- One EXR per conversion.
- Request size: 10 MB on the Free plan or 100 MB on paid plans.
- 413 for renders over 100,000,000 pixels.
- Output sizes from 1 to 20,000 px per side.
- A Targa side can't exceed 65,535 px: a 70,000 × 16 EXR returned 413
TGA images can be at most 65,535 pixels on a side, and this one is 70,000×16. - 120 seconds of work per request.
Where teams use it
Textures for game engines
Some engine import pipelines and modding tools take TGA with alpha but not EXR. Converting rendered sprites, decals or UI elements gives them a file those tools load.
Sprite sheets from rendered frames
Scripts that pack rendered frames into sprite sheets can read Targa with a few lines of code. Each frame converts in its own request.
Questions
Is the Targa compressed?
No. Every file from this route is image type 2, uncompressed true color, and there is no option for run-length type 10. The 240 × 300 sample is 288,018 bytes: an 18-byte header and 4 bytes per pixel.
Does the file end with a Targa 2.0 footer?
No. The file ends with the last pixel: there is no extension area, developer area or TRUEVISION-XFILE footer, so readers treat it as an original Targa. The image descriptor, 0x28, gives the top-left origin and 8 alpha bits.
Is the alpha premultiplied in the TGA?
Targa alpha is read as straight, and the API copies the EXR's colors unchanged, which were premultiplied in the render. Opaque pixels are exact; partly clear ones look darker than intended.
Convert your first EXR file in a minute.