Developer API

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.

Base URL

https://renovaitor.com/api/v1

Authentication

Authorization: Bearer rvtr_live_...

Rate limit

60 requests / minute / key

Plan

Business subscription required

01 · Auth

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_xxxxxxxxxxxxxxxxxxxxxxxx

A 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.

02 · Endpoint

POST /renders

Starts a render. The request is validated and queued synchronously; the render itself runs asynchronously (see Async results & webhooks).

Headers

FieldTypeDescription
AuthorizationstringBearer token: your API key.
Idempotency-KeyoptionalstringAny 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)

FieldTypeDescription
imagestringA public https:// URL or a data: URI of the source photo. URLs resolving to a private, loopback, or internal host are rejected.
editTypeenumOne 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.
promptstringPlain-language description of the change you want.
resolutionoptionalstringnano-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.
callbackUrloptionalstringAn 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.

03 · Endpoint

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.

04 · Endpoint

GET /credits

Returns your account’s current credit balance.

{ "balance": 482 }
05 · Reliability

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.

06 · Async model

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/:id until status is succeeded, failed, or canceled.
  • Webhook. Pass callbackUrl and we’ll POST the result once the render reaches a terminal state.

Webhook request

POST callbackUrl
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:

node
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.

07 · Reference

Errors

Every error response shares one shape:

{
  "error": {
    "code": "insufficient_credits",
    "message": "Insufficient credits"
  }
}
StatuscodeMeaning
400bad_requestMalformed JSON or a field failed validation.
401unauthorizedMissing, malformed, or invalid/revoked API key.
403forbiddenThe key belongs to an account without an active Business subscription.
404not_foundNo render exists with that id on your account.
402insufficient_creditsYour credit balance can’t cover this render.
429rate_limitedYou’ve exceeded 60 requests/minute for this key.
500internal_errorSomething failed on our side. Safe to retry.
08 · Limits

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.

09 · Pricing

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:

ModelCredits
nano-banana7
nano-banana-pro15 (2K) / 25 (4K)
seedream-45, 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.

10 · Quickstart

cURL quickstart

Start a render:

bash
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:

bash
curl https://renovaitor.com/api/v1/renders/f3b1c2a0-9e21-4f3a-9c11-7a8e2b1d6c44 \
  -H "Authorization: Bearer rvtr_live_xxxxxxxxxxxxxxxxxxxxxxxx"