If you need to download a ChatGPT image from a link, save the file while the link still opens. ChatGPT image URLs are temporary, and a gen_id cannot turn an expired link back into image bytes.
Use the recovery path that matches what you still have:
| What you still have | Best recovery path | Expected result |
|---|---|---|
| A working ChatGPT image link or the original conversation | Open the image, download the bytes, then re-host the file | The original image can usually be preserved |
A complete ChatGPT export with conversations.json and asset files | Match the file-service:// pointer to an exported file | The image can be recovered if its bytes are in the archive |
An expired backend-api or oaiusercontent link | Return to the original chat, check a complete export, or regenerate | Editing the old URL will not renew it |
Only a gen_id | Recreate the image from the prompt or saved context | The original pixels cannot be retrieved from the id |
| A repeatable automated workflow | Generate through a documented API, download the output, and store your own URL | Future files are kept under your control |
The important distinction is between a link that still authorizes a download and identifiers that only describe the conversation. A working signed link can deliver bytes. A gen_id cannot.
Download from a working ChatGPT image link
If the original conversation still exists, open the generated image and use ChatGPT's download control. If you copied the image link instead, open the complete link without removing its query string and save the response immediately.
Before treating the download as successful, verify that the response:
- returns HTTP 200;
- has an image content type such as
image/pngorimage/webp; - has a plausible file size; and
- opens as the expected image.
A link that works only in your current browser is not a durable application URL. The browser may still have the right session or a fresh signature. Download the bytes and upload them to storage you control instead of publishing the ChatGPT link.
Why ChatGPT image links expire
ChatGPT may store several references around one generated image:
gen_ididentifies a generation inside a conversation.file-service://file-...is an internal asset pointer, not an HTTP address.chatgpt.com/backend-api/...andfiles.oaiusercontent.com/...are temporary signed pickup URLs.
These references serve different internal jobs. None is a permanent public file URL.
A copied link can fail in two common ways:
HTTP 422
missing query fields such as p or ts
The URL was copied without all signed query parameters, or another tool stripped them while pasting.
HTTP 404
ResourceNotFound
The signature or underlying asset has expired, the file was deleted, or the request no longer has the context that issued the link. Reconstructing a plausible path from gen_id does not restore the signature or the image.
Recover an image from a complete ChatGPT data export
A complete export can contain conversations.json plus separate asset files. The JSON may describe an image like this:
{
"content_type": "image_asset_pointer",
"asset_pointer": "file-service://file-A1b2C3d4E5f6",
"size_bytes": 1417238,
"width": 1024,
"height": 1024,
"metadata": {
"dalle": {"gen_id": "aBcD3fGh1jKl"}
}
}
The file-... value after file-service:// is the useful join key. The gen_id is not a filename or download credential.
1. Extract the entire archive
Keep the directory structure intact. Opening only conversations.json removes the asset files needed for the other half of the match.
2. Find the image pointer
Search conversations.json for "content_type": "image_asset_pointer", locate the conversation and image you need, and copy the full asset_pointer value.
3. Extract the file id
Remove only the internal scheme:
file-service://file-A1b2C3d4E5f6
└─ file-A1b2C3d4E5f6
Do not replace file-service:// with https:// or try to curl it. It is an internal URI scheme, not a public host.
4. Search the exported files
On macOS or Linux:
find . -type f -iname '*file-A1b2C3d4E5f6*'
If the archive renamed or nested assets differently, search for the distinctive id segment. Compare candidate files with size_bytes, width, and height when those fields are available.
5. Verify and re-host the file
Open the candidate locally and confirm it is the expected image. Copy the verified bytes to your own S3, R2, CDN, or application storage and record that durable URL.
For many pointers, this local script lists the unique asset ids in an export without contacting ChatGPT:
import json
from pathlib import Path
data = json.loads(Path("conversations.json").read_text())
seen = set()
def walk(value):
if isinstance(value, dict):
if value.get("content_type") == "image_asset_pointer":
pointer = value.get("asset_pointer", "")
if pointer.startswith("file-service://"):
seen.add(pointer.removeprefix("file-service://"))
for child in value.values():
walk(child)
elif isinstance(value, list):
for child in value:
walk(child)
walk(data)
for file_id in sorted(seen):
print(file_id)
A pointer proves that the conversation once referred to an asset. It does not guarantee that the export contains the bytes. If the fully extracted archive has no matching file, neither file-service:// nor gen_id can reconstruct the original pixels.
When only the gen_id remains
There is no public conversion from gen_id to PNG, WebP, or a fresh signed URL. If the original conversation, working link, export asset, and file are gone, regenerate from the prompt and any saved context.
Keep the failed identifier only as diagnostic metadata. For images your application must retain, store:
- the prompt and generation settings;
- the provider and model;
- the provider task id for support and tracing; and
- your final object key or durable storage URL.
Build an automated download workflow
Use an API designed to return output files. With hiapi's async task API, the stable workflow is create, poll, download, verify, and re-host:
curl -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-2/text-to-image",
"input": {
"prompt": "a paper crane on a desk in soft morning light",
"aspect_ratio": "1:1",
"resolution": "1K"
}
}'
Poll GET /v1/tasks/{taskId} until the task succeeds, then read data.output[0].url and expireAt:
curl -s "https://api.hiapi.ai/v1/tasks/$TASK_ID" \
-H "Authorization: Bearer $HIAPI_API_KEY"
curl -s -o generated-image.webp "<data.output[0].url>"
The API output URL is also temporary, but its expiry is part of the documented contract. Download it before expiry, reject HTML or JSON error bodies, and upload the verified bytes to your own storage.
For model inputs and current pricing, use the gpt-image-2 model page and live pricing page. The task creation reference documents the request envelope.
A storage pattern that survives expiring links
For each successful task:
- Save the task id before polling so a worker retry does not create and bill a duplicate task.
- Download the output while its signed URL is valid.
- Check status, content type, and file size before accepting the response as an image.
- Upload the verified bytes under an object key you control.
- Persist your object URL and keep the temporary source URL only for short-lived diagnostics.
FAQ
How do I download an image from a ChatGPT link?
Open the complete link while it still works, keep its signed query parameters, download the image bytes, and re-host the file. A link that opens only in your logged-in browser should be treated as temporary.
Is there an official gen_id download endpoint?
No. ChatGPT does not expose a public endpoint that accepts gen_id and returns the generated image. The identifier only has meaning in its original conversation context.
Why does a ChatGPT image URL return 422?
The signed link is usually missing required query fields. Copying only the path, or using a tool that strips the query string, invalidates it.
Why does the same image URL later return 404?
Signed URLs and underlying assets are temporary. After the link or file expires, ResourceNotFound is expected and gen_id cannot renew it.
How do I recover a file-service:// image from an export?
Copy the file id after file-service:// and search the fully extracted archive for a filename containing that id. Use file size and dimensions as secondary checks.
How do I create a permanent image URL?
Download the bytes while a valid source exists, upload them to storage you control, and save your own URL. Permanence comes from your storage policy, not the provider's pickup link.







