How to Generate 3D Assets with Leonardo AI

Building high-quality 3D assets and textures for game engines used to take days of manual sculpting and texture painting. With Leonardo AI's modern generation platform and REST API, you can automate asset creation directly inside your asset pipeline. Whether you need stylized props, height maps, or PBR texture sets, calling Leonardo AI programmatically yields rapid results.
In this guide, we will walk through setting up the API, building prompts tailored for 3D modeling pipelines, and scripting a complete Python tool that converts simple text descriptions into clean, textured mesh elements ready for your engine.
What Will You Learn From This Guide?
By the end of this tutorial, you will have a working Python script that interfaces with Leonardo AI to generate ready-to-use game props and material depth passes. Specifically, you will master:
- Authenticating with the Leonardo AI API using secure environment variables
- Configuring parameter payloads for consistent 3D style control
- Generating high-resolution PBR texture maps and depth maps
- Automating polling and file downloads for batch pipeline generation
- Troubleshooting API rate limits, HTTP status errors, and visual artifacts
We will build a modular script that can easily be integrated into custom Blender extensions, Unity import tools, or Unreal Engine automation plugins.
Prerequisites & What You Need
Before writing code, make sure you have your developer environment configured properly. Here is what you need on your machine:
- Python 3.11 or newer installed on your local system
- A active Leonardo AI API Key (available in your account settings under API Access)
- The
requestslibrary for sending API calls - The
pillowlibrary for client-side image processing - A code editor such as VS Code or Cursor
Set up your environment variables by running the following commands in your terminal:
pip install requests pillow pydantic
export LEONARDO_API_KEY="your_actual_api_key_here"
Developer Note: Never hardcode your API key into your scripts. If you push code to a public repository, exposed credentials can lead to account suspension or unexpected billing usage.
Step-by-Step Guide to Asset Generation
Follow these steps to set up your request pipeline and build a reliable generator.
Step 1: Set Up Authentication and Payload Structures
Create a file named asset_generator.py. Start by importing the required modules and defining helper functions to read environment variables.
import os
import time
import requests
from pathlib import Path
API_KEY = os.getenv("LEONARDO_API_KEY")
BASE_URL = "https://cloud.leonardo.ai/api/rest/v1"
if not API_KEY:
raise ValueError("LEONARDO_API_KEY environment variable is not set.")
HEADERS = {
"accept": "application/json",
"content-type": "application/json",
"authorization": f"Bearer {API_KEY}"
}
Step 2: Construct the Asset Prompt and Parameters
When generating 3D props and materials, your prompt structure matters immensely. You need flat lighting, clean backgrounds, and precise mechanical or organic detail specifications.
Here is how we construct a payload for generating a game prop image with an isolated alpha background look:
def build_payload(prompt_text, width=1024, height=1024):
return {
"height": height,
"width": width,
"modelId": "e31a6756-2917-4d6f-a949-d97a003f0d4f", # Leonardo Diffusion XL
"prompt": prompt_text,
"negative_prompt": "blurry, low quality, shadows, complex background, text, watermark, organic noise",
"num_images": 1,
"guidance_scale": 7,
"public": False,
"alchemy": True
}
Step 3: Trigger the Generation Request
Send a POST request to the /generations endpoint. The response returns a generationId, which you use to check the processing status.
def start_generation(payload):
endpoint = f"{BASE_URL}/generations"
response = requests.post(endpoint, json=payload, headers=HEADERS)
if response.status_code != 200:
raise Exception(f"API Request failed with status {response.status_code}: {response.text}")
data = response.json()
gen_id = data.get("sdGenerationJob", {}).get("generationId")
print(f"Generation job queued successfully. ID: {gen_id}")
return gen_id
Step 4: Poll Status and Retrieve Output
Image generation takes a few seconds to process. Write a loop that polls the status endpoint until the image is ready, then download the resulting asset.
def wait_and_download(generation_id, output_path="output.png"):
endpoint = f"{BASE_URL}/generations/{generation_id}"
for attempt in range(30):
response = requests.get(endpoint, headers=HEADERS)
if response.status_code == 200:
data = response.json()
gen_data = data.get("generations_by_pk", {})
status = gen_data.get("status")
if status == "COMPLETE":
images = gen_data.get("generated_images", [])
if images:
img_url = images[0].get("url")
print(f"Asset ready! Downloading from: {img_url}")
img_data = requests.get(img_url).content
with open(output_path, "wb") as f:
f.write(img_data)
print(f"Saved asset to {output_path}")
return output_path
elif status == "FAILED":
raise RuntimeError("Generation job failed on Leonardo servers.")
time.sleep(3)
raise TimeoutError("Generation request timed out after 90 seconds.")
Real-World Example: Building a Game Asset
Let's tie these steps together into a full, runnable script. In this scenario, we will generate a stylized medieval shield prop, ready for texture projection or mesh fitting.
import os
import time
import requests
API_KEY = os.getenv("LEONARDO_API_KEY")
BASE_URL = "https://cloud.leonardo.ai/api/rest/v1"
HEADERS = {
"accept": "application/json",
"content-type": "application/json",
"authorization": f"Bearer {API_KEY}"
}
def generate_game_prop(prompt_description, filename):
print(f"Starting generation for: '{prompt_description}'")
# Standard technical prop prompt wrapper
full_prompt = (
f"Stylized 3D game asset, {prompt_description}, orthographic view, "
"isolated studio lighting, neutral solid gray background, clean geometry lines, octane render, 8k resolution"
)
payload = {
"height": 1024,
"width": 1024,
"prompt": full_prompt,
"negative_prompt": "cropped, out of frame, drop shadow, ground reflections, realistic photography, text, signature",
"num_images": 1,
"guidance_scale": 7.5
}
response = requests.post(f"{BASE_URL}/generations", json=payload, headers=HEADERS)
if response.status_code != 200:
print(f"Error starting job: {response.status_code} - {response.text}")
return
gen_id = response.json().get("sdGenerationJob", {}).get("generationId")
print(f"Job created with ID: {gen_id}")
# Poll loop
while True:
status_res = requests.get(f"{BASE_URL}/generations/{gen_id}", headers=HEADERS)
if status_res.status_code == 200:
gen_data = status_res.json().get("generations_by_pk", {})
status = gen_data.get("status")
if status == "COMPLETE":
images = gen_data.get("generated_images", [])
if images:
url = images[0].get("url")
img_bytes = requests.get(url).content
with open(filename, "wb") as f:
f.write(img_bytes)
print(f"Successfully downloaded asset to {filename}")
break
elif status == "FAILED":
print("Job failed on server.")
break
time.sleep(2)
if __name__ == "__main__":
prompt = "iron knight heater shield with gold trim and lion crest"
generate_game_prop(prompt, "knight_shield.png")
Community Tip: When targeting specific art styles (like hand-painted or low-poly), keep your main prompt short and push specific render engine terms into the prefix. Excess fluff weakens style consistency.
How Do You Fix Common Leonardo AI Errors?
Working with image generation APIs introduces unique failure points. Here are the most frequent errors and exact fixes for each:
- Error 401 Unauthorized: Your authorization header is missing or improperly formatted. Ensure you include the prefix
Bearerbefore your API key. - Error 422 Unprocessable Entity: Sent invalid dimensions or unknown model IDs. Make sure width and height are multiples of 8, typically between 512 and 1024.
- Error 429 Too Many Requests: You hit your API rate limit. Implement exponential backoff in your script rather than polling every second.
- Distorted Mesh / Background Noise: The background blends into the prop. Add
studio background, clean background, white backdropto your prompt and applyshadows, environment, dirt groundto the negative prompt. - Inconsistent Asset Scale: Props appear too close or cut off. Add
orthographic centered view, full subject viewto your prompt parameters.
Security Warning: Before running high-volume asset generation jobs, set up API spend limits in your billing settings to prevent unexpected charges from run-away retry loops.
Pro Tips & Advanced Usage
To take your generation pipeline further, consider these professional optimization techniques:
- Keep Prompts Concise: Prompt engineering boils down to three core sentences: subject description, material parameters, and render framing. Avoid conversational language.
- Use Alpha Channel Extraction: Use Python's
rembgorpillowto automatically trim solid backgrounds and convert PNGs into transparent texture masks. - Generate Normal Maps: Run your clean asset textures through height-to-normal algorithms using libraries like OpenCV to generate normal maps automatically.
- Standardize Aspect Ratios: Keep textures at 1:1 ratios (1024x1024) to ensure UV mapping algorithms align cleanly without stretching.
- Batch Asset Calls: Build a queue system using Celery or Redis to process multiple prop variations asynchronously without locking up your local script.
What's Next: Related Tutorials & Next Steps
Now that you have automated prop generation working via Leonardo AI, here are logical next steps for expanding your technical art workflow:
- Integrate the output files directly into Blender using Blender Python (
bpy) scripting to project textures onto primitive base meshes. - Combine text-to-image prompts with LLM orchestrators like GPT-5.6 Sol or Claude Sonnet 5 to automatically draft matching asset descriptions from gamedev design documents.
- Set up automated depth-map extraction pipelines to create quick heightmaps for terrain deformation in your game engine.

