4.8 KiB
Nano Banana Browser POC
This repo includes a small browser-driven POC for inspecting Nano Banana's free-tool flow without trying to bypass Turnstile, login, or rate limits.
What it does
- Opens the Nano Banana page in a real browser
- Optionally prefills a public image URL
- Optionally clicks the primary action button
- Captures requests and responses for the relevant private endpoints
- Saves screenshots, console logs, downloads, and browser storage state
What it does not do
- It does not bypass Cloudflare Turnstile
- It does not bypass login
- It does not try to evade rate limits
- It is not a production API adapter
Commands
Remove background:
pnpm poc:nanobanana --mode remove-background --auto-submit
Upscale:
pnpm poc:nanobanana --mode upscale --auto-submit
Use a custom image URL:
pnpm poc:nanobanana \
--mode upscale \
--image-url "https://example.com/demo.jpg" \
--auto-submit
Useful flags
--locale en--headless--no-auto-fill--auto-submit--close-on-success--timeout-ms 300000--out-dir /absolute/path/to/output
Output
By default, artifacts are written to:
playwright/nanobanana-poc/<mode>-<timestamp>/
The folder includes:
summary.jsonsession-plan.jsonstorage-state-initial.jsonstorage-state-final.jsonrequests/*.jsonresponses/*.jsonlogs/*.json- screenshots such as
01-loaded.pngand99-final.png
Current endpoints observed
/api/img/rmbg/api/img/upscale/api/img/result/{taskId}?type=img/api/img/image-safety/api/presigned-urls
Current findings (2026-05-21)
These findings were confirmed from the live frontend bundle and manual browser validation.
Remove background
Frontend schema:
imageSource:"upload"or"url"imageUrl: optional stringturnstileToken: optional string
Observed request shape:
{
"imageSource": "url",
"imageUrl": "https://...",
"turnstileToken": "..."
}
Observed endpoint behavior:
POST /api/img/rmbgContent-Type: application/json- success path reads
response.dataas the final output image URL - after success the page clears
turnstileToken 403withmessage === "nsfw"is handled as an NSFW block
Manual validation in the in-app browser produced a successful result and exposed a final image URL hosted on replicate.delivery, which suggests the private backend may fan out to third-party storage or processing before returning the final asset URL.
Image upscaler
Frontend schema:
imageSource:"upload"or"url"imageUrl: optional stringscale: number, min1, max8, default4turnstileToken: optional string
Observed submit request shape:
{
"imageUrl": "https://...",
"scale": "4",
"turnstileToken": "..."
}
Upload mode can also send an image field populated from the uploaded asset URL.
Observed endpoint behavior:
POST /api/img/upscaleContent-Type: application/json- success path reads
taskIdfromresponse.data.taskIdorresponse.taskId - the frontend then polls
GET /api/img/result/{taskId}?type=img - polling treats
code === 1orstatus === 1as success - final output is read from
data.imgAfterSrcorimgAfterSrc - polling treats
code === 2orstatus === 2as failure - the polling loop waits 2 seconds and retries up to 60 times
402is handled as an upgrade/paywall condition403withmessage === "nsfw"is handled as an NSFW block
Turnstile
Observed Cloudflare site key:
0x4AAAAAAAh8CLI0IWTYOWMY
Observed frontend behavior:
- when
turnstileTokenis missing, the frontend renders or executes Turnstile before submitting - after a successful request, the frontend clears
turnstileToken - this means the free-tool flow is not designed as a stable unattended API
Upload helper
Observed upload helper behavior:
POST /api/presigned-urlswith JSON like{ "count": 1, "fileType": "<mime>" }- response returns
urls - the browser
PUTs the file to the returned presigned URL - the frontend then derives a public asset URL in the form:
https://temp.videostudioai.com/<key>
Practical takeaway
This is enough to build an internal unofficial wrapper, but not enough to remove the core operational risk:
- the flow still depends on Turnstile
- Turnstile tokens are intentionally short-lived and cleared after use
- the frontend contract may change at any time
- upscaling still appears more likely to require session state than remove-background
Suggested use
Use this only as an internal validation tool to answer questions like:
- Does the browser session create anonymous or guest state?
- When does Turnstile appear?
- Which cookies or local storage values appear before the request?
- Does upscaling require an authenticated session?
- What request and polling shape does the frontend use today?