How to Use GPT Image 2.5 API: 13 Steps, 90 Min [2026]

OpenAI shipped GPT Image 2.5 on September 8, 2026, and it landed with a twist most teams weren’t expecting: instead of one model, developers now choose between two API variants, Flare and Sunburst, tuned for different jobs inside the same generation. If you’ve been generating images with the older GPT Image 2 endpoint, or you’re building an app from scratch, this guide walks through account setup, API keys, your first request, quality tiers, cost math, and the production patterns that keep a bill from spiraling. By the end you’ll have a working image-generation pipeline and a clear answer to the question every team asks in week one: Flare or Sunburst?

Google · Preferred Sources

Don't miss new tech stories on Google

Add Tech Insider once in the Google app and our stories appear in your news suggestions.

Add Now

What Is GPT Image 2.5, and Why Two Models Instead of One?

GPT Image 2.5 is OpenAI’s latest image generation release, and it shipped simultaneously inside ChatGPT, ChatGPT Work, Codex, and the standalone image API on desktop, mobile, and web. Free-tier ChatGPT users got access on day one too, subject to the usual plan limits. The headline change from the developer side is the split into two named API variants: Flare, described by OpenAI as the faster default option, and Sunburst, the slower variant built for precision edits and more exact rendering control.

That split matters because it changes how you architect a product. A one-off marketing banner generator has different latency tolerance than a photo-editing app where a user is waiting on-screen for a masked inpaint to resolve. Under the old single-model setup, you tuned quality parameters and hoped for the best. Now the model choice itself is part of the design decision, made before a single prompt is written.

Inside ChatGPT, the same release also introduced a “Sketch” feature to ChatGPT Images 2.5, letting users rough out a composition before the model fills in detail. That’s a consumer-facing feature, but it’s worth knowing about if your product embeds ChatGPT-style flows rather than calling the raw API, since the underlying model behavior (composition-first, detail-second) carries into API prompting patterns too.

Prerequisites: What You Need Before You Start

This tutorial assumes a working developer setup. Confirm each of these before moving to Step 1:

  • An OpenAI platform account with billing enabled at platform.openai.com (a credit card on file is required before image generation requests will succeed, even on pay-as-you-go)
  • Python 3.10 or newer, or Node.js 18 LTS or newer, if you’re calling the API directly rather than through ChatGPT
  • The official OpenAI Python SDK, version 1.55.0 or later, installed via pip install --upgrade openai (check PyPI for the current release before you start, since OpenAI ships SDK updates frequently)
  • An API key generated from the OpenAI dashboard under API Keys, scoped to a project with image generation permissions enabled
  • A terminal or code editor (VS Code 1.95 or later is fine) and curl installed for the raw HTTP examples in this guide
  • Roughly 90 minutes for the full walkthrough, including account setup, first request, and testing both Flare and Sunburst on the same prompt
  • A budget line item: even light testing across both variants will use a few dollars of API credit, covered in the cost breakdown below

You do not need prior experience with GPT Image 2 to follow this guide, though if you’re migrating an existing integration, keep your old code open in a second window. The endpoint structure is unchanged; only the model name and a couple of parameters differ.

Step 1: Create or Verify Your OpenAI Platform Account

Go to platform.openai.com and sign in, or create a new account if this is your first time using the API (note: a ChatGPT Plus or Work subscription does not automatically grant API access, these are billed separately). Once logged in, navigate to Settings, then Billing, and add a payment method. OpenAI requires an active payment method on file before any image generation call will process, even if you’re within a free trial credit balance.

While you’re in the billing section, set a monthly spend limit. Image generation costs scale with output resolution and quality tier, and it’s easy to burn through credit quickly during testing if you’re iterating on prompts in a loop. A $20 soft cap is a sane starting point for a solo developer testing this guide.

Step 2: Generate and Secure Your API Key

From the dashboard, click API Keys in the left sidebar, then Create New Secret Key. Name it something identifiable, like gpt-image-2-5-tutorial, so you can revoke it later without guessing which key is which. Copy the key immediately; OpenAI will not show it to you again.

Store the key as an environment variable rather than pasting it into your source code. On Mac or Linux:

export OPENAI_API_KEY="sk-your-key-here"
echo 'export OPENAI_API_KEY="sk-your-key-here"' >> ~/.zshrc

On Windows PowerShell:

setx OPENAI_API_KEY "sk-your-key-here"

If you’re deploying to production, use your platform’s secrets manager (AWS Secrets Manager, Vercel environment variables, or similar) instead of a shell profile. Never commit a key to a git repository, and add a .env entry to your .gitignore before your first commit, not after.

Step 3: Install the SDK and Confirm Connectivity

Install the Python SDK:

pip install --upgrade openai

Then run a quick connectivity check that lists available models, which confirms your key is valid and billing is active before you spend anything on image generation:

from openai import OpenAI

client = OpenAI()  # reads OPENAI_API_KEY from environment

models = client.models.list()
for m in models.data:
    if "image" in m.id.lower():
        print(m.id)

If this returns a list including image-capable model IDs without throwing an authentication error, your setup is working. If you get a 401, double-check that the environment variable is actually loaded in the shell session you’re running the script from, not just the one where you set it earlier.

Step 4: Understand Flare vs. Sunburst Before Writing Code

Before your first real request, decide which model variant fits your use case. Both were released together on September 8, 2026, as siblings under the same GPT Image 2.5 generation, split by workload rather than by capability generation.

AttributeFlareSunburst
PositioningFast default variantPrecision variant for exact edits
Best forRapid iteration, drafts, high-volume generationFine-grained inpainting, exact text rendering, careful composition edits
Text input pricing$5 per 1M tokens$5 per 1M tokens
Image input pricing$8 per 1M tokens$8 per 1M tokens
Cached image input$2 per 1M tokens$2 per 1M tokens
Image output pricing$30 per 1M tokens$30 per 1M tokens
Model ID (API)gpt-image-2.5-flaregpt-image-2.5-sunburst

Notice that token pricing is identical across both variants, unchanged from the previous GPT Image 2 rates. The cost difference between Flare and Sunburst in practice comes from how many output tokens a given job actually consumes, not from a different rate card. Sunburst’s more careful rendering process can produce more output tokens per image on complex edits, which is where the real cost gap shows up on your invoice, not in the sticker price per token.

A practical rule that’s worked well in early testing: default to Flare for anything user-facing where latency matters, like a chat app generating an illustration mid-conversation. Reach for Sunburst when the job involves precise text overlays, product photography where exact object placement matters, or multi-step edits where an earlier mistake compounds if the model drifts.

Step 5: Make Your First Image Generation Request

With your key set and model choice made, generate your first image using Flare:

from openai import OpenAI
import base64

client = OpenAI()

result = client.images.generate(
    model="gpt-image-2.5-flare",
    prompt="A weathered leather-bound journal open on a wooden desk, "
           "soft morning light from a nearby window, shallow depth of field",
    size="1024x1024",
    quality="medium"
)

image_bytes = base64.b64decode(result.data[0].b64_json)
with open("journal.png", "wb") as f:
    f.write(image_bytes)

print("Saved journal.png")

Run the script. Within a few seconds you should have a PNG file in your working directory. If the request hangs for more than 30 seconds, check your network connection and confirm you haven’t hit a rate limit (covered in the troubleshooting section below).

To try the same prompt with Sunburst for comparison, change only the model parameter:

result = client.images.generate(
    model="gpt-image-2.5-sunburst",
    prompt="A weathered leather-bound journal open on a wooden desk, "
           "soft morning light from a nearby window, shallow depth of field",
    size="1024x1024",
    quality="medium"
)

Compare the two outputs side by side. On straightforward prompts like this one, the difference is often subtle. Where Sunburst earns its keep is on prompts with embedded text, multiple named objects that need to stay spatially correct, or reference-image edits, which we’ll cover in Step 8.

Step 6: Choose the Right Quality Tier for Your Budget

GPT Image 2.5 pricing is unchanged from GPT Image 2, and per-image cost depends heavily on which quality tier you select. Reported per-image pricing at 1024×1024 resolution ranges from roughly $0.006 at the lowest quality setting up to $0.211 at the newest “max” quality tier. The available tiers are Low, Medium, High, Xhigh, and Max.

Quality TierApprox. Cost per 1024×1024 ImageTypical Use Case
Low~$0.006Thumbnails, placeholder art, high-volume batch jobs
Medium~$0.02–$0.04Standard app content, blog illustrations, draft concepts
High~$0.07–$0.10Marketing assets, hero images, client-facing deliverables
Xhigh~$0.13–$0.16Print-adjacent work, detailed product renders
Max~$0.211Final production assets where fidelity is non-negotiable

Set the quality parameter explicitly in every request rather than relying on a default, since defaults can shift between SDK versions and you don’t want a silent cost increase across your fleet of generation calls:

result = client.images.generate(
    model="gpt-image-2.5-flare",
    prompt="Minimalist app icon, flat design, rounded square, teal and white",
    size="1024x1024",
    quality="low"  # explicit, cheap, good for icon iteration
)

A common pattern in production apps: generate drafts at Low or Medium quality for user preview, then re-run only the selected final image at High or Max once the user confirms which concept they want. This can cut your average cost per delivered image by more than half compared to generating every candidate at final quality.

Step 7: Handle Text-to-Image and Text-Input Token Costs Separately

It’s easy to think of image generation pricing as a single number, but the billing model actually separates text input, image input, cached image input, and image output into distinct token rates. For a text-only prompt with no reference image, you’re paying the $5 per 1M text-input-token rate on your prompt, plus the $30 per 1M output-token rate on the generated image. Long, highly detailed prompts do add measurable cost at scale, even though it’s a small fraction of a cent per request individually.

Log token usage from every response so your finance team isn’t surprised at the end of the month:

result = client.images.generate(
    model="gpt-image-2.5-flare",
    prompt="A city skyline at dusk, cyberpunk lighting, wide angle",
    size="1024x1024",
    quality="high"
)

usage = result.usage
print(f"Input tokens: {usage.input_tokens}")
print(f"Output tokens: {usage.output_tokens}")
print(f"Total tokens: {usage.total_tokens}")

Pipe this into whatever logging or cost-tracking system you already use for LLM API calls. Treating image generation cost tracking as a separate afterthought from your text-based LLM spend is a common mistake that leads to budget surprises, since image output tokens are billed at a materially higher rate than typical text completion tokens.

Step 8: Edit an Existing Image With a Reference

Both Flare and Sunburst support image editing, where you pass an existing image plus a text instruction describing the change. This is where Sunburst’s precision positioning tends to matter most, particularly for edits that must preserve exact object placement or text.

result = client.images.edit(
    model="gpt-image-2.5-sunburst",
    image=open("product_photo.png", "rb"),
    prompt="Replace the plain white background with a softly blurred "
           "outdoor cafe setting, keep the product exactly as is, "
           "keep the label text sharp and fully legible",
    quality="high"
)

image_bytes = base64.b64decode(result.data[0].b64_json)
with open("product_photo_edited.png", "wb") as f:
    f.write(image_bytes)

Notice the prompt explicitly separates what should change (the background) from what must not change (the product and its label text). Vague edit prompts are the single most common cause of unpredictable results in both variants, but especially with Flare, which prioritizes speed over careful constraint-following.

Step 9: Build a Batch Generation Script for Production Volume

Most real applications don’t generate one image at a time interactively; they process a queue. Here’s a pattern for batch generation with basic error handling and a delay to avoid hammering the rate limit:

import time
from openai import OpenAI, RateLimitError

client = OpenAI()

prompts = [
    "A steaming cup of coffee on a marble counter, morning light",
    "A pair of running shoes on a trail, autumn leaves, natural light",
    "A stack of hardcover books on a nightstand, warm lamp glow",
]

def generate_with_retry(prompt, model="gpt-image-2.5-flare", max_retries=3):
    for attempt in range(max_retries):
        try:
            return client.images.generate(
                model=model,
                prompt=prompt,
                size="1024x1024",
                quality="medium"
            )
        except RateLimitError:
            wait = 2 ** attempt
            print(f"Rate limited, retrying in {wait}s")
            time.sleep(wait)
    raise RuntimeError(f"Failed after {max_retries} attempts: {prompt}")

for i, prompt in enumerate(prompts):
    result = generate_with_retry(prompt)
    with open(f"batch_{i}.png", "wb") as f:
        import base64
        f.write(base64.b64decode(result.data[0].b64_json))
    print(f"Generated batch_{i}.png")
    time.sleep(1)  # basic pacing between requests

For genuinely high-volume jobs, replace the simple sleep-based pacing with a proper token-bucket rate limiter and run requests concurrently up to your account’s tier limit, which you can check in the Limits section of the OpenAI dashboard.

Step 10: Set Up Content Moderation and Safety Checks

Every image generation product needs a moderation layer, both to comply with OpenAI’s usage policies and to protect your own users. GPT Image 2.5 applies its own safety filtering server-side and will return an error if a prompt violates content policy, but you should still catch and handle that gracefully rather than letting it surface as a raw exception to end users:

from openai import OpenAI, BadRequestError

client = OpenAI()

def safe_generate(prompt, model="gpt-image-2.5-flare"):
    try:
        return client.images.generate(
            model=model,
            prompt=prompt,
            size="1024x1024",
            quality="medium"
        )
    except BadRequestError as e:
        if "content_policy" in str(e).lower() or "safety" in str(e).lower():
            return {"error": "This request was flagged by content safety checks."}
        raise

If you’re building a consumer product where users write their own prompts, add a client-side pre-check too, using OpenAI’s moderation endpoint before you even send the image request. This saves you the token cost of a request that would be rejected anyway.

Step 11: Integrate Into a Web Application

For a real product, you’ll expose image generation through your own API endpoint rather than calling OpenAI directly from the client (never expose your API key in frontend code). Here’s a minimal Flask example:

from flask import Flask, request, jsonify, send_file
from openai import OpenAI
import base64, io, os

app = Flask(__name__)
client = OpenAI()

@app.route("/api/generate-image", methods=["POST"])
def generate_image():
    data = request.get_json()
    prompt = data.get("prompt", "").strip()
    if not prompt or len(prompt) > 4000:
        return jsonify({"error": "Prompt required, max 4000 characters"}), 400

    model = "gpt-image-2.5-sunburst" if data.get("precise") else "gpt-image-2.5-flare"

    try:
        result = client.images.generate(
            model=model,
            prompt=prompt,
            size=data.get("size", "1024x1024"),
            quality=data.get("quality", "medium")
        )
    except Exception as e:
        return jsonify({"error": str(e)}), 502

    image_bytes = base64.b64decode(result.data[0].b64_json)
    return send_file(io.BytesIO(image_bytes), mimetype="image/png")

if __name__ == "__main__":
    app.run(port=5000)

This lets your frontend request either speed (Flare, the default) or precision (Sunburst, via the precise flag) without exposing model names or your API credentials to the browser.

Step 12: Cache Results to Cut Repeat-Generation Costs

Because image output tokens are the most expensive line item on your bill at $30 per 1M tokens, avoid regenerating an image for a prompt you’ve already served. A simple hash-based cache keyed on the prompt, model, size, and quality tuple prevents duplicate spend:

import hashlib, os, json

CACHE_DIR = "image_cache"
os.makedirs(CACHE_DIR, exist_ok=True)

def cache_key(prompt, model, size, quality):
    raw = f"{prompt}|{model}|{size}|{quality}"
    return hashlib.sha256(raw.encode()).hexdigest()

def get_or_generate(prompt, model="gpt-image-2.5-flare", size="1024x1024", quality="medium"):
    key = cache_key(prompt, model, size, quality)
    path = os.path.join(CACHE_DIR, f"{key}.png")
    if os.path.exists(path):
        return path

    result = client.images.generate(model=model, prompt=prompt, size=size, quality=quality)
    import base64
    with open(path, "wb") as f:
        f.write(base64.b64decode(result.data[0].b64_json))
    return path

Note that OpenAI also offers cached image input pricing at $2 per 1M tokens (versus $8 for fresh image input), which applies when you resend an image the API has recently processed as part of a multi-turn edit session. That’s a separate mechanism from the local disk cache above, and both are worth using together in a production system.

Step 13: Verify Output and Log for Debugging

Before shipping generated images to end users, add a lightweight verification step: check the file isn’t zero bytes, confirm it opens as a valid image, and log the prompt alongside the output for later debugging when a user reports a bad result.

from PIL import Image
import logging

logging.basicConfig(filename="image_gen.log", level=logging.INFO)

def verify_and_log(path, prompt, model):
    try:
        img = Image.open(path)
        img.verify()
        logging.info(f"OK | model={model} | size={img.size} | prompt={prompt[:80]}")
        return True
    except Exception as e:
        logging.error(f"FAILED | model={model} | error={e} | prompt={prompt[:80]}")
        return False

This log becomes invaluable once you’re running thousands of generations a week and need to spot patterns, like a specific prompt structure that reliably produces malformed output on Flare but not Sunburst.

Sample Output: What a Real API Response Looks Like

Here’s an abbreviated example of the JSON structure returned by a successful generation call, useful for building your own parsing logic without guessing at field names:

{
  "created": 1757296812,
  "data": [
    {
      "b64_json": "iVBORw0KGgoAAAANSUhEUgAA...",
      "revised_prompt": "A weathered leather-bound journal open on a wooden desk..."
    }
  ],
  "usage": {
    "input_tokens": 24,
    "output_tokens": 1290,
    "total_tokens": 1314
  },
  "model": "gpt-image-2.5-flare"
}

The revised_prompt field is worth logging separately, since the model sometimes expands a terse prompt with details it inferred, and comparing your original prompt to the revised one is a fast way to understand why an output didn’t match expectations.

5 Common Pitfalls When Switching to GPT Image 2.5

1. Assuming Flare and Sunburst share a rate limit pool. Some accounts see separate rate limit buckets per model. If you’re load-balancing across both variants, check your account’s Limits page rather than assuming shared headroom.

2. Hardcoding the old GPT Image 2 model string. Existing integrations that pin model="gpt-image-2" will keep working, since OpenAI doesn’t typically deprecate models overnight, but you won’t get any of the 2.5 improvements until you explicitly update the string to gpt-image-2.5-flare or gpt-image-2.5-sunburst.

3. Generating at Max quality by default. It’s tempting to always request the best output, but at roughly $0.211 per image, doing that across a high-volume feature multiplies cost fast. Reserve Max for final, user-approved assets.

4. Ignoring the revised_prompt field. When output doesn’t match intent, the fastest debugging step is comparing your prompt to what the model actually interpreted, and teams that skip this waste time guessing at prompt wording instead.

5. Treating text-input token cost as negligible at scale. A single verbose prompt costs fractions of a cent, but if your app auto-generates long, template-heavy prompts on every request across millions of calls, that line item adds up in a way flat per-image pricing estimates miss.

Troubleshooting: 8 Issues You’ll Likely Hit

1. 401 Unauthorized errors. Your API key environment variable isn’t loaded in the current shell session, or the key was revoked. Re-export it and confirm with echo $OPENAI_API_KEY.

2. 429 Too Many Requests. You’ve exceeded your account’s requests-per-minute or tokens-per-minute limit. Implement exponential backoff as shown in Step 9, and check your current tier limits in the dashboard.

3. Generated image doesn’t match the prompt at all. Check the revised_prompt field first. If it diverged heavily from your input, your original prompt was likely too ambiguous or contained conflicting instructions.

4. Edit requests ignore the “keep unchanged” instruction. Switch from Flare to Sunburst for edit-heavy workflows; Flare’s speed optimization trades off some constraint adherence on complex multi-part edits.

5. Unexpectedly high monthly bill. Audit your quality tier defaults across every code path. It’s common to find a debug script still set to quality="max" that made it into a production cron job.

6. Content policy rejection on a seemingly benign prompt. Certain combinations of brand names, real public figures, or specific violence-adjacent language can trigger filters even in an innocuous context. Rephrase generically and test again.

7. Slow response times on Sunburst. This is expected behavior, not a bug. Sunburst prioritizes precision over speed by design. If latency is a hard requirement, switch that specific call path to Flare.

8. b64_json field is empty or truncated. This usually indicates a timeout or connection issue mid-response on very large image sizes. Retry the request, and consider requesting a smaller size for testing before scaling up to production dimensions.

Advanced Tips for Production Deployments

Once your basic pipeline works, a few refinements separate a prototype from something that holds up under real traffic. First, build a prompt template library rather than letting every code path construct prompts ad hoc. Consistent prompt scaffolding (consistent lighting language, consistent aspect ratio phrasing) produces more predictable results across your whole product, and it’s far easier to debug one shared template than a dozen bespoke prompt strings scattered through your codebase.

Second, consider routing by prompt complexity rather than picking one model variant globally for your whole app. A simple heuristic, like checking for the presence of quoted text (which needs to render accurately) or multiple named subjects, can auto-select Sunburst for those cases while defaulting everything else to Flare. This gets you Sunburst’s precision only where it’s actually needed, keeping average cost and latency down.

Third, if you’re running this at meaningful volume, compare GPT Image 2.5 costs against other providers in your stack rather than assuming it’s your only option for every job type. Some teams run a tiered approach: a cheaper, faster model for internal drafts and iteration, then GPT Image 2.5 Sunburst for the final, client-facing asset. This is the same “escalate only when needed” pattern that’s common in LLM cost optimization, applied to image generation.

Fourth, set up a dead-letter queue for failed generations in any asynchronous pipeline. A generation that fails moderation, times out, or hits a rate limit shouldn’t silently vanish from your job queue; route it somewhere a human can review and decide whether to retry with a modified prompt or drop it entirely.

Complete Working Project: Image Generation Microservice

Putting the pieces from this guide together, here’s a complete, minimal but production-oriented microservice you can run today. Save this as app.py:

import base64
import hashlib
import io
import logging
import os
import time

from flask import Flask, request, jsonify, send_file
from openai import OpenAI, RateLimitError, BadRequestError

logging.basicConfig(filename="image_service.log", level=logging.INFO)

app = Flask(__name__)
client = OpenAI()

CACHE_DIR = "image_cache"
os.makedirs(CACHE_DIR, exist_ok=True)


def cache_key(prompt, model, size, quality):
    raw = f"{prompt}|{model}|{size}|{quality}"
    return hashlib.sha256(raw.encode()).hexdigest()


def generate_with_retry(prompt, model, size, quality, max_retries=3):
    for attempt in range(max_retries):
        try:
            return client.images.generate(
                model=model, prompt=prompt, size=size, quality=quality
            )
        except RateLimitError:
            time.sleep(2 ** attempt)
    raise RuntimeError("Rate limit exceeded after retries")


@app.route("/api/generate-image", methods=["POST"])
def generate_image():
    data = request.get_json(force=True)
    prompt = (data.get("prompt") or "").strip()
    if not prompt or len(prompt) > 4000:
        return jsonify({"error": "Prompt required, max 4000 characters"}), 400

    model = "gpt-image-2.5-sunburst" if data.get("precise") else "gpt-image-2.5-flare"
    size = data.get("size", "1024x1024")
    quality = data.get("quality", "medium")

    key = cache_key(prompt, model, size, quality)
    cached_path = os.path.join(CACHE_DIR, f"{key}.png")
    if os.path.exists(cached_path):
        logging.info(f"CACHE HIT | {key}")
        return send_file(cached_path, mimetype="image/png")

    try:
        result = generate_with_retry(prompt, model, size, quality)
    except BadRequestError as e:
        logging.error(f"BadRequest | {e}")
        return jsonify({"error": "Request rejected by content safety checks"}), 400
    except Exception as e:
        logging.error(f"Failed | {e}")
        return jsonify({"error": "Generation failed"}), 502

    image_bytes = base64.b64decode(result.data[0].b64_json)
    with open(cached_path, "wb") as f:
        f.write(image_bytes)

    logging.info(
        f"GENERATED | model={model} | tokens={result.usage.total_tokens} | key={key}"
    )
    return send_file(io.BytesIO(image_bytes), mimetype="image/png")


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

Run it with python app.py, then test with:

curl -X POST http://localhost:5000/api/generate-image \
  -H "Content-Type: application/json" \
  -d '{"prompt": "A vintage typewriter on a desk, warm afternoon light", "quality": "medium"}' \
  --output result.png

This service already handles model routing, disk-based caching, retry logic, content-policy error handling, and structured logging, which covers most of what a small-to-mid-size product needs before reaching for a more elaborate queue-based architecture.

Understanding Rate Limits and Usage Tiers

OpenAI assigns API accounts to usage tiers based on payment history and total spend, and your tier determines how many image generation requests per minute you can send, alongside how many tokens per minute across text and image workloads combined. New accounts start in the lowest tier and graduate automatically as spend accumulates and payments clear, typically over a period of days to weeks rather than instantly after your first invoice.

Check your current tier and limits under Settings, then Limits, in the OpenAI dashboard before you build out anything that depends on high throughput. A common mistake is designing a batch pipeline assuming top-tier throughput, then discovering in staging that a brand-new account is capped far lower. If your product genuinely needs higher limits sooner, OpenAI’s support team can review manual tier increase requests, though approval isn’t guaranteed on any particular timeline.

Rate limit headers are returned on every response and are worth reading programmatically rather than guessing:

import requests

response = requests.post(
    "https://api.openai.com/v1/images/generations",
    headers={"Authorization": f"Bearer {api_key}"},
    json={
        "model": "gpt-image-2.5-flare",
        "prompt": "A quiet library reading room, tall windows, soft light",
        "size": "1024x1024",
        "quality": "medium"
    }
)

print("Requests remaining:", response.headers.get("x-ratelimit-remaining-requests"))
print("Tokens remaining:", response.headers.get("x-ratelimit-remaining-tokens"))
print("Resets in:", response.headers.get("x-ratelimit-reset-requests"))

Building your pacing logic around these headers, rather than a fixed sleep interval, lets your pipeline run as fast as your actual limit allows instead of leaving throughput on the table with an overly conservative fixed delay, or getting throttled with one that’s too aggressive.

Security Practices Beyond the API Key

Protecting your API key is the obvious first step, but a production image generation feature has a few other exposure points worth closing before launch. First, validate and sanitize any user-uploaded reference image before it’s forwarded to the edit endpoint. Confirm the file is actually an image (not a disguised script or oversized payload) using a library like Pillow’s verify() method, and cap file size well below whatever limit the API itself enforces.

from PIL import Image
import io

MAX_UPLOAD_BYTES = 8 * 1024 * 1024  # 8MB

def validate_upload(file_bytes):
    if len(file_bytes) > MAX_UPLOAD_BYTES:
        raise ValueError("File too large")
    try:
        img = Image.open(io.BytesIO(file_bytes))
        img.verify()
    except Exception:
        raise ValueError("Not a valid image file")
    return True

Second, apply per-user rate limiting on your own API endpoint, separate from OpenAI’s account-level limits. Without this, a single user or bot can exhaust your entire monthly budget by hammering your generation endpoint in a tight loop, well before OpenAI’s own limits would ever kick in.

Third, if you’re storing generated images (rather than streaming them back and discarding), scan your storage bucket permissions before launch. Generated content, especially anything derived from user prompts, should default to private access with signed, time-limited URLs for delivery, not a public bucket that anyone with the link can browse indefinitely.

Fourth, log prompts for abuse review, but be deliberate about retention. Keeping every prompt indefinitely creates a growing liability if your product ever needs to respond to a legal request or a data deletion request from a user; set an explicit retention window (30 to 90 days is common) and purge on a schedule rather than accumulating logs forever by default.

Migrating an Existing GPT Image 2 Integration

If you already have a working integration against the previous GPT Image 2 endpoint, the migration path is narrow and low-risk, which is part of why this release is worth adopting quickly rather than waiting. The request and response shape is unchanged; you’re swapping a model string and, optionally, adding logic to route between two model choices instead of one.

A practical migration sequence: first, update a single non-critical code path, like an internal admin tool or a staging environment, to call gpt-image-2.5-flare instead of the old model string, and run your existing test suite against it unchanged. Second, compare a sample of outputs side by side against the old model on the same prompts, checking specifically for any regressions in your most common use cases rather than assuming parity. Third, once you’re satisfied, roll the change out to production behind a feature flag, so you can revert instantly to the previous model string if something unexpected surfaces under real traffic.

Only after Flare is running cleanly in production should you evaluate whether specific call sites, particularly anything involving image edits with strict preservation requirements, would benefit from a targeted switch to Sunburst. Treat that as a second, separate rollout rather than bundling both changes into one deployment, since it makes isolating the source of any issue far simpler if something does go wrong.

How GPT Image 2.5 Fits Into the Broader Image Model Landscape

GPT Image 2.5 arrives into a crowded field of premium proprietary image models, alongside offerings like Nano Banana 2, Qwen Image 3.0, and Seedream 5.0 Pro and Lite, all competing on quality, latency, and price per image. If you’re weighing GPT Image 2.5 against its predecessor’s positioning versus Google’s rival model, our earlier GPT Image 2 vs. Nano Banana Pro comparison is a useful baseline, and our AI image generator API cost breakdown covers how these providers stack up on price per image at scale. What differentiates GPT Image 2.5 isn’t a lower price (its per-token rates are unchanged from the prior generation) but the model-choice flexibility Flare and Sunburst introduce, letting a single API account serve both fast, cheap iteration and slower, higher-fidelity output without switching providers.

For teams already standardized on the OpenAI ecosystem for text generation, agents, or voice, adding image generation through the same account and billing relationship also simplifies operations meaningfully. You’re not managing a second vendor’s rate limits, a second dashboard, or a second invoice, which matters more than benchmark scores once you’re running production infrastructure rather than a research project.

Frequently Asked Questions

Is GPT Image 2.5 available to free ChatGPT users?
Yes. OpenAI made GPT Image 2.5 available inside ChatGPT for free-tier users at launch on September 8, 2026, subject to standard plan usage limits. API access, by contrast, requires a billed OpenAI platform account regardless of ChatGPT subscription tier.

What’s the actual difference between Flare and Sunburst?
Flare is the faster default variant, tuned for speed. Sunburst is the slower variant, tuned for precise edits and more exact rendering, particularly useful for embedded text and careful multi-object composition. Token pricing is identical between the two.

Did pricing change from GPT Image 2 to GPT Image 2.5?
No. Reported API token pricing stayed the same: $5 per 1M text input tokens, $8 per 1M image input tokens, $2 per 1M cached image input tokens, and $30 per 1M image output tokens, identical across both Flare and Sunburst.

Can I still use the old gpt-image-2 model string?
Existing integrations pinned to the previous model identifier should continue functioning, since OpenAI typically maintains backward compatibility rather than an abrupt cutoff. You will not get 2.5-generation improvements until you update the model parameter explicitly.

How much does a single image actually cost?
It depends entirely on the quality tier. Reported per-image costs at 1024×1024 range from about $0.006 at Low quality up to roughly $0.211 at the Max quality tier, with Medium, High, and Xhigh landing in between.

Does GPT Image 2.5 support editing existing images, not just generating new ones?
Yes, both variants support the image edit endpoint, where you supply an existing image plus a text instruction. Sunburst tends to perform more reliably on edits that require preserving specific details unchanged.

What new feature came with this release inside ChatGPT specifically?
OpenAI added a “Sketch” capability to ChatGPT Images 2.5, letting users rough out a composition manually before the model fills in the rest, alongside the underlying Flare and Sunburst model upgrade.

Which variant should a small team default to if they can only pick one?
Start with Flare for general use. It’s the faster, default-positioned model and handles the majority of everyday generation and editing tasks well. Add Sunburst selectively for the specific job types, precise text, careful multi-object edits, where the extra rendering time pays off.

Related Coverage

Elias Virtanen

Elias Virtanen

Cybersecurity Analyst

Elias Virtanen is the Cybersecurity Analyst at Tech Insider, bringing hands-on expertise from his background in penetration testing and security consulting. He previously worked as a security researcher at F-Secure in Helsinki, where he focused on threat intelligence and vulnerability disclosure. Elias covers ransomware trends, zero-trust architecture, and the evolving regulatory landscape including NIS2 and the EU Cyber Resilience Act. He holds a CISSP certification and an MSc in Information Security from Aalto University.

View all articles