AI Image API: Python Tutorial (Free)
You can generate AI images from Python in under 10 lines of code. No GPU required, no complex model setup, no dependency headaches. Just send a POST request to an API endpoint and get an image URL back. This tutorial walks through every pattern you need: basic generation, error handling, async workflows, batch processing, and saving images to disk.
We will use the ZSky AI API, which offers 100 free calls per day with no credit card. The same patterns apply to any REST-based image generation API, but ZSky AI is the easiest to start with because there is zero setup friction.
Prerequisites
You need Python 3.7+ and the requests library. If you do not have requests installed:
pip install requests
For async examples later in this tutorial, you will also need:
pip install aiohttp
Step 1: Your First Image Generation
Here is the simplest possible example. This generates a single image from a text prompt and prints the URL where you can download it.
import requests
API_KEY = "sk_live_your_api_key_here"
API_URL = "https://api.zsky.ai/v1/generate/image"
response = requests.post(
API_URL,
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"prompt": "A mountain lake at sunrise, photorealistic",
"width": 1024,
"height": 1024
}
)
data = response.json()
print(data["image_url"]) # Direct download link, valid 24 hours
That is it. Seven lines of meaningful code. The API handles all the heavy computation on GPU servers and returns a CDN-hosted image URL. You do not need to install any AI frameworks, download model weights, or configure CUDA drivers.
Step 2: Save the Image to Disk
Most workflows need the image as a local file. Here is how to download and save it:
import requests
API_KEY = "sk_live_your_api_key_here"
# Generate the image
response = requests.post(
"https://api.zsky.ai/v1/generate/image",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"prompt": "Cyberpunk street market at night, neon lights, rain",
"width": 1024,
"height": 768,
"output_format": "png"
}
)
image_url = response.json()["image_url"]
# Download and save
image_data = requests.get(image_url)
with open("generated_image.png", "wb") as f:
f.write(image_data.content)
print("Saved to generated_image.png")
Step 3: Error Handling
Production code needs to handle API errors gracefully. The API returns standard HTTP status codes and detailed error messages.
import requests
import time
API_KEY = "sk_live_your_api_key_here"
API_URL = "https://api.zsky.ai/v1/generate/image"
def generate_image(prompt, retries=3):
for attempt in range(retries):
try:
response = requests.post(
API_URL,
headers={"Authorization": f"Bearer {API_KEY}"},
json={"prompt": prompt, "width": 1024, "height": 1024},
timeout=60
)
if response.status_code == 200:
return response.json()["image_url"]
elif response.status_code == 429:
# Rate limited -- exponential backoff
wait = 2 ** attempt
print(f"Rate limited. Waiting {wait}s...")
time.sleep(wait)
elif response.status_code == 400:
# Bad request -- do not retry
print(f"Bad request: {response.json()['error']}")
return None
else:
print(f"Error {response.status_code}: {response.text}")
time.sleep(1)
except requests.exceptions.Timeout:
print(f"Timeout on attempt {attempt + 1}")
except requests.exceptions.ConnectionError:
print(f"Connection error on attempt {attempt + 1}")
time.sleep(2)
return None
# Usage
url = generate_image("A serene Japanese garden in autumn")
if url:
print(f"Generated: {url}")
else:
print("Generation failed after retries")
Step 4: Generate Multiple Images
When you need to generate many images, there are two approaches: the batch endpoint (up to 50 in one call) or concurrent requests with asyncio.
Batch Endpoint
import requests
API_KEY = "sk_live_your_api_key_here"
prompts = [
"Sunset over the ocean, photorealistic",
"Snow-capped mountains at dawn",
"Dense forest with morning fog",
"Desert dunes under starlight",
"Tropical waterfall in rainforest"
]
response = requests.post(
"https://api.zsky.ai/v1/batch",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"requests": [
{"prompt": p, "width": 1024, "height": 1024}
for p in prompts
]
}
)
results = response.json()["results"]
for i, result in enumerate(results):
print(f"Image {i+1}: {result['image_url']}")
Async Concurrent Requests
import asyncio
import aiohttp
API_KEY = "sk_live_your_api_key_here"
API_URL = "https://api.zsky.ai/v1/generate/image"
async def generate(session, prompt):
async with session.post(
API_URL,
headers={"Authorization": f"Bearer {API_KEY}"},
json={"prompt": prompt, "width": 1024, "height": 1024}
) as response:
data = await response.json()
return data["image_url"]
async def main():
prompts = [
"Abstract art with bold colors",
"Minimalist landscape, pastel palette",
"Portrait in Renaissance style",
"Sci-fi cityscape at twilight",
]
async with aiohttp.ClientSession() as session:
tasks = [generate(session, p) for p in prompts]
urls = await asyncio.gather(*tasks)
for url in urls:
print(url)
asyncio.run(main())
The async approach is faster when you are making many individual requests because all requests run concurrently instead of waiting for each one to finish.
Step 5: Advanced Parameters
The API accepts several parameters that give you control over the output:
| Parameter | Type | Description |
|---|---|---|
prompt | string | Text description of the image to generate (required) |
negative_prompt | string | What to avoid in the output |
width | int | Image width: 512, 768, 1024, 2048 |
height | int | Image height: 512, 768, 1024, 2048 |
guidance_scale | float | Prompt adherence (1.0 to 20.0, default 7.5) |
seed | int | Reproducible results with same seed |
output_format | string | png, jpeg, or webp |
style | string | photorealistic, stylized, anime, illustration |
response = requests.post(
"https://api.zsky.ai/v1/generate/image",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"prompt": "A cozy cabin in the woods, warm light from windows",
"negative_prompt": "blurry, low quality, distorted",
"width": 1024,
"height": 768,
"guidance_scale": 8.0,
"seed": 42,
"output_format": "webp",
"style": "photorealistic"
}
)
Using the same seed with the same prompt and parameters produces identical results, which is useful for reproducible workflows and A/B testing.
Step 6: Build a Flask Wrapper
Here is a minimal Flask app that turns the API into your own image generation microservice:
from flask import Flask, request, jsonify
import requests as http
app = Flask(__name__)
API_KEY = "sk_live_your_api_key_here"
@app.route("/generate", methods=["POST"])
def generate():
prompt = request.json.get("prompt")
if not prompt:
return jsonify({"error": "prompt required"}), 400
resp = http.post(
"https://api.zsky.ai/v1/generate/image",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"prompt": prompt,
"width": request.json.get("width", 1024),
"height": request.json.get("height", 1024)
},
timeout=60
)
if resp.status_code == 200:
return jsonify(resp.json())
return jsonify({"error": "generation failed"}), 500
if __name__ == "__main__":
app.run(port=8080)
This gives you a clean internal API that your frontend or other services can call without needing to know the ZSky AI authentication details.
Start Generating Images with Python
100 free API calls per day. No credit card. Get your API key and start building in minutes.
Get Free API Key →Common Patterns and Tips
Environment Variables for API Keys
Never hardcode API keys in source code. Use environment variables:
import os
API_KEY = os.environ["ZSKY_API_KEY"]
# Set in terminal: export ZSKY_API_KEY=sk_live_your_key
Polling for Async Jobs
For high-resolution images or video, the API returns a job ID. Poll the status endpoint until it completes:
import time
# Submit the job
resp = requests.post(
"https://api.zsky.ai/v1/generate/image",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"prompt": "Epic landscape", "width": 2048, "height": 2048}
)
job_id = resp.json()["job_id"]
# Poll until done
while True:
status = requests.get(
f"https://api.zsky.ai/v1/status/{job_id}",
headers={"Authorization": f"Bearer {API_KEY}"}
).json()
if status["status"] == "completed":
print(f"Done: {status['result_url']}")
break
elif status["status"] == "failed":
print(f"Failed: {status['error']}")
break
time.sleep(2) # Check every 2 seconds
Rate Limit Awareness
Every API response includes rate limit headers. Use them to avoid hitting limits:
remaining = int(response.headers.get("X-RateLimit-Remaining", 0))
reset_time = int(response.headers.get("X-RateLimit-Reset", 0))
if remaining < 5:
print(f"Only {remaining} requests left. Resets at {reset_time}")
Frequently Asked Questions
What Python libraries do I need to call an AI image API?
You only need the requests library, which comes pre-installed with most Python distributions. For async workflows, use aiohttp or httpx. No special SDK is required since ZSky AI uses a standard REST API that any HTTP client can call.
Is the ZSky AI image API free to use with Python?
Yes. ZSky AI offers 100 free API calls per day with no credit card required. This is enough for prototyping, personal projects, and testing. Paid plans start at $0.02 per image for higher volumes.
How do I handle errors when calling an AI image API from Python?
Check the HTTP status code of the response. 200 means success. 400 means bad request parameters. 429 means rate limited, so implement exponential backoff. 500 means server error, so retry after a short delay. Always wrap API calls in try-except blocks to handle network errors.
Can I generate multiple AI images in a single Python script?
Yes. Use the batch endpoint to send up to 50 prompts in one API call, or use asyncio with aiohttp to send concurrent requests. Both approaches are significantly faster than sequential single-image calls.
How long does it take to generate an AI image via API?
Standard resolution images typically generate in 3 to 8 seconds. Photorealistic and high-resolution images may take 10 to 20 seconds. The API returns a job ID immediately, and you can poll for the result or use a webhook callback.
Build AI-Powered Apps with Python
From prototypes to production. Generate images, videos, and edits with a simple REST API. Free to start.
Get API Access →