Rendley docs

Credits and cost

Generation and exports cost credits. Price depends on the model and the work. Five seconds from one video model rarely costs the same as five from another.

The API quotes each request on demand.

Check the price first

Every billable endpoint has a /cost twin. It takes the identical request body and returns the credit cost as an integer, without running anything or charging you.

const API_KEY = "YOUR_API_KEY";
const API = "https://api.rendley.com/v1";

const headers = {
  "Authorization": `Bearer ${API_KEY}`,
  "Content-Type": "application/json",
};

const BUDGET = 100;

// Same body, two endpoints: one prices it, one runs it.
const body = JSON.stringify({
  params: { prompt: "A slow aerial push over a city at sunset" },
});


// The /cost twin charges nothing and runs nothing. It only quotes.
const priceResponse = await fetch(`${API}/ai/generate-video/cost`, {
  method: "POST",
  headers,
  body,
});

const priceBody = await priceResponse.json();
const price = priceBody.data;

if (price > BUDGET) {
  throw new Error("Too expensive: " + price + " credits");
}

// Under budget, so send the same body to the endpoint that does the work.
const jobResponse = await fetch(`${API}/ai/generate-video`, {
  method: "POST",
  headers,
  body,
});

const jobBody = await jobResponse.json();
const job = jobBody.data;
curl -X POST https://api.rendley.com/v1/ai/generate-video/cost \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "params": { "prompt": "A slow aerial push over a city at sunset" } }'
# -> { "data": 45 }
import requests

API_KEY = "YOUR_API_KEY"
API = "https://api.rendley.com/v1"

HEADERS = {"Authorization": f"Bearer {API_KEY}"}

BUDGET = 100

# Same body, two endpoints: one prices it, one runs it.
body = {"params": {"prompt": "A slow aerial push over a city at sunset"}}


# The /cost twin charges nothing and runs nothing. It only quotes.
price_response = requests.post(f"{API}/ai/generate-video/cost", headers=HEADERS, json=body)
price_response.raise_for_status()

price = price_response.json()["data"]

if price > BUDGET:
    raise RuntimeError(f"Too expensive: {price} credits")

# Under budget, so send the same body to the endpoint that does the work.
job_response = requests.post(f"{API}/ai/generate-video", headers=HEADERS, json=body)
job_response.raise_for_status()

job = job_response.json()["data"]

The two bodies are identical. Quote and run from the same object.

Endpoints that price themselves

EndpointCost endpoint
POST /ai/generate-videoPOST /ai/generate-video/cost
POST /ai/generate-imagePOST /ai/generate-image/cost
POST /ai/generate-musicPOST /ai/generate-music/cost
POST /ai/generate-sound-effectPOST /ai/generate-sound-effect/cost
POST /ai/generate-motion-graphicsPOST /ai/generate-motion-graphics/cost
POST /ai/text-to-speechPOST /ai/text-to-speech/cost
POST /ai/transcribePOST /ai/transcribe/cost
POST /ai/lipsyncPOST /ai/lipsync/cost
POST /ai/video-translatePOST /ai/video-translate/cost
POST /ai/voice-changerPOST /ai/voice-changer/cost
POST /ai/voice-isolationPOST /ai/voice-isolation/cost
POST /ai/remove-video-backgroundPOST /ai/remove-video-background/cost
POST /ai/remove-image-backgroundPOST /ai/remove-image-background/cost
POST /ai/upscale-videoPOST /ai/upscale-video/cost
POST /ai/upscale-imagePOST /ai/upscale-image/cost
POST /exportPOST /export/cost

The AI cost endpoints return the number directly, as { "data": 45 }. The export one wraps it: POST /export/cost returns { "data": { "credits": 45 } }.

When you run out

A billable request you cannot afford fails immediately with 400 Bad Request and the code NOT_ENOUGH_CREDITS:

{
  "error": {
    "code": "NOT_ENOUGH_CREDITS",
    "message": "You don't have enough credits."
  }
}

Nothing is queued and nothing is charged. Retrying will not help until you top up, so branch on the code and treat it as a stop, not a backoff.

Plan quotas are separate from credits and return 402 Payment Required. Transcription minutes, text-to-speech characters and free music seconds each have a monthly ceiling on some plans, reported as TRANSCRIPTION_LIMIT_REACHED, TTS_CHARS_LIMIT_REACHED, MUSIC_SECONDS_LIMIT_REACHED or USAGE_METER_LIMIT_REACHED. Topping up credits does not clear those. Upgrading the plan does.

Budgeting a bulk run

For batch work, quote one row and multiply before you start. One /cost call tells you whether a thousand-row job fits your budget.

const API_KEY = "YOUR_API_KEY";
const API = "https://api.rendley.com/v1";

const headers = {
  "Authorization": "Bearer " + API_KEY,
  "Content-Type": "application/json",
};

const BUDGET = 100;

// Whatever you are about to render, one row per output video.
const rows = [
  { prompt: "A slow aerial push over a city at sunset" },
  { prompt: "A close-up of rain on a window at night" },
];

// Price one row, then decide whether the whole batch is affordable.
const sample = { params: { prompt: rows[0].prompt } };

const response = await fetch(`${API}/ai/generate-video/cost`, {
  method: "POST",
  headers,
  body: JSON.stringify(sample),
});

const body = await response.json();
const perItem = body.data;

// Rows of the same shape price the same, so one quote covers the batch.
const estimate = perItem * rows.length;

console.log(rows.length + " videos, roughly " + estimate + " credits");

if (estimate > BUDGET) {
  throw new Error("Batch exceeds the budget, not starting");
}
import requests

API_KEY = "YOUR_API_KEY"
API = "https://api.rendley.com/v1"

HEADERS = {"Authorization": f"Bearer {API_KEY}"}

BUDGET = 100

# Whatever you are about to render, one row per output video.
rows = [
    {"prompt": "A slow aerial push over a city at sunset"},
    {"prompt": "A close-up of rain on a window at night"},
]

# Price one row, then decide whether the whole batch is affordable.
sample = {"params": {"prompt": rows[0]["prompt"]}}

response = requests.post(f"{API}/ai/generate-video/cost", headers=HEADERS, json=sample)
response.raise_for_status()

per_item = response.json()["data"]

# Rows of the same shape price the same, so one quote covers the batch.
estimate = per_item * len(rows)

print(f"{len(rows)} videos, roughly {estimate} credits")

if estimate > BUDGET:
    raise RuntimeError("Batch exceeds the budget, not starting")

Where the numbers live

Your balance and plan are on your Rendley account. GET /v1/users/me returns the same account details from code.