Quickstart
Get your first inference call running in under 5 minutes. Create an account, add credits, generate a key, and fire your first request.
Sign up, go to Wallet, and add at least ₹10. Credits are deducted per token used. Minimum top-up is ₹10 (1000 paise). Supports UPI, RuPay, and cards via Razorpay.
Go to API Keys and click "Create new key". Copy it immediately, it is shown only once. Keys are in the format sk-<48 hex chars> and stored as SHA-256 hashes, the raw key is never persisted.
Send a request to POST /gateway/v1/inference/run with your key in the Authorization header.
import httpx
response = httpx.post(
"http://www.velona.in/gateway/v1/inference/run",
headers={"Authorization": "Bearer <your-key>"},
json={
"model": "openai/gpt-4o-mini",
"turns": [{"role": "user", "content": "Hello, world!"}],
}
)
print(response.json()["data"]["output"])
GET /gateway/v1/account/credits or in the dashboard. All amounts are in INR (paise internally).
API reference
All endpoints accept JSON and return JSON. Authentication is via Bearer token in the Authorization header. The gateway is fully OpenAI-SDK compatible, just change base_url and api_key.
/gateway/v1/inference/run
Text generation: chat, completion, instruction following.
/gateway/v1/inference/embed
Generate vector embeddings from text inputs.
/gateway/v1/images/render
Generate images from a text prompt.
/gateway/v1/models
List all available models with pricing and capabilities.
/gateway/v1/account/credits
Read-only wallet balance in USD and INR.
Authentication
All gateway endpoints require a Bearer token. Keys are created and managed via the API Keys dashboard. There is no API for key creation.
How to authenticate
All requests must include an Authorization header:
Authorization: Bearer sk-<your-48-hex-key>
Key format & security
sk-<48 lowercase hex chars>. The raw key is shown exactly once at creation and never stored, only its SHA-256 hash is persisted. Revoke and regenerate from the API Keys page if compromised.
Key attributes
Each key can be configured with optional constraints:
OpenAI SDK compatibility
Existing code using the OpenAI Python or Node SDK works without changes, just swap the base URL and key:
from openai import OpenAI
client = OpenAI(
api_key="<your-velona-key>",
base_url="http://www.velona.in/v1",
)
completion = client.chat.completions.create(
model="openai/gpt-4o-mini",
messages=[{"role": "user", "content": "Hello!"}],
)
print(completion.choices[0].message.content)
Models
The gateway routes to 300+ models across leading AI providers via OpenRouter. Use the model ID in the format provider/model-name. All pricing is displayed in INR.
Popular models
GET /gateway/v1/models or visit the Models page. Pricing data is cached for 2 hours and includes platform markup.
INR pricing formula
All costs are calculated transparently:
cost_paise = max(1, int(cost_usd × buffered_forex_rate × (1 + MARKUP_PERCENT/100) × 100))
# Where:
# buffered_forex_rate = live USD>INR rate × (1 + FOREX_BUFFER_PERCENT/100)
# MARKUP_PERCENT = platform markup (visible on /pricing)
# Minimum charge = 1 paise (never zero)
For streaming requests, a provisional debit is made upfront (1,000 prompt + 1,000 completion tokens estimate) and reconciled to the actual token count after the stream completes. You are never over-charged.
Free model access
Certain models are available for free-tier access. Free model requests bypass wallet billing entirely but are subject to daily call limits and per-minute rate gates. Request free model access from the Models page.
Streaming
Set stream: true in your request to receive responses as Server-Sent Events (SSE). Each chunk is a JSON object on a data: line.
import httpx
with httpx.stream(
"POST",
"http://www.velona.in/gateway/v1/inference/run",
headers={"Authorization": "Bearer <your-key>"},
json={
"model": "openai/gpt-4o-mini",
"turns": [{"role": "user", "content": "Count to 5."}],
"stream": True,
}
) as r:
for line in r.iter_lines():
if line.startswith("data:"):
print(line[5:].strip())
data: {"request_id":"req_abc","event":"delta","data":{"run_id":"run_xyz","delta":"1"}}
data: {"request_id":"req_abc","event":"delta","data":{"run_id":"run_xyz","delta":", 2"}}
data: {"request_id":"req_abc","event":"done","data":{"finish":"complete","usage":{...},"billed_usd":0.000012}}
data: [DONE]
delta (incremental token), done (final usage + billing), error (if something goes wrong mid-stream).
Streaming billing
For streaming requests the gateway makes a provisional debit upfront (assumes 1,000 prompt + 1,000 completion tokens, capped at your current balance). After the stream completes, actual token counts are used to calculate the real cost. The difference is refunded or charged in a reconciliation step. Deterministic idempotency keys (prov-<id>, reconcile-refund-<id>) prevent any double-billing on retries.
Memory sessions
Memory sessions let you persist conversation context across multiple API calls and browser sessions. Attach a mem_ session ID to any inference call and the gateway automatically injects the stored context and appends the new exchange.
How it works
Without memory every API call is stateless, your application must send full conversation history each time. With a memory session, the gateway maintains the history server-side. You only send the latest user message, and the context is injected automatically as a system message before your turns.
You manage full history in your app. Every call includes all prior turns. No server-side state. Ephemeral, wiped on page refresh or process restart.
Gateway stores & compresses context. Send only the new message. Context survives page refreshes, device switches, and process restarts. Shared across the Chat UI and API.
Step 1: Create a session
Create a session from the Memory page or via the API. Choose a size tier that fits your use case:
import httpx
# Create a memory session (uses your JWT, not an API key)
response = httpx.post(
"http://www.velona.in/memory/sessions",
headers={"Authorization": "Bearer <your-jwt>"},
json={
"name": "My Project Assistant",
"max_chars": 150000 # Medium tier
}
)
session = response.json()
memory_id = session["memory_id"] # e.g. "mem_4a7b2c3d1e9f0a8b"
print(f"Session created: {memory_id}")
Step 2: Attach to an inference call
Pass the memory_id in the velona extension field of any /gateway/v1/inference/run call:
import httpx
MEMORY_ID = "mem_4a7b2c3d1e9f0a8b"
# First turn
r1 = httpx.post(
"http://www.velona.in/gateway/v1/inference/run",
headers={"Authorization": "Bearer <your-key>"},
json={
"model": "openai/gpt-4o-mini",
"turns": [{"role": "user", "content": "My name is Rohan. Remember that."}],
"velona": {"memory_session": MEMORY_ID},
}
)
print(r1.json()["data"]["output"])
# Second turn, context from first turn is injected automatically
r2 = httpx.post(
"http://www.velona.in/gateway/v1/inference/run",
headers={"Authorization": "Bearer <your-key>"},
json={
"model": "openai/gpt-4o-mini",
"turns": [{"role": "user", "content": "What's my name?"}],
"velona": {"memory_session": MEMORY_ID},
}
)
print(r2.json()["data"]["output"]) # "Your name is Rohan."
Session management endpoints
Context compression
When a session's stored context exceeds its max_chars limit, the oldest exchanges are pruned automatically. Compression runs in the background after each append, it never adds latency to your API call. The compressed context is pre-computed and stored, and get_context_for_model() is synchronous and sub-millisecond on the hot path.
mem_ ID usable, future calls start fresh. Delete soft-deletes the session, the ID can no longer be used in inference calls.
Using memory in the Chat UI
The Velona Chat page has a built-in memory selector. Pick a session from the dropdown to attach it. The chat UI shows a "Context active" indicator and the session's usage percentage. You can also deep-link directly: /chat?memory=mem_abc123
Error handling
All errors use a consistent envelope regardless of endpoint. Use the error.code field for programmatic handling.
{
"request_id": "req_8f3a1c92b4e7",
"status": "error",
"error": {
"code": "INSUFFICIENT_CREDITS",
"message": "Your account balance is too low to complete this request.",
"docs": "http://www.velona.in/docs#error-handling"
},
"meta": {
"timestamp": "2026-05-14T10:00:00Z"
}
}
import httpx
response = httpx.post(
"http://www.velona.in/gateway/v1/inference/run",
headers={"Authorization": "Bearer <your-key>"},
json={"model": "openai/gpt-4o-mini", "turns": [{"role": "user", "content": "Hi"}]}
)
body = response.json()
if body["status"] == "error":
code = body["error"]["code"]
if code == "INSUFFICIENT_CREDITS":
print("Top up your wallet at /wallet")
elif code == "INVALID_KEY":
print("Check your API key")
elif code == "RATE_LIMIT_EXCEEDED":
print("Slow down, retry after", response.headers.get("Retry-After"), "seconds")
elif code in ("UPSTREAM_ERROR", "UPSTREAM_TIMEOUT"):
print("Provider issue, retry with exponential backoff")
else:
print(f"Error: {code} -- {body['error']['message']}")
else:
print(body["data"]["output"])
Rate limits
Velona enforces multiple layers of rate limiting to protect platform stability and prevent abuse. All limits return HTTP 429 with a Retry-After header.
Retry strategy
The gateway itself retries transient OpenRouter errors (5xx, timeouts) up to 3 times with exponential backoff and jitter before returning a 502 to your client. For 429 responses, check the Retry-After header and wait that many seconds:
import httpx, time
def call_with_retry(payload, max_retries=3):
for attempt in range(max_retries):
response = httpx.post(
"http://www.velona.in/gateway/v1/inference/run",
headers={"Authorization": "Bearer <your-key>"},
json=payload,
)
if response.status_code == 429:
wait = int(response.headers.get("Retry-After", 10))
time.sleep(wait)
continue
return response.json()
raise Exception("Max retries exceeded")
Developer tools
Free utilities (hashing, encoding, formatting, DNS/SSL/network inspection, crypto, QR codes), wallet-free, every tool reachable from your own application.
Calling the tools API
POST /tools/v1/<tool-name>, every tool in the fleet is exposed here (see table below).
Authenticate with the free Developer Tools API Key auto-issued to every account, visible any time on the
API Keys page, that key is gated by exactly one check
(your account exists and is active), never touches your wallet, and never expires. A regular billable API key also works here.
Accepts a single object or a bulk {"items": [...]} body
(capped at TOOLS_BULK_MAX_ITEMS, default 1000), bulk responses come back as {"results": [...]}.
dk_) only ever works against /tools/v1/*, it is rejected by /gateway/v1/* (billed AI inference), so it can never be used to run up wallet charges even if leaked.
# Single
curl -X POST http://www.velona.in/tools/v1/hash \
-H "Authorization: Bearer <your-dev-tools-key>" \
-H "Content-Type: application/json" \
-d '{"data": "hello"}'
# Bulk
curl -X POST http://www.velona.in/tools/v1/hash \
-H "Authorization: Bearer <your-dev-tools-key>" \
-H "Content-Type: application/json" \
-d '{"items": [{"data": "a"}, {"data": "b", "algorithm": "md5"}]}'
# is the "dk_..." key from /keys, free, no wallet, never expires.
# A regular billable API key ("key_..." / "sk-...") works here too.
ai_enhance field, but AI enhancement isn't available through /tools/v1/*, the field is ignored and you always get the base (non-AI) result.
All tools
SDKs & examples
Velona's gateway is fully OpenAI-compatible. Any library built for the OpenAI API works by changing two lines. No new SDK needed.
LangChain
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="openai/gpt-4o-mini",
openai_api_key="<your-velona-key>",
openai_api_base="http://www.velona.in/v1",
)
response = llm.invoke("Explain transformers in one paragraph.")
print(response.content)
LlamaIndex
from llama_index.llms.openai import OpenAI
llm = OpenAI(
model="openai/gpt-4o-mini",
api_key="<your-velona-key>",
api_base="http://www.velona.in/v1",
)
response = llm.complete("What is RAG?")
print(response.text)
Streaming with OpenAI SDK
from openai import OpenAI
client = OpenAI(
api_key="<your-velona-key>",
base_url="http://www.velona.in/v1",
)
with client.chat.completions.stream(
model="openai/gpt-4o-mini",
messages=[{"role": "user", "content": "Tell me a short story."}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
Environment setup
Store your key in an environment variable, never hardcode it:
# .env
VELONA_API_KEY=sk-<your-48-hex-key>
VELONA_BASE_URL=http://www.velona.in/v1