TGA to GIF API
POST a TGA to /v2/convert/tga/to/gif and get a single-frame GIF89a back. When the texture has 256 colors or fewer the palette is exact; fully transparent pixels stay transparent, and every other pixel becomes opaque.
- Endpoint
- POST /v2/
convert/ tga/ to/ gif - Accepts
- tga
- Sample timing
- 2 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/tga/to/gif \
-H "Authorization: Bearer $PDFCONVERSIONAPI_KEY" \
-F File=@shield-icon.tga \
-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/tga/to/gif",
headers={"Authorization": f"Bearer {key}"},
files=[("File", open("shield-icon.tga", "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 = "shield-icon.tga";
form.append("File", new Blob([await readFile(file)]), file);
const url = "https://api.pdfconversionapi.com/v2/convert/tga/to/gif";
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.002,
"Files": [
{
"FileName": "shield-icon.gif",
"FileExt": "gif",
"FileSize": 3416,
"FileData": "R0lGODlhAAEAAYAAAAAAAAAA…"
}
]
}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
- shield-icon.tga (16 KB)
- Output
- shield-icon.gif (3.3 KB)
- Source
- A 256 x 256 pixel-art shield icon with a transparent background and a half-transparent shadow, drawn by us and saved as a 32-bit run-length-encoded TGA. (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 |
|---|---|
TransparentColorstring | Adds a key color: pixels exactly this color join the transparent ones. With #966034 the main wood color of the shield became see-through (3,360 bytes).Values: #RRGGBB, R,G,B or a basic color name |
ImageWidthinteger | Resizes before the palette is built. Resampling invents in-between colors, so the 128 px shield needed a larger palette and came to 4,650 bytes, more than at full size.Values: 1–20000 |
ImageHeightinteger | Height in pixels; with a width it defines a box to fit into.Values: 1–20000 |
ScaleProportionsboolean, default true | false allows a distorted resize to the exact width and height. |
ScaleIfLargerboolean, default false | true means a smaller sprite is never enlarged. |
ScaleImageboolean, default true | false crops from the top-left instead of scaling. |
ColorSpaceenum, default default | monochrome gives a black-and-white GIF (2,081 bytes for the shield); gray maps the colors to gray levels first.Values: default, rgb, gray, monochrome; cmyk returns 400 |
FileNamestring | Base name of the GIF; the TGA's name is used otherwise. |
StoreFileboolean, default false | Return a 3-hour download link instead of base64 data. |
What carries over, and what doesn't
Kept
- Exact colors when the TGA has 256 or fewer: the shield's 12 RGBA values became a 16-entry palette with 12 colors in use, and all 27,344 opaque pixels match
- Fully transparent pixels: the 34,528 empty pixels around the sample stay transparent, through palette entry 0
- Size: 256 × 256 in, 256 × 256 out, as a 3,416-byte file
Lost or not yet supported
- Partial transparency. GIF has one transparent index, so any alpha above 0 becomes fully opaque: the shield's soft shadow (alpha 112) is now a solid near-black ellipse.
- Colors beyond 256. Art with more colors is reduced to a 256-color palette; the shield scaled to 1,000 px had 13,503 distinct values before that step.
- Animation: one TGA makes one frame, with no delay or loop settings.
- Resolution data, since GIF has no DPI field (
ImageResolutionis ignored).
Limits
- One TGA per request, one frame out.
- Resize targets from 1 to 20,000 px per side, with the resized image held to 100,000,000 pixels:
ImageWidth=20000on the square shield returned 413, while 10,000 worked. - 100,000,000 pixels per image, checked from the header.
- Requests up to 10 MB on the Free plan or 100 MB on paid plans; 120 seconds per conversion.
Where teams use it
Pixel-art sprites for forums and wikis
Browsers can't show TGA, so sites that display uploads as they are need a web format. Sprites drawn with a small palette convert to GIF with no color change, and their empty background stays transparent.
Retro and embedded displays
Devices and engines that read indexed GIF can take sprites authored as TGA. Keep the source at 256 colors or fewer and the GIF is exact.
Questions
Why did my sprite's shadow turn black?
GIF transparency is on or off per pixel. Any pixel whose alpha is above 0 is made fully opaque and keeps its color, so a dark shadow at alpha 112 becomes a solid dark shape. Bake the shadow onto a background color first, or remove it, before converting.
Will the colors change?
Not if the texture has 256 colors or fewer, and partial alpha does not add to that count: every pixel with any opacity counts by its color alone, and all fully clear pixels count as one. A test TGA with 200 colors, each at alpha 255 and at alpha 100, kept all 200 exactly. Above 256, the encoder picks a 256-color palette and similar shades get merged.
Can I combine several TGA frames into an animated GIF?
Not with this route. It takes one file and writes one frame. Convert each frame here and assemble the animation in your own code.
Convert your first TGA file in a minute.