API de Compresión de Imágenes#
Comprime imágenes PNG, JPEG y WebP con el endpoint POST /v1/convert/compress-image — el formato nunca cambia (entra PNG, sale PNG). La compresión es primero sin pérdida: se eliminan los metadatos y la imagen se recodifica con los ajustes sin pérdida más fuertes para su formato, por lo que el resultado nunca es más grande que la entrada. Pasa target_size_kb para fijar un presupuesto de tamaño de archivo; si la optimización sin pérdida por sí sola no lo alcanza, la imagen se reduce de escala con la relación de aspecto bloqueada hasta que quepa.
Endpoint#
POST /v1/convert/compress-image
Content-Type: multipart/form-data
Entrada aceptada: archivos .png, .jpg, .jpeg, .webp
Formato de salida: el mismo que la entrada (image/png, image/jpeg o image/webp)
Autenticación#
Requiere una clave de API privada o un token JWT de una clave pública.
X-API-Key: sk_live_your_private_key
O:
Authorization: Bearer <jwt_token>
Parámetros de la solicitud#
| Parámetro | Tipo | Obligatorio | Predeterminado | Descripción |
|---|---|---|---|---|
file |
file | Sí | -- | El archivo de imagen .png, .jpg, .jpeg o .webp a comprimir. |
target_size_kb |
integer |
No | -- | Tamaño de archivo objetivo en kilobytes. Omitido: solo optimización sin pérdida. Establecido: la imagen además se reduce de escala (relación de aspecto bloqueada) hasta que quepa en el objetivo. |
output_filename |
string |
No | Nombre del archivo de entrada | Nombre de archivo de salida personalizado. La extensión del archivo de entrada se conserva. |
direct_download |
boolean |
No | true |
Cuando es true, devuelve los bytes de la imagen sin procesar. Cuando es false, devuelve metadatos JSON con una URL de descarga prefirmada. |
Detalles de la conversión#
Etapa 1 — sin pérdida (siempre se ejecuta):
- Los metadatos (EXIF, XMP, fragmentos de texto PNG) se eliminan; el perfil de color ICC y el indicador de orientación EXIF se conservan para que los colores y la rotación no cambien
- PNG: se recodifica con la máxima compresión zlib, más una conversión a paleta cuando la imagen tiene 256 colores únicos o menos y el resultado es demostrablemente idéntico píxel a píxel
- JPEG: se recodifica reutilizando las tablas de cuantización originales con codificación Huffman progresiva optimizada — no se introduce pérdida de cuantización adicional
- WebP: recodificación sin pérdida real (VP8L) con el máximo esfuerzo
- Gana el más pequeño entre los bytes originales y todos los candidatos — la salida nunca es más grande que la entrada
Etapa 2 — reducción de dimensiones (solo con target_size_kb, y solo si la etapa 1 no lo alcanza):
- La imagen se reduce de escala con la relación de aspecto bloqueada (remuestreo LANCZOS) y se recodifica, buscando de forma binaria el factor de escala con las mayores dimensiones que quepan en el objetivo
- Los JPEG reducidos y los WebP de origen con pérdida se recodifican a calidad 85; los PNG y los WebP de origen sin pérdida permanecen sin pérdida en el tamaño reducido
file_size (o el encabezado X-File-Size) para ver qué se alcanzó.
Respuesta#
Descarga directa (direct_download=true, predeterminado)#
HTTP 200 OK
Content-Type: image/png
Content-Disposition: inline; filename="photo_20260717_123456789.png"
Devuelve los bytes de la imagen sin procesar en el mismo formato que la entrada.
Respuesta con metadatos (direct_download=false)#
{
"presigned_url": "https://spaces.example.com/...",
"object_key": "env/files/{project_id}/compress-image/photo_20260717_123456789.png",
"filename": "photo_20260717_123456789.png",
"file_size": 45678,
"conversion_time_seconds": 0.5
}
Ejemplos de código#
Python#
import requests
with open("photo.png", "rb") as f:
response = requests.post(
"https://api.enconvert.com/v1/convert/compress-image",
headers={"X-API-Key": "sk_live_your_private_key"},
files={"file": ("photo.png", f)},
data={"target_size_kb": 200}
)
with open("photo_compressed.png", "wb") as out:
out.write(response.content)
Node.js#
const form = new FormData();
form.append("file", fs.createReadStream("photo.png"));
form.append("target_size_kb", "200");
const response = await fetch("https://api.enconvert.com/v1/convert/compress-image", {
method: "POST",
headers: { "X-API-Key": "sk_live_your_private_key" },
body: form
});
fs.writeFileSync("photo_compressed.png", Buffer.from(await response.arrayBuffer()));
PHP#
$ch = curl_init("https://api.enconvert.com/v1/convert/compress-image");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-Key: sk_live_your_private_key"],
CURLOPT_POSTFIELDS => [
"file" => new CURLFile("photo.png"),
"target_size_kb" => 200
]
]);
$output = curl_exec($ch);
curl_close($ch);
file_put_contents("photo_compressed.png", $output);
Go#
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
part, _ := writer.CreateFormFile("file", "photo.png")
file, _ := os.Open("photo.png")
io.Copy(part, file)
writer.WriteField("target_size_kb", "200")
writer.Close()
req, _ := http.NewRequest("POST", "https://api.enconvert.com/v1/convert/compress-image", body)
req.Header.Set("Content-Type", writer.FormDataContentType())
req.Header.Set("X-API-Key", "sk_live_your_private_key")
resp, _ := http.DefaultClient.Do(req)
Respuestas de error#
| Estado | Condición |
|---|---|
400 Bad Request |
El archivo no es un archivo .png, .jpg, .jpeg o .webp |
400 Bad Request |
El contenido del archivo no coincide con su extensión (el endpoint nunca cambia de formato) |
400 Bad Request |
Imagen animada (APNG / WebP animado) — no compatible |
400 Bad Request |
target_size_kb es cero o negativo |
422 Unprocessable Entity |
target_size_kb está presente pero no es un número entero |
400 Bad Request |
La conversión de la imagen falló (archivo corrupto o no compatible) |
401 Unauthorized |
Falta la clave de API/token JWT o no es válida |
402 Payment Required |
Se alcanzó el límite mensual de conversiones |
402 Payment Required |
Se alcanzó el límite de almacenamiento |
413 Payload Too Large |
El archivo supera el tamaño máximo permitido por tu plan |
400 Bad Request |
El lienzo de la imagen supera los 40.000.000 de píxeles (protección contra bombas de descompresión) |
Límites#
| Límite | Valor |
|---|---|
| Tamaño máximo de archivo | Según el plan (Free: 5 MB) |
| Dimensiones máximas de imagen | 40.000.000 de píxeles (p. ej. 8000x5000) |
| Formatos | PNG, JPEG, WebP (solo estáticos — sin animaciones) |
| Tamaño objetivo | Mejor esfuerzo — los objetivos inalcanzables devuelven el archivo más pequeño alcanzable |
| Conversiones mensuales | Según el plan |
Preguntas frecuentes#
¿Cómo comprimo una imagen sin perder calidad?#
Envía la imagen a /v1/convert/compress-image sin target_size_kb. El endpoint elimina los metadatos y recodifica con los ajustes sin pérdida más fuertes para el formato — los PNG y los WebP sin pérdida permanecen idénticos píxel a píxel, y el JPEG conserva sus tablas de cuantización originales, por lo que no se introduce pérdida de cuantización adicional. El resultado nunca es más grande que tu entrada.
¿Cómo comprimo una imagen a un tamaño de archivo específico?#
Pasa target_size_kb con tu presupuesto en kilobytes (por ejemplo, 200 para 200 KB). La optimización sin pérdida se ejecuta primero; si el archivo sigue por encima del presupuesto, la imagen se reduce de escala con la relación de aspecto bloqueada hasta que quepa. Si el objetivo es inalcanzable incluso en la escala mínima, obtienes el archivo más pequeño alcanzable en lugar de un error.
¿La compresión cambia el formato de la imagen?#
No, nunca. PNG sigue siendo PNG, JPEG sigue siendo JPEG y WebP sigue siendo WebP — el endpoint rechaza los archivos cuyo contenido no coincide con su extensión en lugar de convertirlos silenciosamente.
¿Se eliminan los metadatos de mi imagen?#
Los EXIF, XMP y fragmentos de texto PNG se eliminan, lo que a menudo ahorra espacio por sí solo. Dos cosas se conservan deliberadamente: el perfil de color ICC (para que los colores no cambien en visores con gestión de color) y el indicador de orientación EXIF (para que las fotos rotadas se sigan mostrando correctamente).
¿Puedo comprimir imágenes animadas?#
No. Los archivos APNG y WebP animados se rechazan con un error 400 en lugar de aplanarse silenciosamente a un solo fotograma.