You call the DALL·E or GPT Image API, get back a URL, drop it in your app — and an hour later it's a broken image. This surprises almost everyone the first time. The URLs image-generation APIs hand you are short-lived by design: OpenAI's image URLs expire roughly 60 minutes after generation. If you want the image to stick around, hosting it is your job.
The usual advice — "download it and upload it to S3" — means IAM policies, a bucket, a CDN in front, and egress bills for every view. That's a lot of yak-shaving to make one generated image durable. Here's the lean version instead.
The fix: grab the bytes, upload once
Fetch the generated image before its URL expires, then hand the bytes to PixelVault in a single POST. You get back a permanent CDN URL — no S3 bucket, no CloudFront distribution, no egress bill to wire up.
# 1. Grab the bytes before it expires:
curl -s -o gen.png "https://oaidalleapiprodscus.blob.core.windows.net/.../gen.png"
# 2. Upload to PixelVault — permanent CDN URL back:
curl -s -X POST https://api.pixelvault.dev/v1/images \
-H "Authorization: Bearer pv_live_xxx" \
-F "file=@gen.png" | jq -r '.url'
# → https://img.pixelvault.dev/proj_abc/img_xyz.png — never expires
That returned URL is served from a global edge CDN, has zero egress fees, and never expires. Register once for an API key, and re-hosting each new generation is one upload call.
When your model returns base64, not a URL
GPT Image can return b64_json, and local Stable Diffusion pipelines hand you raw bytes. There's nothing to download — you already have the image. Decode it and upload the bytes directly:
# Send the bytes straight to PixelVault — no temp file, no S3 setup.
const b64 = gen.data[0].b64_json;
const bytes = Buffer.from(b64, "base64");
const form = new FormData();
form.append("file", new Blob([bytes], { type: "image/png" }), "gen.png");
const res = await fetch("https://api.pixelvault.dev/v1/images", {
method: "POST",
headers: { "Authorization": `Bearer ${process.env.PIXELVAULT_API_KEY}` },
body: form,
});
const { url } = await res.json(); // permanent CDN URL
Works with every generator
The pattern is the same whichever model you use — download the bytes if you got a URL, upload them directly if you already have them:
- DALL·E 3 / GPT Image (OpenAI). URLs expire in ~1 hour, or use
b64_json. Re-host either way. - Midjourney. CDN links can rot and are tied to your account. Persist the ones you want to keep under your own URL.
- Flux / Nano Banana (via Replicate, fal, etc.). Output URLs on inference hosts are temporary — re-host on generation.
- Stable Diffusion (local or hosted). Raw bytes out; upload them directly and get a shareable link back.
Why this matters for AI agents
If an agent is generating images — thumbnails, mockups, marketing assets — a URL that expires in an hour is useless downstream. The agent needs a durable link it can put in a PR, a doc, or a database. Because PixelVault registration and upload are both plain API calls, an agent can host a generated image with no browser and no human in the loop. It's the same two calls, just made by the model instead of you.
Free to start
PixelVault's free tier includes 200 MB storage, 500 uploads/month, and 1 GB bandwidth — no credit card, no trial expiry. Enough to persist a lot of generated images before you pay a cent. Paid plans start at $9/month.