AI Portrait Generation API — Consistent Character Images at Scale
You're building a social app that auto-generates profile avatars, or a game that renders the same NPC character across different outfits and environments. The challenge isn't generating a portrait — it's generating portraits of the same person reliably across hundreds of API calls without manual editing in between.
That's the problem reference-guided generation solves. This tutorial walks through portrait generation with Nano Banana Pro on GenRelay, including the reference-image parameter for character consistency, batch patterns, and cost math for production workloads.
What makes portrait generation via API different from standard image generation?
Portrait generation requires character consistency — the same face structure, skin tone, and identity markers preserved across multiple output images. Standard text-to-image requests produce a new, random character on every call. Reference-guided generation pins those identity attributes to a seed image, so subsequent generations can vary the pose, expression, lighting, or background while keeping the face recognizable.
As of September 2026, Nano Banana Pro on GenRelay supports reference-guided generation via the reference_image_url parameter. This differs from basic image generation: you supply a source portrait, and the model treats it as a constraint rather than a style inspiration.
How do I authenticate with the GenRelay API for portrait generation?
All GenRelay endpoints use Bearer token authentication. Generate your API key in the GenRelay console under API Keys, then pass it in the Authorization header:
import requests
API_KEY = "YOUR_GENRELAY_KEY"
BASE_URL = "https://genrelay.ai/v1"
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
Store the key in an environment variable — never hardcode it in source files. In production, use a secrets manager (AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault). Exposed keys can be rotated from the console without changing your integration code.
How do I generate a basic portrait with Nano Banana Pro?
Portrait generation uses the /images/generations endpoint. A minimal request specifies the model, a text prompt, and the output resolution:
payload = {
"model": "nano-banana-pro",
"prompt": (
"Professional headshot, 35-year-old woman, confident expression, "
"neutral grey background, studio lighting, 4K"
),
"resolution": "1024x1024",
"n": 1
}
response = requests.post(
f"{BASE_URL}/images/generations",
headers=headers,
json=payload
)
result = response.json()
image_url = result["data"][0]["url"]
print(f"Portrait URL: {image_url}")
The response arrives synchronously — no polling loop required for standard image generation. Latency is typically 8–20 seconds depending on resolution and current queue depth.
Without a reference image, each call produces a different face. The next section shows how to lock the identity.
How do I maintain character consistency across multiple portraits?
Pass the URL of a reference portrait as reference_image_url. The model extracts facial geometry, skin tone, and structural features from the reference and applies them while following your new prompt for context (pose, background, style):
import time
REFERENCE_URL = "https://your-storage.example.com/character-reference.jpg"
scene_prompts = [
"Same person, casual outfit, outdoor park, natural daylight, 4K",
"Same person, formal business suit, modern office background, professional lighting, 4K",
"Same person, athletic wear, gym interior, 4K",
"Same person, evening wear, restaurant setting, warm ambient light, 4K",
]
portrait_urls = []
for prompt in scene_prompts:
payload = {
"model": "nano-banana-pro",
"prompt": prompt,
"reference_image_url": REFERENCE_URL,
"resolution": "1024x1024",
}
resp = requests.post(f"{BASE_URL}/images/generations", headers=headers, json=payload)
resp.raise_for_status()
portrait_urls.append(resp.json()["data"][0]["url"])
time.sleep(1) # rate limit headroom between serial requests
print(f"Generated {len(portrait_urls)} consistent portraits")
Tips for best consistency results:
- Reference image quality: use a clear, well-lit frontal portrait as the seed. Profile angles, low-resolution images, or cluttered backgrounds reduce consistency fidelity.
- Prompt specificity: specify lighting direction, background type, and approximate angle. The less the model has to infer, the less it drifts from the reference identity.
- Resolution match: generate outputs at the same resolution as the reference image when possible. Upscaling introduces interpolation that can smooth over identity-specific detail.
- Avoid contradictory demographics: a strong prompt that implies a different ethnicity or age range from the reference produces a blend, not a clean override.
How do I run portrait generation in batch at scale?
For large workloads — user avatar backfills, game character sheets, NPC asset packs — serialize at 1 req/s or parallelize with bounded concurrency to avoid rate limit errors:
import concurrent.futures
def generate_portrait(prompt: str, reference_url: str) -> dict:
payload = {
"model": "nano-banana-pro",
"prompt": prompt,
"reference_image_url": reference_url,
"resolution": "1024x1024",
}
resp = requests.post(f"{BASE_URL}/images/generations", headers=headers, json=payload)
resp.raise_for_status()
return {
"prompt": prompt,
"url": resp.json()["data"][0]["url"]
}
scene_prompts = [f"Portrait variation {i}, varied outdoor setting" for i in range(100)]
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
futures = [
executor.submit(generate_portrait, p, REFERENCE_URL)
for p in scene_prompts
]
results = [f.result() for f in concurrent.futures.as_completed(futures)]
print(f"Completed {len(results)} portraits")
Five concurrent workers is a safe default on the standard paid plan. If you hit 429 Too Many Requests, reduce max_workers to 3 and add exponential backoff — the batch image generation guide covers the full retry pattern with jitter.
For very large batches (10,000+ images), consider splitting into chunks of 500 and processing overnight to spread load.
How much does portrait generation cost via API?
Nano Banana Pro pricing on GenRelay is resolution-tiered, billed per successful generation:
| Resolution | Price per image |
|---|---|
| 1K (1024×1024) | $0.030 |
| 2K (2048×2048) | $0.030 |
| 4K (4096×4096) | $0.042 |
The 2K tier is priced identically to 1K — use 2K whenever your product can display higher detail without a cost penalty.
Cost math for common portrait workloads:
| Workload | Resolution | Images | Cost |
|---|---|---|---|
| Prototype / testing | 1K | 20 | $0.60 |
| App avatar backfill | 1K | 500 | $15.00 |
| Game character sheet (4K) | 4K | 200 | $8.40 |
| Monthly production (1K) | 1K | 10,000 | $300.00 |
For workloads where consistency isn't required — varied stock-style portraits, generic avatar generation — Nano Banana 2 costs $0.020/image at 1K and $0.036 at 4K. As of September 2026, Nano Banana 2 does not support the reference_image_url parameter, so Nano Banana Pro remains the choice for character-consistent workflows.
For instruction-based edits on already-generated portraits (swapping backgrounds, adding accessories), GPT-image-2 at $0.014/image handles that layer — see the AI image editing API guide.
FAQ
Does reference-guided generation work across different poses and camera angles?
Yes, including profile views and three-quarter angles, though frontal consistency is strongest. Extreme angles (90° profile, overhead) reduce fidelity because less of the reference face is visible in the output.
What resolution should I use for portrait generation?
For web use — avatars, profile photos, product cards — 1K at $0.030 is sufficient. For print or high-DPI display use 4K at $0.042. The 2K tier is identical in price to 1K, so default to 2K when you need extra detail without extra cost.
Can I use a generated portrait as the reference for subsequent generations?
Yes. GenRelay returns URLs to generated images with a configurable TTL (default 24 hours, visible in console settings). Pass any previously generated portrait URL as reference_image_url for downstream requests. Download the image to your own storage (S3, GCS) before the TTL expires — see the cloud storage integration guide for a reusable pipeline.
Is there a way to control how closely the output matches the reference?
Currently there is no explicit fidelity or strength parameter for the reference image. The model applies a fixed weighting between the reference identity and your text prompt. To increase reference influence, be less specific in your prompt; to allow more creative variation, be more descriptive.
Does the API support generating multiple character variants in a single request?
Set "n": 4 to get four outputs per request at the same cost as four separate calls. This is useful for picking the best output from a batch rather than post-processing a single image.
Next steps
- Try Nano Banana Pro reference-guided generation in the GenRelay console — free credits apply to all models including Nano Banana Pro
- Read the visual consistency guide for prompt strategies that reinforce reference fidelity
- For character animation from still portraits, see the Veo 3.1 image-to-video API guide