Overview
This short guide explains how use python to generate an image using RunPod.
What it does
- Creates a RunPod job using the
/runendpoint with a text prompt. - Polls the
/status/{id}endpoint until the job reaches COMPLETED. - Reads
output.image_url(adata:image/png;base64,...string), decodes the base64 payload, and saves it as{job_id}.png(or into--outdir).
The script is organized with small functions:
create_job(prompt, key, base_url)— create a job and return the API response JSON.poll_job(job_id, key, base_url, timeout)— poll until completion and return the final JSON payload.save_image_from_output(output, job_id, out_dir)— decode and write the PNG to disk.
Prerequisites
requestspackage (install withpip install -r requirements.txtorpip install requests)- A RunPod API key
Set your key using an environment variable:
export RUNPOD_BASE_URL="https://api.runpod.ai/v2/..."
export RUNPOD_KEY="rpa_..."
Tip: You can also pass
--keyon the command line to override the env var.
Usage examples
Create a new job (script will create, poll, and save image):
python main.py --prompt "an astronaut riding a horse on Mars" --outdir ./images
Poll an existing job (skip creation):
python main.py --id 7acd81e5-57ee-4d2b-8d94-8f970176fe9a-e2 --outdir ./images
Control polling timeout (seconds):
python main.py --timeout 120
Adjust logging verbosity:
export LOG_LEVEL=DEBUG
python main.py --prompt "..."
How it all works
- The script expects the RunPod status API response to include at least the following JSON shape:
{
"id": "...",
"status": "IN_QUEUE|RUNNING|COMPLETED|ERROR",
"delayTime": 1168,
"output": {
"image_url": "data:image/png;base64,iVBORw0..."
}
}
- On COMPLETED,
output.image_urlmust contain adata:image/png;base64,prefixed base64 string — the script strips the prefix and decodes the rest. - On FAILED or ERROR, the script raises an error and logs details.
- Polling honors a
delayTimevalue if supplied (converted from ms to seconds) or falls back to a default interval.
Example code is :
import argparse
import base64
import logging
import os
import sys
import time
from typing import Optional, Dict, Any
import requests
# Configuration
DEFAULT_BASE_URL = os.environ.get("RUNPOD_BASE_URL", "https://api.runpod.ai/v2/XXXX")
DEFAULT_TIMEOUT = 600 # seconds
# Try to read key from environment first; fallback to hard-coded value if present
KEY = os.environ.get("RUNPOD_KEY", "rpa_XXXX")
# Logging
logging.basicConfig(
level=os.environ.get("LOG_LEVEL", "INFO"),
format="%(asctime)s %(levelname)s %(name)s - %(message)s",
)
logger = logging.getLogger(__name__)
def create_job(prompt: str, key: Optional[str] = None, base_url: str = DEFAULT_BASE_URL) -> Dict[str, Any]:
key = key or KEY
headers = {"Content-Type": "application/json", "Authorization": f"Bearer {key}"}
data = {"input": {"prompt": prompt}}
logger.info("Creating job with prompt: %s", prompt)
r = requests.post(f"{base_url}/run", headers=headers, json=data)
r.raise_for_status()
return r.json()
def poll_job(job_id: str, key: Optional[str] = None, base_url: str = DEFAULT_BASE_URL, timeout: int = DEFAULT_TIMEOUT) -> Dict[str, Any]:
key = key or KEY
headers = {"Content-Type": "application/json", "Authorization": f"Bearer {key}"}
status_url = f"{base_url}/status/{job_id}"
start = time.time()
while True:
r = requests.get(status_url, headers=headers)
r.raise_for_status()
status_json = r.json()
status = status_json.get("status")
logger.info("Job %s status: %s", job_id, status)
if status == "COMPLETED":
logger.info("Job %s completed", job_id)
return status_json
if status in ("FAILED", "ERROR"):
logger.error("Job %s ended with status: %s", job_id, status)
raise RuntimeError(f"Job ended with status: {status}")
elapsed = time.time() - start
if elapsed > timeout:
logger.error("Polling timed out after %s seconds for job %s", timeout, job_id)
raise TimeoutError("Polling timed out")
delay = status_json.get("delayTime")
if isinstance(delay, (int, float)) and delay > 0:
wait = min(1, delay / 1000.0 + 0.5)
else:
wait = 3
time.sleep(wait)
def save_image_from_output(output: Dict[str, Any], job_id: str, out_dir: Optional[str] = None) -> str:
image_url = output.get("image_url")
if not image_url:
raise RuntimeError("No 'image_url' found in output")
prefix = "data:image/png;base64,"
if image_url.startswith(prefix):
b64 = image_url[len(prefix):]
else:
b64 = image_url
imgdata = base64.b64decode(b64)
if out_dir:
os.makedirs(out_dir, exist_ok=True)
out_file = os.path.join(out_dir, f"{job_id}.png")
else:
out_file = f"{job_id}.png"
with open(out_file, "wb") as f:
f.write(imgdata)
logger.info("Saved image to %s", out_file)
return out_file
def main(argv=None):
parser = argparse.ArgumentParser(description="Run a RunPod job and save returned image")
parser.add_argument("--prompt", default="create an image of a futuristic cityscape at sunset", help="Prompt to send to the model")
parser.add_argument("--key", help="RunPod API key (overrides env var)")
parser.add_argument("--id", help="If provided, skip creating a job and poll this job id instead")
parser.add_argument("--timeout", type=int, default=DEFAULT_TIMEOUT, help="Polling timeout in seconds")
parser.add_argument("--outdir", help="Directory to save the resulting image")
parser.add_argument("--base-url", default=DEFAULT_BASE_URL, help="Base URL for the RunPod API")
args = parser.parse_args(argv)
key = args.key or KEY
if not key:
logger.error("No API key provided (set RUNPOD_KEY env or use --key)")
sys.exit(1)
try:
if args.id:
job_id = args.id
logger.info("Using provided job id: %s", job_id)
else:
resp = create_job(args.prompt, key=key, base_url=args.base_url)
logger.debug("Create response: %s", resp)
job_id = resp.get("id")
if not job_id:
logger.error("No job id returned from run endpoint")
sys.exit(1)
status_json = poll_job(job_id, key=key, base_url=args.base_url, timeout=args.timeout)
output = status_json.get("output", {})
save_image_from_output(output, job_id, out_dir=args.outdir)
except Exception:
logger.exception("An error occurred")
raise
if __name__ == "__main__":
main()
Documentation and links:
- RunPod API Reference - https://docs.runpod.io/api-reference/overview