A failed request is free: how paying per image works
How billing works when a request fails: the charge at submission, the automatic refund, how the API shows both, and what the portal shows, with a code sample.
Rendergrid editorsAbout the editors
In brief
A failed request is free. The price is charged when we accept your request, and if no image is made, the same amount goes straight back to your balance, with nothing for you to ask. In the API you see the charge as cost in the first response and a failed task as failed. After a failure, a new attempt needs a new idempotency key.
A 2K image from Nano Banana 2.1 costs $0.03.
When does the charge happen?
Every request is paid for in advance. When the API accepts a request, it responds with 202 and takes the price from your balance before any work starts; that amount is the cost field of the response. For example, a 2K image from Nano Banana 2.1 costs $0.03. All prices are on the pricing page. When an image is made, the charge stays; when none is made, it comes back.
The prompt behind this image
Photograph one amber glass bottle with a plain black cap on a low, round pale-stone plinth. Frame the entire bottle centrally with ample empty space. Soft light from the left gives the glass a narrow highlight against a matte beige background. No lettering, logos, people, or watermarks.
Nano Banana 2.1
nano-banana-2.1 · 2K · 1:1 · $0.03
Nano Banana 2.1, 2K, 1:1, one attempt. The bottle is centered with plenty of empty beige background above it, and the whole bottle and the plinth are in frame. Light comes from the left: the glass shows soft, pale reflections on its left side, the plinth casts a shadow to the right on the floor, and a warm amber tint lies on the plinth to the right of the bottle’s base. The prompt asked for a narrow highlight on the glass; here the reflection is soft and fairly wide rather than a thin line. There is no label, lettering, logo, person, or watermark. This is a successful result, not a failure: the request completed and was charged once.
Is a failed request charged?
No. There are two cases:
- Rejected before the charge. An empty prompt, a resolution or an aspect ratio the model doesn’t support, or a mask gets a
400response, and your balance doesn’t change. - Accepted, then failed. The request got its
202and the charge was taken, but no image was made. The charge is refunded automatically and right away, whether the failure was on our side (error) or the request was declined (declined), for example under the content rules. See the guide to declined requests.
The prompt behind this image
Arrange two ceramic pump bottles, white and sand-colored, on neighboring low blocks. Point both pumps in the same direction. Use a three-quarter product view, a pale seamless background and soft separate shadows. Keep both bottles fully in frame with a clear gap between them. No lettering, logos, people, or watermarks.
Nano Banana 2.1
nano-banana-2.1 · 2K · 1:1 · $0.03
Nano Banana 2.1, 2K, 1:1, one attempt. Both pumps point the same way, to the left, as the prompt asked, and both bottles are fully in frame with a clear gap between them. The two bottles are alike in shape and size, and the sand-colored one carries small dark flecks. Shadows fall to the right, and the clearest one lies to the right of the right-hand block; its edge is fairly crisp, harder than the soft shadows the prompt asked for. The camera sits almost level with the blocks, so their tops are barely visible and the view reads closer to straight-on than to the three-quarter view the prompt asked for. There is no label, lettering, logo, person, or watermark. This is a successful result: the request completed and was charged once.
How does a failed request look in the API?
Two responses show it: the 202 you get when you send the request, with id, status, task_id, and cost, and the task you read with GET /creations/{id}. The task ends as completed with result_urls, or as failed with an error text and a failure object holding type, code, and retryable. The code below sends one request, prints the charge, polls no more often than every 5 seconds, and handles both outcomes.
# Requires bash, curl, and uuidgen. One idempotency key per request. IDEMPOTENCY_KEY="$(uuidgen)" || exit 1 curl --fail-with-body --max-time 30 \ -X POST https://api.rendergrid.io/ api/ public/ v1/ images/ generate \ -H "Authorization: Bearer $RENDERGRID_API_KEY" \ -H "Content-Type: application/json" \ -H "X-Idempotency-Key: $IDEMPOTENCY_KEY" \ -d '{ "model": "nano-banana-2.1", "prompt": "A ceramic mug on marble, soft window light", "resolution": "2K", "aspect_ratio": "1:1" }' # Then ask for the outcome with the id from the 202 response. CREATION_ID="5b0214ae-9e39-460a-820a-f6ae3e24400b" curl --fail-with-body --max-time 20 \ "https:// api.rendergrid.io/ api/ public/ v1/ creations/ $CREATION_ID" \ -H "Authorization: Bearer $RENDERGRID_API_KEY"
import os import time import uuid import requests API = "https://api.rendergrid.io/ api/ public/ v1" AUTH = {"Authorization": f"Bearer {os.environ['RENDERGRID_API_KEY']}"} response = requests.post( f"{API}/images/generate", headers={**AUTH, "X-Idempotency-Key": str(uuid.uuid4())}, json={ "model": "nano-banana-2.1", "prompt": "A ceramic mug on marble, soft window light", "resolution": "2K", "aspect_ratio": "1:1", }, timeout=(10, 120), ) response.raise_for_status() creation = response.json() print("charged at submission:", creation["cost"]) while True: r = requests.get(f"{API}/creations/{creation['id']}", headers=AUTH, timeout=(5, 20)) r.raise_for_status() state = r.json() if state["status"] in ("completed", "failed"): break time.sleep(5) # no more often than once every 5 seconds if state["status"] == "completed": print(state["result_urls"]) else: # the charge has already been refunded; a new attempt needs a new key print(state["error"], state.get("failure"))
// Node.js 22+, .mjs file import { randomUUID } from "node:crypto"; const API = "https://api.rendergrid.io/ api/ public/ v1"; const AUTH = { Authorization: `Bearer ${process.env.RENDERGRID_API_KEY}` }; const submit = await fetch(`${API}/images/generate`, { method: "POST", headers: { ...AUTH, "Content-Type": "application/json", "X-Idempotency-Key": randomUUID() }, body: JSON.stringify({ model: "nano-banana-2.1", prompt: "A ceramic mug on marble, soft window light", resolution: "2K", aspect_ratio: "1:1", }), signal: AbortSignal.timeout(120_000), }); if (submit.status !== 202) { throw new Error(`HTTP ${submit.status}: ${await submit.text()}`); } const creation = await submit.json(); console.log("charged at submission:", creation.cost); let state; do { // no more often than once every 5 seconds await new Promise((resolve) => setTimeout(resolve, 5000)); const res = await fetch(`${API}/creations/${creation.id}`, { headers: AUTH, signal: AbortSignal.timeout(20_000), }); if (!res.ok) { throw new Error(`HTTP ${res.status}: ${await res.text()}`); } state = await res.json(); } while (["queued", "processing"].includes(state.status)); if (state.status === "completed") { console.log(state.result_urls); } else { // the charge has already been refunded; a new attempt needs a new key console.log(state.error, state.failure); }
{
"id": "5b0214ae-9e39-460a-820a-f6ae3e24400b",
"status": "queued",
"task_id": "5b0214ae-9e39-460a-820a-f6ae3e24400b",
"cost": 0.03
}
{
"id": "5b0214ae-9e39-460a-820a-f6ae3e24400b",
"status": "failed",
"error": "The image or prompt was rejected by the safety filter. Try a different image or rephrase the prompt.",
"failure": {
"type": "declined",
"code": "content_policy",
"retryable": false
}
}
A failed task comes back with HTTP 200: it is the task’s outcome, not an error of the status request. By the time you read it, the amount is back on your balance.
Show error to people and build your logic on failure.type and failure.code: the text may change.
For a new attempt after failed, send a new request with a new idempotency key: it is a new paid operation, and its charge comes back if it fails too. The idempotency docs explain when to repeat a request with the same key.
How does it look in the portal?
In the portal, the section where you top up also shows your balance and the transaction history. A charge for a generation and a refund for a failed request are separate entries there. The analytics page repeats that a failed request is free and shows how much was returned to your balance over the period it covers.
Is this the same as a refund of a top-up?
No. The automatic refund puts the charge of a failed request back on your balance, and you never request it. A refund of a top-up is a separate, manual process: it returns the unspent cash part of a top-up, you ask for it in the portal within 7 days of that top-up, and we pay it by hand in USDT, only to the crypto wallet that sent your own top-ups. A top-up’s fees aren’t refunded.
Where does this not apply?
- These images are ordinary results. A failed request makes no image, so none of these frames shows one, and none is a screenshot of the portal.
- A refund is not a speed promise. There is no promised waiting time: it depends on the model and the load.
- A new attempt is a new charge. After
failed, it takes a new key and is charged as usual.
The prompt behind this image
An overhead flat lay of a wooden brush, oval soap and neatly folded white towel on beige. Space the objects evenly without overlap. Diffuse daylight should reveal the brush bristles and towel loops. Leave a clear border around the arrangement. No lettering, logos, people, or watermarks.
Nano Banana 2.1
nano-banana-2.1 · 2K · 1:1 · $0.03
Nano Banana 2.1, 2K, 1:1, one attempt. The three objects stand in one row with a similar gap between them and no overlap, and a clear beige border surrounds the group, widest above and below. The brush bristles show as separate tufts, the loops of the towel are visible, as the prompt asked, and a woven band runs across the middle of the towel. The soap’s surface shows a few faint streaks, and a soft shadow falls below the brush and the towel. There is no label, lettering, logo, person, or watermark. This is a successful result: the request completed and was charged once.
A failed request is free
See the charge on your own request
Make an image in the playground. If a request fails, the amount goes straight back to your balance.
Try Nano Banana 2.1Questions and answers
Is a failed request really free?
How soon does the money come back?
GET /balance: see the balance docs.Does repeating a request with the same key cost twice?
202 response, with the same id, status, task_id, and cost, and there is no second charge. That saved response may still say queued. Once a task has gone from 202 to failed, the same key doesn’t restart it: a new attempt takes a new key.Sources
- BalanceCharge, refund, and where to see them.
- Tasks and statusesThe
failedstatus and thefailureobject. - Error codesRejections before the charge and refunded failures.
- IdempotencyThe same key, and a new attempt after
failed. - Create an imageThe request, the
202response, andcost. - PricingPrice per image. Checked on .
- The Rendergrid portalBalance, transaction history, and analytics.




