Email Size Limits Are Everywhere
You’ve composed the perfect email. Product photos attached, screenshots inline, report PDF ready to go. You hit send. Bounce. “Message size exceeds the maximum allowed.”
Gmail caps total message size at 25MB. Outlook at 20MB. Yahoo at 25MB. These limits include the entire message — headers, body, and all attachments combined. Base64 encoding inflates attachment sizes by about 33%, so a 15MB image actually consumes roughly 20MB of your limit.
That’s the generous end. Marketing platforms and transactional email services have tighter constraints. Mailchimp recommends images under 1MB. Many ESP platforms cap individual images at 1-2MB. Newsletter builders restrict hero images to 600KB. Exceed these and your email either fails to send or renders a broken image placeholder.
The manual fix is familiar: open Photoshop or Preview, export at lower quality, check the file size, adjust, repeat. For one image, it takes a minute. For an email campaign with 15 product images, it takes an afternoon.
The Image Transformation API compresses images to exact byte targets. No guesswork, no iteration, no Photoshop.
The Compression Pipeline
A raw email image — straight from a camera or design tool — is usually too large in both dimensions and file size. The fix is a two-step pipeline: constrain the dimensions first, then compress to a target size.
import { IterationLayer } from "iterationlayer";
const client = new IterationLayer({ apiKey: "YOUR_API_KEY" });
const { data: { buffer: compressedImageBase64 } } = await client.transform({
file: { type: "url", name: "hero.jpg", url: "https://cdn.example.com/campaign/hero.jpg" },
operations: [
// Email clients render at ~600px wide max. No point keeping 4000px.
{ type: "resize", width_in_px: 1200, height_in_px: 900, fit: "inside" },
// Sharpen after downscale — resizing softens images
{ type: "sharpen", sigma: 0.5 },
// Convert to JPEG for broad email client compatibility
{ type: "convert", format: "jpeg", quality: 85 },
// Hit the size target
{ type: "compress_to_size", max_file_size_in_bytes: 500_000 },
],
});
const compressedImage = Buffer.from(compressedImageBase64, "base64");
Operations execute sequentially. The resize constrains dimensions, sharpen compensates for downscale softening, convert ensures JPEG output, and compress_to_size guarantees the result is under 500KB.
Why resize before compressing? Because compress_to_size works by reducing quality first, then dimensions if needed. If you feed it a 4000x3000 image with a 500KB target, it might need to drop quality aggressively. If you resize to 1200x900 first, the quality reduction needed is much smaller — and the image looks better at the same file size.
Common Email Size Limits
Different platforms, different limits. Here are the pipelines for the most common ones.
Gmail and Outlook attachments (under 2MB per image):
const operations = [
{ type: "resize", width_in_px: 2000, height_in_px: 1500, fit: "inside" },
{ type: "convert", format: "jpeg" },
{ type: "compress_to_size", max_file_size_in_bytes: 2_000_000 },
];
2MB per image is generous. Most photos will stay at high quality levels.
Marketing platform images (under 1MB):
const operations = [
{ type: "resize", width_in_px: 1200, height_in_px: 900, fit: "inside" },
{ type: "sharpen", sigma: 0.5 },
{ type: "convert", format: "jpeg" },
{ type: "compress_to_size", max_file_size_in_bytes: 1_000_000 },
];
1MB with a 1200px max width is plenty for email renders. Most email clients display images at 600px wide anyway — the extra resolution is for retina screens.
Newsletter hero images (under 500KB):
const operations = [
{ type: "resize", width_in_px: 1200, height_in_px: 628, fit: "cover" },
{ type: "sharpen", sigma: 0.5 },
{ type: "convert", format: "jpeg" },
{ type: "compress_to_size", max_file_size_in_bytes: 500_000 },
];
Here fit: "cover" crops to the exact 1200x628 aspect ratio — a common newsletter hero size. The image fills the dimensions and excess is trimmed.
Batch Processing for Email Campaigns
An email campaign might have 10-20 images: hero banner, product thumbnails, lifestyle photos, CTA buttons. Processing them one by one is fine for a single email. For weekly campaigns or automated triggers, you want a batch pipeline.
const MAX_FILE_SIZE_IN_BYTES = 1_000_000;
const compressForEmail = async (imageUrl: string, fileName: string) => {
const { data: { buffer } } = await client.transform({
file: { type: "url", name: fileName, url: imageUrl },
operations: [
{ type: "resize", width_in_px: 1200, height_in_px: 900, fit: "inside" },
{ type: "sharpen", sigma: 0.5 },
{ type: "convert", format: "jpeg" },
{ type: "compress_to_size", max_file_size_in_bytes: MAX_FILE_SIZE_IN_BYTES },
],
});
return Buffer.from(buffer, "base64");
};
const campaignImages = [
{ url: "https://cdn.example.com/hero.png", name: "hero.png" },
{ url: "https://cdn.example.com/product-1.png", name: "product-1.png" },
{ url: "https://cdn.example.com/product-2.png", name: "product-2.png" },
{ url: "https://cdn.example.com/lifestyle.jpg", name: "lifestyle.jpg" },
{ url: "https://cdn.example.com/cta-banner.png", name: "cta-banner.png" },
];
const compressedImages = await Promise.all(
campaignImages.map(({ url, name }) => compressForEmail(url, name))
);
Every image comes back under 1MB regardless of its original size. A 12MB DSLR photo and a 200KB design mockup both get handled correctly — the large one gets compressed, the small one passes through with minimal quality loss.
Why JPEG for Email
WebP and AVIF are better formats. They compress more efficiently and support transparency. But email clients are years behind browsers.
Outlook desktop doesn’t support WebP. Older versions of Apple Mail have spotty AVIF support. Most email clients fall back to displaying nothing — or a broken image icon — when they encounter a format they don’t understand. There’s no graceful degradation.
JPEG is the safe choice for email. Every email client, every device, every webmail interface renders JPEG correctly. You can convert to JPEG as part of the pipeline with { type: "convert", format: "jpeg" } and know it will display everywhere.
If your images have transparency — logos, product images on transparent backgrounds — add { type: "remove_transparency", hex_color: "#FFFFFF" } before the convert step. This composites the image onto a white background so you don’t end up with a black rectangle where the alpha channel used to be.
const operations = [
{ type: "resize", width_in_px: 1200, height_in_px: 900, fit: "inside" },
{ type: "remove_transparency", hex_color: "#FFFFFF" },
{ type: "convert", format: "jpeg" },
{ type: "compress_to_size", max_file_size_in_bytes: 1_000_000 },
];
Inline Images vs. Attachments
Email attachments and inline images have different constraints.
Attachments are separate files. The size limit applies to the entire message, so you’re managing a budget across all attachments. If you have 10 images and a 20MB total limit, you need each image under 2MB — or more precisely, under about 1.5MB each to account for base64 overhead and the email body itself.
Inline images (embedded in HTML) need to be smaller. They load in the email body, and large inline images make the email slow to render. Most email design guides recommend inline images under 200KB each. For a product grid with 8 thumbnails, that’s tight.
// Inline thumbnail — aggressive compression
const inlineOperations = [
{ type: "resize", width_in_px: 300, height_in_px: 300, fit: "cover" },
{ type: "sharpen", sigma: 0.5 },
{ type: "convert", format: "jpeg" },
{ type: "compress_to_size", max_file_size_in_bytes: 200_000 },
];
// Attachment — more room for quality
const attachmentOperations = [
{ type: "resize", width_in_px: 2000, height_in_px: 1500, fit: "inside" },
{ type: "convert", format: "jpeg" },
{ type: "compress_to_size", max_file_size_in_bytes: 1_500_000 },
];
What’s Next
Combine with Image Generation for branded email header images — same auth, same credit pool.
Get Started
The Image Transformation API is part of Iteration Layer. Sign up for a free account — no credit card required — and get an API key in the dashboard.
The full Image Transformation docs cover all 24 operations. For email workflows, compress_to_size combined with resize and convert handles most cases in a single API call.