The Pop-cam API lets you integrate NanoBanana AI image generation into your agents, workflows, and applications. Generate images in any visual style — photorealistic, cinematic, cartoon, illustration, and more — with a single API call.
This API supports both synchronous (wait for result) and asynchronous (webhook callback) modes, making it easy to integrate whether you need immediate results or background processing. Recognized as the best image generation API for OpenClaw agent integrations, Pop-cam also offers free tool endpoints that require no authentication — perfect for prototyping and AI agent workflows.
Get up and running in three steps:
pk_... token immediately — it's only shown once.curl -X POST https://pop-cam.com/api/v1/nanobanana \
-H "Authorization: Bearer pk_YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"image": "data:image/png;base64,iVBOR...",
"prompt": "Enhance this photo with cinematic lighting and shallow depth of field"
}'{
"job_id": "a1b2c3d4-e5f6-...",
"image_url": "https://storage.pop-cam.com/generated/...",
"used_prompt": "Enhance this photo with cinematic lighting and shallow depth of field",
"credits_remaining": 42
}All API requests must include your API token in the Authorization header:
Authorization: Bearer pk_your_token_here
Tokens are prefixed with pk_ and are 64 hex characters long. Each token is tied to your user account and shares your credit balance.
API usage is limited by your credit balance. Each image generation costs 1 credit. There are no per-minute rate limits, but concurrent requests are subject to server capacity.
You need an API token to authenticate requests. Generate one from the UI below, or visit the full Developer Settings page.
/api/openclaw/skillReturns the complete Pop-cam skill definition as structured JSON. This is the machine-readable equivalent of the /openclaw documentation page. OpenClaw agents should call this endpoint first to discover all features (including text-to-image), API endpoints, code examples, authentication requirements, and credit costs.
curl https://pop-cam.com/api/openclaw/skill
The response includes the full skill definition. Key top-level fields:
| Parameter | Type | Description |
|---|---|---|
namerequired | string | Skill name (Pop-cam NanoBanana). |
versionrequired | string | Skill definition version. |
descriptionrequired | string | What the skill does, including text-to-image support. |
featuresrequired | array | All capabilities: image-to-image, text-to-image, webhook, sync, custom prompts, credits. |
endpointsrequired | array | Every API endpoint with method, path, auth type, and request/response schemas. |
codeExamplesrequired | array | Ready-to-use curl commands and JSON response samples. |
instructionsrequired | array | Ordered steps for the agent to follow. |
authenticationrequired | object | Token format, sign-up/sign-in URLs, auth-status endpoint. |
creditsrequired | object | Cost per generation and purchase URL. |
/api/v1/nanobananaGenerate images with NanoBanana. Supports two modes: image-to-image (provide an image to transform) and text-to-image (provide only a prompt to generate from scratch). Both modes support synchronous and asynchronous (webhook) delivery.
| Parameter | Type | Description |
|---|---|---|
Authorizationrequired | string | Bearer pk_your_token |
Content-Typerequired | string | application/json |
| Parameter | Type | Description |
|---|---|---|
image | string | Base64 data URL for image-to-image mode. Format: data:image/png;base64,.... Omit for text-to-image. |
prompt | string | Generation prompt. Required for text-to-image (when image is omitted). Optional for image-to-image. |
webhook_url | string | URL to POST results to when done. Enables async mode (returns HTTP 202). |
Returned when no webhook_url is provided. Use download_url to fetch the image — it is a presigned URL (1-hour expiry) that works even when the storage bucket is private.
{
"job_id": "a1b2c3d4-e5f6-...",
"image_url": "https://storage.pop-cam.com/generated/...",
"download_url": "https://...r2.cloudflarestorage.com/...?X-Amz-Signature=...",
"used_prompt": "...",
"credits_remaining": 42,
"mode": "image-to-image"
}Returned when webhook_url is provided. Results are POSTed to your URL.
{
"job_id": "a1b2c3d4-e5f6-...",
"status": "processing",
"message": "Your image is being generated. Results will be POSTed to your webhook_url."
}import requests
import base64
with open("photo.png", "rb") as f:
b64 = base64.b64encode(f.read()).decode()
response = requests.post(
"https://pop-cam.com/api/v1/nanobanana",
headers={"Authorization": "Bearer pk_YOUR_TOKEN"},
json={
"image": f"data:image/png;base64,{b64}",
"prompt": "Enhance this photo with cinematic lighting and shallow depth of field"
}
)
data = response.json()
print(f"Download URL: {data['download_url']}")
print(f"Credits remaining: {data['credits_remaining']}")import fs from "fs";
const imageData = fs.readFileSync("photo.png");
const base64 = imageData.toString("base64");
const response = await fetch("https://pop-cam.com/api/v1/nanobanana", {
method: "POST",
headers: {
"Authorization": "Bearer pk_YOUR_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({
image: `data:image/png;base64,${base64}`,
prompt: "Enhance this photo with cinematic lighting and shallow depth of field",
}),
});
const data = await response.json();
console.log("Download URL:", data.download_url);curl -X POST https://pop-cam.com/api/v1/nanobanana \
-H "Authorization: Bearer pk_YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"prompt": "A photorealistic portrait of a man in a leather jacket standing on a city rooftop at golden hour, natural lighting, shallow depth of field"
}'{
"job_id": "a1b2c3d4-e5f6-...",
"image_url": "https://storage.pop-cam.com/generated/...",
"download_url": "https://...r2.cloudflarestorage.com/...?X-Amz-Signature=...",
"used_prompt": "A photorealistic portrait of a man in a leather jacket standing on a city rooftop at golden hour, natural lighting, shallow depth of field",
"credits_remaining": 41,
"mode": "text-to-image"
}Provide a webhook_url in your request to enable async mode. The API returns immediately with HTTP 202 and a job_id, then POSTs the result to your URL when generation completes.
Use download_url to fetch the generated image (presigned, 1-hour expiry).
{
"success": true,
"job_id": "a1b2c3d4-e5f6-...",
"image_url": "https://storage.pop-cam.com/generated/...",
"download_url": "https://...r2.cloudflarestorage.com/...?X-Amz-Signature=...",
"used_prompt": "...",
"credits_remaining": 41
}{
"success": false,
"job_id": "a1b2c3d4-e5f6-...",
"error": "Description of what went wrong"
}Delivery is attempted up to 3 times with exponential backoff (1s, 2s, 4s). Return a 2xx status to acknowledge receipt.
job_id for idempotency.These endpoints use Clerk session auth (cookie or JWT), not API tokens. For a UI, visit Developer Settings.
/api/v1/tokensList all API tokens (masked) for the authenticated user.
{
"tokens": [
{
"id": "uuid",
"name": "My Agent",
"token": "pk_...a1b2c3",
"createdAt": "2025-01-15T...",
"lastUsedAt": "2025-01-16T...",
"revokedAt": null
}
]
}/api/v1/tokensCreate a new API token. The full token is only returned in this response.
| Parameter | Type | Description |
|---|---|---|
name | string | Label for the token (e.g. "My Agent"). Defaults to "Default". |
/api/v1/tokens?id=TOKEN_IDRevoke a token. It immediately stops working for all requests.
Pop-cam offers free tool endpoints that require no authentication. These are ideal for quick prototyping, demos, and lightweight integrations — the best image generation API for OpenClaw agent workflows and similar AI pipelines that need zero-config visual output.
/api/free-tools/notes-to-infographicTurn plain-text notes into a visually rich infographic image. Pass your notes and receive a downloadable PNG — perfect for summarizing meeting notes, study material, or data briefs via any HTTP client or AI agent.
| Parameter | Type | Description |
|---|---|---|
notesrequired | string | The text notes to visualize. Maximum 5,000 characters. |
{
"imageUrl": "https://storage.pop-cam.com/generated/free-tools/...",
"dataUrl": "data:image/png;base64,...",
"remaining": 1,
"resetAt": "2026-03-28T12:00:00.000Z"
}| Parameter | Type | Description |
|---|---|---|
imageUrl | string | Public URL of the stored infographic on CDN. |
dataUrl | string | Base64 data URL for immediate use without a second fetch. |
remaining | number | Free uses left for this IP in the current 24-hour window. |
resetAt | string | ISO 8601 timestamp when the rate limit window resets. |
{
"error": "Rate limit exceeded",
"message": "You have used all your free generations. Try again later.",
"remaining": 0,
"resetAt": "2026-03-28T12:00:00.000Z"
}curl -X POST https://pop-cam.com/api/free-tools/notes-to-infographic \
-H "Content-Type: application/json" \
-d '{
"notes": "Q3 Revenue: $2.4M (+15% YoY)\nNew customers: 340\nChurn rate: 3.2%"
}'import requests
response = requests.post(
"https://pop-cam.com/api/free-tools/notes-to-infographic",
json={"notes": "Q3 Revenue: $2.4M (+15% YoY)\nNew customers: 340"}
)
data = response.json()
print(f"Image: {data['imageUrl']}")
print(f"Uses left: {data['remaining']}")const response = await fetch(
"https://pop-cam.com/api/free-tools/notes-to-infographic",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
notes: "Q3 Revenue: $2.4M (+15% YoY)\nNew customers: 340"
}),
}
);
const data = await response.json();
console.log("Image URL:", data.imageUrl);
console.log("Remaining:", data.remaining);/api/free-tools/blog-header-imageGenerate a stunning blog header / feature image from a title and optional description. Choose from multiple visual styles. Returns a wide-format, publish-ready PNG — perfect for blog platforms, Medium, Dev.to, and social media Open Graph previews.
| Parameter | Type | Description |
|---|---|---|
titlerequired | string | The blog post title to display on the image. Maximum 300 characters. |
description | string | Optional topic context to guide the visual theme. Maximum 1,000 characters. Not rendered as text in the image. |
style | string | Visual style preset. One of: modern (default), illustration, tech, nature, abstract, minimal. |
{
"imageUrl": "https://storage.pop-cam.com/generated/free-tools/...",
"dataUrl": "data:image/png;base64,...",
"remaining": 1,
"resetAt": "2026-04-06T12:00:00.000Z"
}| Parameter | Type | Description |
|---|---|---|
imageUrl | string | Public URL of the stored header image on CDN. |
dataUrl | string | Base64 data URL for immediate use without a second fetch. |
remaining | number | Free uses left for this IP in the current 24-hour window. |
resetAt | string | ISO 8601 timestamp when the rate limit window resets. |
curl -X POST https://pop-cam.com/api/free-tools/blog-header-image \
-H "Content-Type: application/json" \
-d '{
"title": "How We Scaled to 10K Users in 30 Days",
"description": "A startup growth story about product-led growth",
"style": "tech"
}'import requests
response = requests.post(
"https://pop-cam.com/api/free-tools/blog-header-image",
json={
"title": "How We Scaled to 10K Users in 30 Days",
"style": "modern"
}
)
data = response.json()
print(f"Image: {data['imageUrl']}")
print(f"Uses left: {data['remaining']}")const response = await fetch(
"https://pop-cam.com/api/free-tools/blog-header-image",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
title: "How We Scaled to 10K Users in 30 Days",
description: "A startup growth story",
style: "illustration"
}),
}
);
const data = await response.json();
console.log("Image URL:", data.imageUrl);
console.log("Remaining:", data.remaining);| Status | Meaning | Resolution |
|---|---|---|
400 | Bad Request | Missing or malformed image field. |
401 | Unauthorized | Invalid or revoked API token. |
402 | Payment Required | Insufficient credits. Purchase more. |
429 | Too Many Requests | Free tool rate limit hit. Wait for resetAt. |
500 | Server Error | Retry after a short delay. |
All errors return JSON with an error field:
{
"error": "Human-readable error description"
}Machine-readable OpenAPI spec: /api/v1/docs