The External Render API.
Submit a room photo and a prompt, get back a photoreal render. REST over HTTPS, JSON in and out, one API key per app. Available on the Business plan.
https://renovaitor.com/api/v1
Authorization: Bearer rvtr_live_...
60 requests / minute / key
Business subscription required
Authentication
Every request carries a Business-plan API key as a bearer token. Create and revoke keys from your dashboard. The full secret is shown once at creation time and only a short prefix (e.g. rvtr_live_a1b2c3d4) is stored for display afterwards, so save it somewhere safe.
Authorization: Bearer rvtr_live_xxxxxxxxxxxxxxxxxxxxxxxxA request with a missing, malformed, or revoked key returns 401 unauthorized. A valid key on an account without an active Business subscription returns 403 forbidden.
POST /renders
Starts a render. The request is validated and queued synchronously; the render itself runs asynchronously (see Async results & webhooks).
Headers
| Field | Type | Description |
|---|---|---|
| Authorization | string | Bearer token: your API key. |
| Idempotency-Keyoptional | string | Any string you choose. Replaying the same key returns the original render id instead of starting a second render, which is safe for network retries. |
Body (JSON)
| Field | Type | Description |
|---|---|---|
| image | string | A public https:// URL or a data: URI of the source photo. URLs resolving to a private, loopback, or internal host are rejected. |
| editType | enum | One of STYLE_CHANGE, OBJECT_MODIFICATION, BACKGROUND_CHANGE, FURNITURE_ADDITION, COLOR_ADJUSTMENT, LIGHTING_CHANGE, LAYOUT_CHANGE, TEXT_EDIT. |
| modeloptional | "nano-banana" | "nano-banana-pro" | Defaults to nano-banana-pro for style and layout changes and nano-banana for everything else. Seedream-4 is used elsewhere in the product but isn’t selectable through this field. |
| prompt | string | Plain-language description of the change you want. |
| resolutionoptional | string | nano-banana supports 1K / 2K; nano-banana-pro supports 2K / 4K. Both default to 2K. If you omit resolution, or pass a value the chosen model doesn’t support, this does not error. it silently falls back to the model’s default resolution, and you’re billed for the resolution actually used, not the one requested. |
| callbackUrloptional | string | An https:// URL to receive a signed webhook when the render finishes. See below. |
Response: 202 Accepted
{
"id": "f3b1c2a0-9e21-4f3a-9c11-7a8e2b1d6c44",
"status": "processing",
"credits_charged": 15
}402 if your credit balance can’t cover the render, 400 for a malformed or invalid body.
Idempotent replay: 200 OK
If the request reuses an Idempotency-Key that already started a render, no new render is created and no credits are charged. Instead you get a 200 with the original render’s id and idempotent_replay: true:
{
"id": "f3b1c2a0-9e21-4f3a-9c11-7a8e2b1d6c44",
"status": "processing",
"idempotent_replay": true
}On a replay, status is always "processing" and isn’t authoritative. It doesn’t reflect the render’s actual current state. Call GET /renders/:id to get the real status.
GET /renders/:id
Fetches the current state of a render by the id returned from the create call.
Response
{
"id": "f3b1c2a0-9e21-4f3a-9c11-7a8e2b1d6c44",
"status": "succeeded",
"outputUrl": "https://cdn.renovaitor.com/renders/f3b1c2a0.jpg",
"error": null,
"createdAt": "2026-07-05T14:02:11.000Z",
"completedAt": "2026-07-05T14:02:47.000Z"
}status is one of processing, succeeded, failed, or canceled. outputUrl and completedAt are null until the render reaches a terminal state; error is only set on failed. Renders are scoped to your account, not to the individual API key that created them. Any key on the same account can read any render belonging to that account. 404 is returned only when the id doesn’t belong to your account at all.
GET /credits
Returns your account’s current credit balance.
{ "balance": 482 }Idempotency
Pass an Idempotency-Key header on POST /renders to make retries safe. The key is scoped to your account, not to the individual API key that sent it. The same value collides across all of your account’s API keys, not just the one that made the original request. If a request with a key that’s already started a render is repeated, the endpoint returns the original render’s id instead of charging credits and starting a second one. Use a fresh key (e.g. a UUID) per logical render, and reuse it only when retrying that exact request. Deduplication only applies to requests made one after another. If two requests with the same key arrive concurrently, both can start a render (and both can be charged), so send retries sequentially rather than in parallel.
Async results & webhooks
Renders run in the background. There are two ways to learn when one finishes. use whichever fits your integration, or both:
- Poll. Call
GET /renders/:iduntilstatusissucceeded,failed, orcanceled. - Webhook. Pass
callbackUrland we’ll POST the result once the render reaches a terminal state.
Webhook request
X-Renovaitor-Signature: <hex-encoded HMAC-SHA256 of the raw body>
Content-Type: application/json
{
"id": "f3b1c2a0-9e21-4f3a-9c11-7a8e2b1d6c44",
"status": "succeeded",
"outputUrl": "https://cdn.renovaitor.com/renders/f3b1c2a0.jpg",
"error": null
}Verify the signature by computing an HMAC-SHA256 of the raw request body with your webhook signing secret and comparing it, in constant time, to the X-Renovaitor-Signature header:
const crypto = require('crypto')
function isValidSignature(rawBody, signatureHeader, secret) {
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader))
}Your webhook signing secret is issued by Renovaitor when webhook delivery is enabled for your account. It isn’t currently self-serve from the dashboard, so contact support if you need one issued or rotated. Delivery is best-effort with a short timeout and isn’t retried, so treat polling as the source of truth and the webhook as a low-latency notification on top of it. Callback URLs must be https:// and resolve to a public host.
Errors
Every error response shares one shape:
{
"error": {
"code": "insufficient_credits",
"message": "Insufficient credits"
}
}| Status | code | Meaning |
|---|---|---|
| 400 | bad_request | Malformed JSON or a field failed validation. |
| 401 | unauthorized | Missing, malformed, or invalid/revoked API key. |
| 403 | forbidden | The key belongs to an account without an active Business subscription. |
| 404 | not_found | No render exists with that id on your account. |
| 402 | insufficient_credits | Your credit balance can’t cover this render. |
| 429 | rate_limited | You’ve exceeded 60 requests/minute for this key. |
| 500 | internal_error | Something failed on our side. Safe to retry. |
Rate limits
Each API key is limited to 60 requests per minute, covering all three endpoints combined. Exceeding it returns 429 rate_limited. The limit is per key, not per account, so separate keys (e.g. staging vs. production) don’t share a budget.
Credit costs
Renders are charged in credits from your account balance, the same balance used by the in-app Design Studio. Costs depend on the model and, for Nano Banana Pro, the resolution:
| Model | Credits |
|---|---|
| nano-banana | 7 |
| nano-banana-pro | 15 (2K) / 25 (4K) |
| seedream-4 | 5, used elsewhere in the product, not selectable via model on this API |
See the full pricing page for subscription and credit-pack rates. API access itself requires an active Business subscription; the credits it spends are the same pooled balance shown in your dashboard.
cURL quickstart
Start a render:
curl -X POST https://renovaitor.com/api/v1/renders \
-H "Authorization: Bearer rvtr_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 8f14e45f-ceea-4b7d-b6d3-1c9e1a3c9a1b" \
-d '{
"image": "https://example.com/living-room.jpg",
"editType": "STYLE_CHANGE",
"model": "nano-banana-pro",
"prompt": "Modern Scandinavian living room, warm oak floors, soft daylight",
"resolution": "2K",
"callbackUrl": "https://yourapp.com/webhooks/renovaitor"
}'Then poll for the result:
curl https://renovaitor.com/api/v1/renders/f3b1c2a0-9e21-4f3a-9c11-7a8e2b1d6c44 \
-H "Authorization: Bearer rvtr_live_xxxxxxxxxxxxxxxxxxxxxxxx"