Size and Quality

Size, Quality and the Auto Option

size takes 1024x1024, 1536x1024, 1024x1536, auto, or any WIDTHxHEIGHT with sides that are multiples of 16, an aspect ratio from 1:3 to 3:1, no side over 3,840 pixels and 655,360 to 8,294,400 pixels in all. quality runs low, medium, high, xhigh and max, and defaults to auto.

params.mjs: four parameter sets through the Images APIJavaScript
// params.mjs: four parameter sets through the Images API (Section 8.3)
import fs from 'node:fs';
import OpenAI from 'openai';
const openai = new OpenAI();
const scene = 'A reading nook in a small bookshop: armchair, lamp, shelves, '
  + 'flat vector illustration in deep blue and saffron';
const icon = 'A flat icon of an open book with a small bird nesting on it, deep blue';
const runs = {
  auto: { prompt: scene, size: 'auto', quality: 'auto' },
  wide: { prompt: scene, size: '1536x864', quality: 'low' },
  clear: { prompt: icon, quality: 'low', background: 'transparent', output_format: 'webp' },
  jpeg: { prompt: icon, quality: 'low', n: 2, output_format: 'jpeg', output_compression: 60 },
};
for (const [name, params] of Object.entries(runs)) {
  const t0 = performance.now();
  const res = await openai.images.generate({ model: 'gpt-image-2.5-flare', ...params });
  const secs = ((performance.now() - t0) / 1000).toFixed(1);
  const kb = res.data.map((d, i) => {
    const bytes = Buffer.from(d.b64_json, 'base64');
    fs.writeFileSync(`${name}-${i}.${res.output_format}`, bytes);
    return `${Math.round(bytes.length / 1024)} KB`;
  });
  console.log(name.padEnd(5), res.size, res.quality, res.background, res.output_format,
    kb.join(' + '), `${res.usage.output_tokens} tokens ${secs} s`);
}
Output
auto  1214x1295 low opaque png 1519 KB 215 tokens 16.3 s
wide  1536x864 low opaque png 1375 KB 120 tokens 12.3 s
clear 1254x1254 low transparent webp 351 KB 229 tokens 10.6 s
jpeg  1254x1254 low opaque jpeg 76 KB + 73 KB 458 tokens 9.5 s
params.mjs's five images: auto, wide, clear (transparent WebP on a checkerboard) and the n = 2 JPEGs. Flare, low quality, $0.0314 in all
params.mjs's five images: auto, wide, clear (transparent WebP on a checkerboard) and the n = 2 JPEGs. Flare, low quality, $0.0314 in all

Left to auto, the model picked low quality and a 1214x1295 portrait, a size you could not request yourself; omitting size gave the icons 1254x1254. Name both values in production so cost and layout stay predictable.

A size checker for gpt-image-2.5: the rules for WIDTHxHEIGHT, the shape to scale, and the tokens each quality costsHTMLLive
<!doctype html>
<style>
  body { margin: 0; padding: 8px; background: #fafaf7; font: 12px system-ui, sans-serif; color: #263238; max-width: 600px; }
  input[type=number] { width: 70px; }
  .row { display: flex; gap: 12px; align-items: flex-start; }
  ul { list-style: none; padding: 0; margin: 4px 0; }
  li.ok::before { content: '✔ '; color: #3f7d3a; } li.bad::before { content: '✘ '; color: #b5452f; }
  table { border-collapse: collapse; background: #fff; margin-top: 6px; }
  td, th { border: 1px solid #ddd; padding: 2px 8px; text-align: right; }
</style>
<p>size: <input type="number" id="w" value="1536" step="16"> × <input type="number" id="h" value="864" step="16">
  presets: <button data-s="1024x1024">1024²</button> <button data-s="1536x1024">1536×1024</button>
  <button data-s="512x1536">512×1536</button> <button data-s="500x1500">500×1500</button> <button data-s="3840x2160">4K</button></p>
<div class="row"><canvas id="shape" width="200" height="200"></canvas><div><ul id="rules"></ul><div id="cost"></div></div></div>
<script>
  // OpenAI's calculator formula for gpt-image-2.5 output tokens
  const PATCHES = { low: 16, medium: 24, high: 48, xhigh: 64, max: 96 };
  function outputTokens(w, h, quality) {
    const p = PATCHES[quality], s = p / (Math.max(w, h) / Math.min(w, h));
    const f = Math.floor(s), q = s - f === 0.5 ? f + (f % 2) : Math.round(s);
    return Math.ceil(p * q * (2e6 + w * h) / 4e6);
  }
  const $ = id => document.getElementById(id);
  function update() {
    const w = +$('w').value, h = +$('h').value, ratio = Math.max(w, h) / Math.min(w, h);
    const rules = [
      [w % 16 === 0 && h % 16 === 0, 'both sides are multiples of 16'],
      [ratio <= 3, `aspect ratio ${ratio.toFixed(2)}:1 is within 1:3 to 3:1`],
      [Math.max(w, h) <= 3840, 'no side over 3,840 pixels'],
      [w * h >= 655360 && w * h <= 8294400, `${(w * h).toLocaleString()} pixels, within 655,360 to 8,294,400`],
    ];
    $('rules').innerHTML = rules.map(([ok, text]) => `<li class="${ok ? 'ok' : 'bad'}">${text}</li>`).join('');
    const valid = rules.every(r => r[0]);
    // The shape to scale inside a 3840-pixel frame
    const g = $('shape').getContext('2d'), k = 190 / 3840;
    g.clearRect(0, 0, 200, 200);
    g.strokeStyle = '#ccc';  g.strokeRect(5, 5, 190, 190);
    g.fillStyle = valid ? '#1f5f8b' : '#b5452f';  g.globalAlpha = 0.8;
    g.fillRect(5, 5, Math.min(190, w * k), Math.min(190, h * k));  g.globalAlpha = 1;
    g.fillStyle = '#263238';  g.fillText(`${w} × ${h}`, 10, 190);
    $('cost').innerHTML = valid
      ? '<table><tr><th>quality</th><th>tokens</th><th>$ at $30/M</th></tr>' + Object.keys(PATCHES).map(q => {
          const t = outputTokens(w, h, q);
          return `<tr><td>${q}</td><td>${t.toLocaleString()}</td><td>$${(t * 30 / 1e6).toFixed(4)}</td></tr>`;
        }).join('') + '</table><p>auto may pick any size and quality: name both to keep cost predictable.</p>'
      : `<p style="color:#b5452f">400 invalid_value: Invalid size '${w}x${h}'. Not billed.</p>`;
  }
  document.querySelectorAll('input').forEach(i => i.addEventListener('input', update));
  document.querySelectorAll('[data-s]').forEach(b => b.onclick = () => {
    [$('w').value, $('h').value] = b.dataset.s.split('x');  update();
  });
  update();
</script>