Generating text from an API is easy. Generating image assets is significantly harder simply because of file size and latency. When you make a request to DALL-E 3, the payload getting returned isn't a 2kb string of text. It is a massive binary blob.
Most developers read the OpenAI documentation, copy the basic snippet, and accidentally build a devastating architectural flaw: they ask the API for a temp URL, pass that URL to the frontend, and realize 2 hours later that the URL expired and their web app is displaying broken images.
In this tutorial, we will build the correct, production-ready backend pipeline using Python. We will request a b64_json image, decode it locally, and prepare it for permanent storage (like AWS S3) before we ever send it to the frontend.
The Production Pattern
The flow must look like this to prevent data loss:
- User clicks "Generate" on your frontend UI.
- Frontend sends the text prompt to your Python backend.
- Your Backend modifies the prompt (using hidden system constraints so the user can't generate inappropriate content) and sends it to OpenAI.
- OpenAI returns a raw base64 string.
- Your Backend decodes the string, saves the PNG file to your Cloud Storage (S3, Cloudflare R2), and gets a permanent, public URL.
- Your Backend returns that permanent URL to the User frontend.
Step 1: The Basic DALL-E 3 Call
Assume you have your environment set up with openai and python-dotenv installed (as covered in our ChatGPT API Guide). DALL-E 3 costs $0.04 per HD image. Do not put this function on an open internet port without user authentication, or a bot will bankrupt you.
import os
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
client = OpenAI()
def generate_image_basic(prompt):
print("Requesting DALL-E 3...")
response = client.images.generate(
model="dall-e-3",
prompt=prompt,
size="1024x1024",
quality="standard",
n=1,
)
# By default, OpenAI returns a URL that expires in roughly 1 hour.
image_url = response.data[0].url
return image_url
if __name__ == "__main__":
url = generate_image_basic("A futuristic neon coffee mug, unreal engine 5 render.")
print("EXPIRING URL:", url)
If you use that URL in an <img src=""> tag, your app will look great today, and broken tomorrow. We need to fix this.
Step 2: Requesting Base64 Data
We need to explicitly tell the API to return the raw binary data of the image instead of a link. We change the response_format parameter.
import base64
def generate_image_base64(prompt):
print("Requesting Base64 from DALL-E 3...")
response = client.images.generate(
model="dall-e-3",
prompt=prompt,
size="1024x1024",
quality="standard",
response_format="b64_json", # Crucial change
n=1,
)
# We now have a massive string of encoded binary data
b64_data = response.data[0].b64_json
return b64_data
Step 3: Decoding and Saving Locally
Once your backend receives the b64_data, it needs to be decoded into bytes, and then written into a file stream. Here is the utility function to do that and save it to your server's local disk.
import base64
import uuid
def save_b64_to_disk(b64_data, output_folder="generated_images"):
# Ensure folder exists
os.makedirs(output_folder, exist_ok=True)
# Generate a random filename so we don't overwrite previous images
filename = f"dalle_{uuid.uuid4().hex[:8]}.png"
filepath = os.path.join(output_folder, filename)
# Decode the base64 string
image_bytes = base64.b64decode(b64_data)
# Write the bytes to a PNG file
with open(filepath, "wb") as f:
f.write(image_bytes)
print(f"Image saved permanently to: {filepath}")
return filepath
Step 4: The Production Architecture (Putting it together)
Now, let's wire it up as if we were fulfilling an API request from an Express or React app. We intercept the prompt, modify it for safety, get the Base64, save it, and simulate returning the path to the user.
def handle_frontend_request(user_prompt):
# 1. Enforce business logic (Prompt Injection Defense)
safe_prompt = f"A cute 3D Pixar style illustration of: {user_prompt}. No text. Safe for work."
# 2. Call OpenAI
try:
b64_raw = generate_image_base64(safe_prompt)
except Exception as e:
return {"status": "error", "message": f"API Failed: {e}"}
# 3. Save the image to permanent storage
# In a real app, you would replace this with a boto3 call to AWS S3.
local_path = save_b64_to_disk(b64_raw)
# 4. Return the permanent path to the frontend
return {
"status": "success",
"url": f"https://my_app.com/{local_path}" # Example domain
}
if __name__ == "__main__":
result = handle_frontend_request("A golden retriever holding a coffee cup")
print(result)
Conclusion
Treat LLM image generation APIs exactly how you treat user uploads. The image does not exist until it is safely written to a hard drive you control. By handling the Base64 decoding directly in your backend, you eliminate broken image links, prevent users from seeing the raw OpenAI URLs, and secure the intellectual property of your generations.