Why use Nano Banana Pro via Wisdom Gate
Developers want a single, reliable touchpoint to integrate advanced generative features without juggling multiple SDKs. The Wisdom Gate unified API gives you a production-ready path to hook into Nano Banana Pro’s image generation capabilities (and preview text-to-video) with minimal friction.
- Unified base URL and auth headers
- Works with familiar chat completion semantics
- Practical: easy to deploy, log, and secure
This guide shows you how to generate images (and short video previews) using the model gemini-3-pro-image-preview through the Wisdom Gate endpoint, using cURL and TypeScript you can drop into real apps.
Fast setup
Prerequisites
- An API key for Wisdom Gate (keep it secret, rotate regularly).
- Base URL: https://wisdom-gate.juheapi.com/v1
- Model id: gemini-3-pro-image-preview
- Node.js 18+ for TypeScript examples (native fetch and fs).
Environment variables
Store your API key in your environment, not in source control.
- WISDOM_GATE_API_KEY
Standard headers
- Authorization: YOUR_API_KEY
- Content-Type: application/json
- Accept: /
The core pattern: request, parse, persist
Wisdom Gate exposes a chat-like completion interface. For image generation, you’ll instruct the model to return a compact JSON payload that contains the image as a base64 string along with its MIME type. Your app then decodes that payload and writes the file to disk or cloud storage.
- Send a prompt about the image you want
- Ask for strict JSON output that includes mime_type and b64
- Parse the response and persist the binary
This approach avoids guessing about additional endpoints, and works cleanly with existing logging and monitoring.
cURL quick start: generate one image
The following example mirrors the official request shape but adds a system directive that forces the model to return JSON only. Replace YOUR_API_KEY.
curl --location --request POST 'https://wisdom-gate.juheapi.com/v1/chat/completions' \
--header 'Authorization: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--header 'Accept: */*' \
--header 'Host: wisdom-gate.juheapi.com' \
--header 'Connection: keep-alive' \
--data-raw '{
"model": "gemini-3-pro-image-preview",
"messages": [
{
"role": "system",
"content": "Return only compact JSON with keys mime_type and b64 for a single image. No text, no code fences."
},
{
"role": "user",
"content": "Generate a photorealistic tropical reef at sunrise, vibrant corals, soft god rays, 4K."
}
]
}'
The response content will be a JSON string. Example (truncated for readability):
{
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "{\"mime_type\":\"image/png\",\"b64\":\"iVBORw0KGgoAAA...\"}"
}
}
]
}
You’ll parse choices[0].message.content into an object, then decode the b64 and write a file.
TypeScript client: robust helper for image generation
The helper below wraps fetch, validates output, extracts JSON even if the model adds stray text (we asked it not to, but defensive code is healthy), and writes the file.
import fs from 'node:fs';
import path from 'node:path';
const API_URL = 'https://wisdom-gate.juheapi.com/v1/chat/completions';
const API_KEY = process.env.WISDOM_GATE_API_KEY;
if (!API_KEY) {
throw new Error('Missing WISDOM_GATE_API_KEY');
}
function extractJsonString(rawContent: string): string {
// If the model followed instructions, rawContent is already a JSON string.
// Otherwise, try to extract the first JSON object we see.
try {
JSON.parse(rawContent);
return rawContent;
} catch {
const start = rawContent.indexOf('{');
const end = rawContent.lastIndexOf('}');
if (start !== -1 && end !== -1 && end > start) {
return rawContent.slice(start, end + 1);
}
throw new Error('No JSON object found in assistant content');
}
}
type ImagePayload = { mime_type: string; b64: string };
async function generateImage(prompt: string): Promise<ImagePayload> {
const body = {
model: 'gemini-3-pro-image-preview',
messages: [
{
role: 'system',
content:
'Return only compact JSON with keys mime_type and b64 for a single image. No explanations.'
},
{ role: 'user', content: prompt }
]
};
const res = await fetch(API_URL, {
method: 'POST',
headers: {
Authorization: API_KEY,
'Content-Type': 'application/json',
Accept: '*/*'
},
body: JSON.stringify(body)
});
if (!res.ok) {
const text = await res.text();
throw new Error(`HTTP ${res.status}: ${text}`);
}
const data = (await res.json()) as any;
const content = data?.choices?.[0]?.message?.content;
if (!content || typeof content !== 'string') {
throw new Error('Missing assistant content');
}
const jsonString = extractJsonString(content);
const payload = JSON.parse(jsonString) as ImagePayload;
if (!payload?.mime_type || !payload?.b64) {
throw new Error('Invalid payload: expected mime_type and b64');
}
return payload;
}
async function saveImage(payload: ImagePayload, outfile: string) {
const buffer = Buffer.from(payload.b64, 'base64');
await fs.promises.writeFile(outfile, buffer);
console.log(`Saved ${payload.mime_type} -> ${outfile}`);
}
(async () => {
const prompt =
'Photorealistic tropical reef at sunrise, vibrant corals, soft god rays, 4K.';
const payload = await generateImage(prompt);
const ext = payload.mime_type.includes('png') ? 'png' : 'jpg';
const outfile = path.join(process.cwd(), `reef.${ext}`);
await saveImage(payload, outfile);
})();
Prompt crafting: consistent, controllable outputs
You’ll get the best results when you:
Give clear visual intent
- Subject: what to depict (reef, cityscape, portrait)
- Composition: wide angle, macro, isometric
- Lighting: golden hour, softbox, volumetric
- Style: photorealistic, watercolor, anime
- Resolution or aspect: 4K, 1024x1024, 16:9
Add constraints the model can follow
- Keep backgrounds minimal
- Avoid human faces if you want faster approval in some marketplaces
- Request a single frame (for images) or short duration (for video)
Example prompts
- Minimalist product hero: “Matte-black wireless earbuds on marble, soft rim light, 3/4 angle, 1024x1024, e-commerce style.”
- Architectural render: “Sunlit atrium, glass and wood, top-down isometric, clean shadows, magazine-quality.”
Multiple images and variations
Want more than one image in a single call? Ask the model to return an array of image payloads. Your app then iterates and writes them to disk.
cURL: request multiple variants
curl --location --request POST 'https://wisdom-gate.juheapi.com/v1/chat/completions' \
--header 'Authorization: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data-raw '{
"model": "gemini-3-pro-image-preview",
"messages": [
{
"role": "system",
"content": "Return only compact JSON with key images: an array of objects each with mime_type and b64."
},
{
"role": "user",
"content": "Create 3 variations of a neon cyberpunk alley, rain, reflections, 1024x1024."
}
]
}'
TypeScript: parse an array of images
async function generateVariants(prompt: string, count = 3) {
const body = {
model: 'gemini-3-pro-image-preview',
messages: [
{
role: 'system',
content:
'Return only compact JSON: {"images":[{"mime_type":"image/png","b64":"..."}]}'
},
{ role: 'user', content: `${prompt}. Make ${count} distinct variants.` }
]
};
const res = await fetch(API_URL, {
method: 'POST',
headers: { Authorization: API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
const data = (await res.json()) as any;
const content = data?.choices?.[0]?.message?.content ?? '';
const jsonString = extractJsonString(content);
const parsed = JSON.parse(jsonString);
const images: ImagePayload[] = parsed?.images ?? [];
if (!Array.isArray(images) || images.length === 0) {
throw new Error('No images in response');
}
await Promise.all(
images.map((p: ImagePayload, i: number) =>
saveImage(p, path.join(process.cwd(), `variant_${i + 1}.png`))
)
);
}
Nano Banana text-to-video preview
The gemini-3-pro-image-preview model can be steered to produce short video previews encoded in base64 (e.g., MP4 or GIF). Treat it similarly to images: ask for compact JSON with mime_type and b64, then write the binary to a file.
cURL: request a short clip
curl --location --request POST 'https://wisdom-gate.juheapi.com/v1/chat/completions' \
--header 'Authorization: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data-raw '{
"model": "gemini-3-pro-image-preview",
"messages": [
{
"role": "system",
"content": "Return only compact JSON with mime_type and b64 for a single short video (2–3s)."
},
{
"role": "user",
"content": "Text to video: Neon cyberpunk alley camera pan, rain, reflections, 2 seconds, 720p MP4."
}
]
}'
TypeScript: save an MP4
async function generateVideo(prompt: string) {
const body = {
model: 'gemini-3-pro-image-preview',
messages: [
{
role: 'system',
content:
'Return only compact JSON with mime_type and b64 for a short video (<=3s).'
},
{ role: 'user', content: prompt }
]
};
const res = await fetch(API_URL, {
method: 'POST',
headers: { Authorization: API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
const data = (await res.json()) as any;
const content = data?.choices?.[0]?.message?.content ?? '';
const payload = JSON.parse(extractJsonString(content)) as ImagePayload;
const outfile = path.join(process.cwd(), payload.mime_type.includes('gif') ? 'clip.gif' : 'clip.mp4');
await fs.promises.writeFile(outfile, Buffer.from(payload.b64, 'base64'));
console.log(`Saved video -> ${outfile}`);
}
Error handling, retries, and timeouts
Retry logic
- Use exponential backoff on 429 or transient 5xx.
- Cap retries to protect latency budgets.
- Log request ids if provided for traceability.
Timeouts
- Set an application-level timeout (e.g., AbortController) around fetch.
- For large outputs, increase timeout but keep an upper bound (30–60s).
Validation
- Confirm payload contains mime_type and b64 before writing.
- If JSON parsing fails, attempt extractJsonString and alert when it’s unrecoverable.
Observability
- Log prompt, model id, and elapsed time (not the raw b64) to avoid bloating logs.
- Track success rate and error codes to improve reliability.
Performance tips
Concurrency with care
- Batch small requests but keep concurrency below I/O limits (e.g., 4–8 simultaneous writes).
- Use Promise.allSettled when saving multiple variants to avoid cascading failures.
Caching
- Cache prompts and resulting file hashes to skip re-generation.
- De-duplicate identical prompts with a normalized key (lowercase, trimmed whitespace).
Stream responses when available
If the API supports server-sent events or chunked streaming, adopt it to begin decoding sooner. For static images, chunking offers marginal benefit; for video, it can reduce time-to-first-frame.
File formats
- Prefer PNG for lossless UI assets; JPG for photography; WEBP for web delivery.
- For video previews, MP4 broadly works; GIF for quick sharing.
Security and compliance
Protect your key
- Never ship the API key in client code.
- Use server-side proxy routes; rate limit based on user identity.
Content policies
- Follow platform content rules. Filter prompts and review outputs before publishing.
PII and user data
- Avoid embedding personal data in prompts. Hash or tokenize IDs.
Production checklist
- Environment: key loaded and rotated
- Observability: metrics, tracing, structured logs
- Resilience: retries, circuit breaker for repeated failures
- Security: proxy, input validation, output review
- Storage: file integrity checksums, CDN cache-control headers
- Performance: concurrency tuned, caching enabled
- UX: placeholders, progress indicators, error UI fallback
FAQs
Can I control the aspect ratio?
Yes. Include explicit dimensions in your prompt (e.g., 1024x1024 or 16:9). The model respects clear constraints.
How do I get multiple styles in one call?
Ask for an array of images and describe each style. Or call generateVariants with style keywords appended.
Does Wisdom Gate support other Nano Banana models?
Yes, through the same unified endpoint. Swap the model id accordingly.
What about very large outputs?
Expect longer generation time and larger payloads. Consider downscaling or chunking post-process.
Wrap-up
You now have a practical pattern to integrate Nano Banana Pro image generation (and short text-to-video previews) via the Wisdom Gate unified API. The key is consistent prompts and structured JSON outputs that your app can safely parse and persist. With the cURL and TypeScript snippets above, you can ship real features today.
Appendix: baseline example from Wisdom Gate
Here’s the minimal example request for completeness, using the provided endpoint details:
curl --location --request POST 'https://wisdom-gate.juheapi.com/v1/chat/completions' \
--header 'Authorization: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--header 'Accept: */*' \
--header 'Host: wisdom-gate.juheapi.com' \
--header 'Connection: keep-alive' \
--data-raw '{
"model":"gemini-3-pro-image-preview",
"messages": [
{
"role": "user",
"content": "Draw a stunning sea world."
}
]
}'