- name
- gpt-image-2
- description
- Generate or edit images with OpenAI's GPT Image 2 (ChatGPT Images 2.0) via Fal AI. Two endpoints — text-to-image AND image edit (reference images + optional mask). Best-in-class for fine typography — chalkboards, signs, menus, posters, packaging, UI mockups, anything with legible text. Sync API, 1-4 images per call, custom dimensions up to 8.3MP. Includes pointer to a 700+ community prompt library (CC BY 4.0) for style/category inspiration. Triggers on gpt image 2, gpt-image-2, chatgpt image, openai image, typography image, sign mockup, poster mockup, menu image, packaging mockup, edit image, image edit, modify image, gpt image 2 prompt, awesome gpt-image-2.
# GPT Image 2 (ChatGPT Images 2.0)
OpenAI's latest image model — the strongest model available for **legible text inside images**. Use this when the picture *is* the typography: chalkboards, store signs, hand-lettered posters, menu boards, magazine covers, packaging mockups, UI screenshots, book covers.
**Two endpoints:**
- **Text-to-image** (`openai/gpt-image-2`) — generate from a prompt
- **Image edit** (`openai/gpt-image-2/edit`) — modify a reference image (with optional mask for region-specific edits)
| Field | Value |
|-------|-------|
| Model ID | `openai/gpt-image-2` (text-to-image) · `openai/gpt-image-2/edit` (edit) |
| Provider | Fal AI |
| Method | Sync (POST → response with image URLs, no polling) |
| Type | Image generation + editing |
| API Key | `.env` → `FAL_KEY` |
| Docs | https://fal.ai/models/openai/gpt-image-2 · https://fal.ai/models/openai/gpt-image-2/edit |
---
## Setup
1. Get a Fal API key from https://fal.ai/dashboard/keys
2. Add to your `.env` file (or wherever your project keeps secrets):
```
FAL_KEY=your_key_here
```
3. That's it. Sync API — no SDK install required, just `curl` or `fetch`.
---
## How to call it
Auth header is `Authorization: Key {key}` — **not** `Bearer`. This applies to both endpoints.
### Text-to-image — minimum request
```bash
curl -s -X POST "https://fal.run/openai/gpt-image-2" \
-H "Authorization: Key $FAL_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt": "A vintage diner chalkboard reading TODAY SPECIAL — Lobster Roll $24"}'
```
### Text-to-image — full request
```json
{
"prompt": "...",
"image_size": "landscape_4_3",
"quality": "medium",
"num_images": 1,
"output_format": "png"
}
```
### Image edit — minimum request
Pass one or more public image URLs as references. Prompt describes the edit.
```bash
curl -s -X POST "https://fal.run/openai/gpt-image-2/edit" \
-H "Authorization: Key $FAL_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "Same workers, same beam — but they are all on phones now. One taking a selfie, one annoyed on a call. Hard hats with AirPods.",
"image_urls": ["https://v3b.fal.media/files/b/.../reference.png"]
}'
```
### Image edit — full request
```json
{
"prompt": "...",
"image_urls": ["https://...", "https://..."],
"image_size": "auto",
"quality": "medium",
"num_images": 1,
"output_format": "png",
"mask_url": "https://.../mask.png"
}
```
- `image_size: "auto"` infers dimensions from the reference image — usually what you want.
- `mask_url` (optional) is a black/white image marking the region to edit. White = edit, black = keep. Use for surgical edits ("only change the sky").
### Response (both endpoints)
```json
{
"images": [
{
"url": "https://v3b.fal.media/files/b/.../result.png",
"content_type": "image/png",
"file_name": "result.png"
}
]
}
```
Download `images[i].url` immediately — Fal URLs may expire.
### Where do `image_urls` come from?
The reference image needs to be at a publicly accessible URL. Options:
1. **Fal's own storage** — use the JS/Python client's upload helper (`fal.storage.upload(file)` in JS, `fal_client.upload_file(path)` in Python).
2. **Reuse a previous Fal output URL** — outputs from any Fal model can be passed straight back as `image_urls` to chain edits.
3. **Any public host** — S3, GitHub raw, your own server, etc. Must be a direct image URL (not an HTML page).
If your project uses [Kie AI](https://kie.ai), their file upload endpoint at `https://kieai.redpandaai.co/api/file-stream-upload` works fine and returns a URL you can pass to Fal — see the bash recipe below for an example.
---
## Parameters
### Text-to-image (`openai/gpt-image-2`)
| Param | Required | Options | Default |
|-------|----------|---------|---------|
| `prompt` | Yes | string | — |
| `image_size` | No | preset name OR `{width, height}` | `"landscape_4_3"` |
| `quality` | No | `"low"`, `"medium"`, `"high"` | `"medium"` (Jay's default — high is overkill for most cases, ~3.5x more expensive) |
| `num_images` | No | 1 – 4 | `1` |
| `output_format` | No | `"jpeg"`, `"png"`, `"webp"` | `"png"` |
| `sync_mode` | No | bool — return base64 data URI inline | `false` |
### Image edit (`openai/gpt-image-2/edit`)
| Param | Required | Options | Default |
|-------|----------|---------|---------|
| `prompt` | Yes | string — describe the edit | — |
| `image_urls` | Yes | array of public image URLs | — |
| `image_size` | No | preset, `{width, height}`, or `"auto"` | `"auto"` (infer from input) |
| `quality` | No | `"low"`, `"medium"`, `"high"` | `"medium"` |
| `num_images` | No | 1 – 4 | `1` |
| `output_format` | No | `"jpeg"`, `"png"`, `"webp"` | `"png"` |
| `sync_mode` | No | bool — return base64 data URI inline | `false` |
| `mask_url` | No | URL of B/W mask (white = edit, black = keep) | — |
### Image sizes
**Preset names:** `square_hd`, `square`, `portrait_4_3`, `portrait_16_9`, `landscape_4_3`, `landscape_16_9`.
**Custom dimensions** — pass an object:
```json
"image_size": { "width": 1536, "height": 1024 }
```
Constraints on custom sizes:
- Both dimensions must be multiples of **16**
- Max edge: **3840px**
- Aspect ratio: **≤ 3:1**
- Total pixels between **655,360 and 8,294,400** (~640×1024 min, ~3840×2160 max)
---
## Pricing
Token-based, billed via Fal:
| Token type | Input | Cached | Output |
|------------|-------|--------|--------|
| Text | $5 / 1M | $1.25 / 1M | $10 / 1M |
| Image | $8 / 1M | $2 / 1M | $30 / 1M |
**Per-image rough cost** (landscape_4_3 = 1536×1024):
- `low` ≈ $0.02 per image
- `medium` ≈ $0.05 per image ← **default**
- `high` ≈ $0.18 per image
`quality` is the biggest cost lever. Default to `medium` — that's the sweet spot for most work. Bump to `high` only when typography/detail really matters and the image is a final. `low` for cheap drafts. Check actual usage in the Fal dashboard.
---
## When to use vs other models
| Scenario | Endpoint | Notes |
|----------|----------|-------|
| Sign / chalkboard / poster with text | text-to-image | Best in class for typography |
| Magazine cover / book cover | text-to-image | |
| Menu board / packaging mockup | text-to-image | |
| UI screenshot mockup | text-to-image | |
| Modify an existing image ("change the sky", "swap the logo", "add text") | edit | Pass image as `image_urls`, describe the change |
| Surgical region edit ("only change this corner") | edit + `mask_url` | Mask defines the editable region |
| Iterative refinement | edit | Feed previous output URL back in |
| Photoreal portrait | other | Use Nano Banana / Flux |
| Style transfer with strong style preservation | other | GPT Image 2 edit reinterprets more than it preserves |
| Need < $0.02 per render | other | Switch model — even `low` quality here is comparable to other models' default |
---
## Recipe — Node.js
One helper, both endpoints. Drop in a `.js` file and run with `node`. Forward slashes in paths work fine on Windows.
```javascript
import fs from 'node:fs';
import path from 'node:path';
const FAL_KEY = process.env.FAL_KEY ||
fs.readFileSync('.env', 'utf8').match(/FAL_KEY=(.+)/)[1].trim();
// Pass image_urls to use the edit endpoint; omit for text-to-image.
async function gptImage2({
prompt,
image_urls,
mask_url,
image_size,
quality = 'medium',
num_images = 1,
output_format = 'png',
outDir = './output',
label = 'gpt2',
}) {
fs.mkdirSync(outDir, { recursive: true });
const isEdit = Array.isArray(image_urls) && image_urls.length > 0;
const url = isEdit
? 'https://fal.run/openai/gpt-image-2/edit'
: 'https://fal.run/openai/gpt-image-2';
const body = { prompt, quality, num_images, output_format };
if (isEdit) {
body.image_urls = image_urls;
body.image_size = image_size ?? 'auto';
if (mask_url) body.mask_url = mask_url;
} else {
body.image_size = image_size ?? 'landscape_4_3';
}
const res = await fetch(url, {
method: 'POST',
headers: { 'Authorization': `Key ${FAL_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
const json = await res.json();
if (!json.images?.length) throw new Error(`No images returned: ${JSON.stringify(json)}`);
const ts = Date.now();
const saved = [];
for (let i = 0; i < json.images.length; i++) {
const suffix = json.images.length > 1 ? `_${i + 1}` : '';
const fname = `${label}_${ts}${suffix}.${output_format}`;
const fp = path.join(outDir, fname);
const buf = Buffer.from(await (await fetch(json.images[i].url)).arrayBuffer());
fs.writeFileSync(fp, buf);
saved.push(fp);
console.log(`Saved: ${fp}`);
}
return saved;
}
// Text-to-image
await gptImage2({
prompt: 'A chalkboard sign on a brick wall, hand-lettered: "OPEN AT 7"',
label: 'chalkboard',
});
// Edit an existing image
await gptImage2({
prompt: 'Same scene but everyone is on their phone now. One taking a selfie, one on a call.',
image_urls: ['https://v3b.fal.media/files/b/.../original.png'],
label: 'phones_edit',
});
```
## Recipe — Python
One helper, both endpoints. Pass `image_urls` for the edit variant; omit for text-to-image.
```python
import os, time, requests, pathlib
GitHub에서 보기