
You dug into ChatGPT's conversation JSON — a data export, or the backend-api/conversation payload in DevTools — looking for your generated images. What you found instead was this:
{
"content_type": "image_asset_pointer",
"asset_pointer": "file-service://file-A1b2C3d4E5f6",
"size_bytes": 1417238,
"width": 1024,
"height": 1024,
"metadata": { "dalle": { "gen_id": "aBcD3fGh1jKl", "seed": 92014, "...": "..." } }
}
No .png, no https:// link. And every obvious way to turn those identifiers into pixels fails:
$ curl "file-service://file-A1b2C3d4E5f6"
curl: (1) Protocol "file-service" not supported
HTTP 404
<Error><Code>ResourceNotFound</Code>
<Message>The specified resource does not exist.</Message></Error>
Here is what each identifier actually is, why none of them download anything, and the routes that do work.
Ordered by how often each one is the wall people hit:
file-service:// is an internal URI scheme, not a network protocol. It's how ChatGPT's backend names a stored file internally. No HTTP client can resolve it — curl exits immediately with the Protocol "file-service" not supported error above, and a browser address bar just searches for the text. Only ChatGPT's own backend can exchange that pointer for a real link, inside your authenticated session. The scheme is an implementation detail, and internal details like this change without notice.gen_id is generation metadata, not a file handle. It exists so the model can refer back to a previous generation inside the same conversation ("same scene, but at night"). There is no public endpoint anywhere that accepts a gen_id and returns an image — we tested the plausible candidates in ChatGPT image gen_id: how to retrieve and download generated images.asset_pointer for a signed download URL (a chatgpt.com/backend-api/... link that redirects to files.oaiusercontent.com/...). Those links carry timestamp + signature query params and are scoped to your session: strip the params and you get an HTTP 422 listing the missing fields; let them age and you get the HTTP 404 ResourceNotFound above. Full breakdown of those two failure modes: ChatGPT image gen_id URLs explained.conversations.json plus asset files named by file id, so file-service://file-A1b2C3d4E5f6 matches a file-A1b2C3d4E5f6-*.png in the archive. Caveat: exports cover what's still stored — images from old conversations whose assets have already expired server-side can be missing.gen_id? Regenerate instead of excavating. A gen_id cannot be converted into pixels after the fact. If the image mattered, reproduce it from the prompt — and if this keeps happening, that's the sign your workflow belongs on an API.gpt-image-2 — the API line of the model behind ChatGPT's image generation — is available through the unified task endpoint POST /v1/tasks (see the gpt-image-2 model page). The task result hands you a downloadable output[0].url with an explicit expireAt — a documented pickup window instead of a reverse-engineered one. The standard text-to-image line is $0.03 per image at 1K resolution (live pricing).Create a task with the bare model id (gpt-image-2 — appending pipeline suffixes like /text-to-image to the id returns a 400):
curl -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-2",
"input": {"prompt": "a paper crane on a desk, morning light", "aspect_ratio": "1:1", "resolution": "1K"}
}'
Success returns {"code":200,"data":{"taskId":"..."}}. If you instead get HTTP 401 with:
{"error":{"code":"permission_denied","type":"hiapi_error","request_id":"..."}}
your key is missing, wrong, or not enabled for the model — see the authentication docs and check your key in the dashboard.
Then poll the task and download the output as soon as it succeeds:
curl -s https://api.hiapi.ai/v1/tasks/$TASK_ID -H "Authorization: Bearer $HIAPI_KEY"
# when "status":"success": download output[0].url before its expireAt
curl -s -o crane.png "<output[0].url>"
Notice the contrast with the JSON archaeology above: the URL is part of the response contract, the expiry is an explicit field, and nothing depends on a browser session. (Yes, hiapi's output URL expires too — that's deliberate, and the fix is the same re-host habit: download before expireAt.)
What is asset_pointer in ChatGPT's conversation JSON?
An internal reference to a stored file, written as a file-service://file-... URI. The ChatGPT app exchanges it for a temporary signed download URL inside your session. Outside that session it resolves to nothing.
Can I convert a gen_id into an image URL?
No. gen_id identifies a generation for in-conversation follow-ups (edits, variations). No public endpoint resolves it to a file, and it isn't a parameter on any image API.
Why does the files.oaiusercontent.com link from my export/JSON return 404 ResourceNotFound?
It's a signed URL that has expired or lost its query parameters. Those links authenticate with embedded timestamp + signature params and are meant for immediate, in-session use only.
Does the ChatGPT data export include generated images?
Generally yes for recent conversations — asset files sit in the zip named by file id, matching the asset_pointer values in conversations.json. Assets that already expired server-side can be absent from the export.
Is there an API that accepts a gen_id or asset pointer?
No. For programmatic image generation, call an image API directly. On hiapi that's POST /v1/tasks with model gpt-image-2: submit a prompt, poll the task, download output[0].url before expireAt.
How do I get a permanent URL for a generated image? Host it yourself. Download the bytes (from the ChatGPT UI, the export, or an API output URL) and upload them to your own bucket or CDN. Every provider-side URL — ChatGPT's or an API's — is a pickup token, not hosting.