API Documentation

Complete reference for the RooyaLLM API. OpenAI-compatible interface that works with every major AI provider.

v1
HTTPS
JSON
Base URLhttps://llm.rooyai.com

Overview

RooyaLLM provides a unified, OpenAI-compatible API that routes requests to 100+ AI models through a single endpoint. One API key, one base URL — access every model we support.

FeatureDescription
Unified APISingle endpoint for every LLM - OpenAI-compatible interface
StreamingFull SSE streaming support with real-time token delivery
ASR / Speech-to-TextOpenAI-compatible /v1/audio/transcriptions for speech-to-text transcription (up to 25 MB)
TTS / Text-to-SpeechOpenAI-compatible /v1/audio/speech — converts text to audio. Streamed, zero-copy delivery. Billed per input character.
OCR / Document ExtractionMistral-powered /v1/ocr — extract structured text from PDFs and images (up to 50 MB, billed per page)
Image GenerationFLUX.1 & turbo image models via /v1/images/generations — OpenAI-compatible with gateway-side format conversion (PNG/JPEG/WebP) and compression
Cost TrackingAutomatic per-request cost calculation and budget enforcement
Rate LimitingPer-key RPM + TPD limits with Redis-backed sliding windows
3-Tier Auth CacheIn-memory -> Redis -> Database key validation (<1us hot path)
Model HealthAuto-synced model catalog with live health monitoring

Authentication

All API requests (except /v1/models and /health) require a valid API key via the Authorization header using the Bearer scheme.

Authorization: Bearer sk-your-api-key

Obtaining an API Key

  1. Sign up at the registration page
  2. Navigate to Dashboard → API Keys
  3. Click Create Key, set a name and optional budget limit
  4. Copy the key immediately — it is shown only once
⚠️
Security: API keys are hashed with SHA-256 before storage. The plaintext key cannot be recovered. Store it securely.

Key Format

sk-<random-alphanumeric-string>

Quick Start

The fastest way to get started — just point your OpenAI SDK to your gateway URL and set your API key:

python
from openai import OpenAI

client = OpenAI(
    base_url="https://llm.rooyai.com/v1",
    api_key="sk-your-api-key"
)

response = client.chat.completions.create(
    model="<your-chat-model>",
    messages=[{"role": "user", "content": "Hello!"}]
)
print(response.choices[0].message.content)

That's it. Every OpenAI SDK feature works — streaming, function calling, vision, and more.

Chat Completions

Creates a chat completion for a conversation. This is the primary endpoint for interacting with LLMs.

POST/v1/chat/completions

Request Body

ParameterTypeRequiredDefaultDescription
modelstring-Model ID. Use GET /v1/models to see all available models.
messagesarray-Array of message objects with role and content
temperaturenumber1.0Sampling temperature between 0 and 2
top_pnumber1.0Nucleus sampling between 0 and 1
ninteger1Number of completions to generate (1-128)
streambooleanfalseEnable Server-Sent Events streaming
stopstring | arraynullUp to 4 stop sequences
max_tokensinteger-Maximum tokens to generate
presence_penaltynumber0Penalty for new topics (-2 to 2)
frequency_penaltynumber0Penalty for repetition (-2 to 2)
userstring-Unique end-user identifier

Example Request

bash
curl https://llm.rooyai.com/v1/chat/completions \\
  -H "Authorization: Bearer sk-your-api-key" \\
  -H "Content-Type: application/json" \\
  -d '{
    "model": "<your-chat-model>",
    "messages": [
      {"role": "system", "content": "You are a helpful assistant."},
      {"role": "user", "content": "Explain quantum computing in simple terms."}
    ],
    "temperature": 0.7,
    "max_tokens": 500
  }'
json
{
  "id": "chatcmpl-abc123",
  "object": "chat.completion",
  "created": 1714000000,
  "model": "<model-id>",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Quantum computing uses qubits instead of traditional bits..."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 28,
    "completion_tokens": 150,
    "total_tokens": 178
  }
}

Completions (Legacy)

Creates a text completion. This is the legacy completions endpoint for non-chat models.

POST/v1/completions
ParameterTypeRequiredDefaultDescription
modelstring-Model ID
promptstring | array-The prompt(s) to complete
suffixstringnullText after the completion
max_tokensinteger16Maximum tokens to generate
temperaturenumber1.0Sampling temperature (0-2)
top_pnumber1.0Nucleus sampling (0-1)
ninteger1Number of completions (1-128)
streambooleanfalseEnable SSE streaming
stopstring | arraynullUp to 4 stop sequences
bash
curl https://llm.rooyai.com/v1/completions \\
  -H "Authorization: Bearer sk-your-api-key" \\
  -H "Content-Type: application/json" \\
  -d '{
    "model": "<your-completion-model>",
    "prompt": "Write a haiku about programming:",
    "max_tokens": 50,
    "temperature": 0.8
  }'

Embeddings

Generate vector embeddings for text input.

POST/v1/embeddings
ParameterTypeRequiredDefaultDescription
modelstring-Embedding model ID. Use GET /v1/models to see available embedding models.
inputstring | array-Text(s) to embed
encoding_formatstringfloatfloat or base64
userstring-End-user identifier
bash
curl https://llm.rooyai.com/v1/embeddings \\
  -H "Authorization: Bearer sk-your-api-key" \\
  -H "Content-Type: application/json" \\
  -d '{
    "model": "<your-embedding-model>",
    "input": "The quick brown fox jumps over the lazy dog"
  }'
json
{
  "object": "list",
  "data": [
    {
      "object": "embedding",
      "index": 0,
      "embedding": [0.0023, -0.0091, 0.0152, ...]
    }
  ],
  "model": "<embedding-model-id>",
  "usage": { "prompt_tokens": 9, "total_tokens": 9 }
}

List Models

Returns a list of all available models. No authentication required.

GET/v1/models
json
{
  "object": "list",
  "data": [
    { "id": "<model-id-1>", "object": "model", "created": 1714000000, "owned_by": "your-gateway" },
    { "id": "<model-id-2>", "object": "model", "created": 1714000000, "owned_by": "your-gateway" }
  ]
}

Get Model

Retrieve details for a specific model. No authentication required.

GET/v1/models/{model_id}
ParameterDescription
model_idThe model ID to retrieve. Use GET /v1/models to list all available IDs.
json
{
  "id": "<model-id>",
  "object": "model",
  "created": 1714000000,
  "owned_by": "your-gateway"
}

Model Health Status

Returns the full model catalog with live health status, availability, and latency data. No authentication required.

GET/v1/models/status
json
{
  "object": "list",
  "data": [
    {
      "id": "uuid",
      "model_id": "<model-id>",
      "model_type": "chat",
      "is_available": true,
      "health_status": "healthy",
      "last_health_latency_ms": 245
    }
  ],
  "total": 150, "healthy": 142, "down": 3, "unknown": 5
}

Audio Transcriptions (ASR)

NEW

Transcribes an audio file into text using speech recognition models. Fully OpenAI-compatible — just change base_url and api_key in your existing transcription code.

POST/v1/audio/transcriptionsmultipart/form-dataAuth required

Request Parameters

ParameterTypeRequiredDefaultDescription
filefile (binary)Audio file to transcribe. Maximum size: 25 MB.
modelstringAudio transcription model ID. Use GET /v1/models to list available speech-to-text models.
languagestringauto-detectISO 639-1 language code (e.g. en, ar, fr). Improves speed & accuracy when set.
response_formatstringverbose_jsonOutput format: verbose_json | json | text | srt | vtt.
temperaturenumber0Sampling temperature 0–1. 0 = deterministic greedy decoding.
promptstringOptional hint to guide the model style or continue a prior transcript.

Supported Audio Formats

ℹ️
Maximum file size: 25 MB. Files exceeding this limit return 413 Request Entity Too Large.
FormatMIME Types
MP3audio/mpeg, audio/mp3
MP4 / M4Aaudio/mp4, audio/m4a, audio/x-m4a
WAVaudio/wav, audio/wave
OGGaudio/ogg
FLACaudio/flac
WebMaudio/webm
AACaudio/aac
Opusaudio/opus

Response Formats

response_formatContent-TypeDescription
verbose_jsonapplication/json(Default) Full JSON with text, duration, language, and word-level segments. Recommended — used for accurate billing.
jsonapplication/jsonCompact JSON with just the text field.
texttext/plainRaw transcription text — no JSON wrapper.
srttext/plainSubRip subtitle format with timestamps.
vtttext/plainWebVTT subtitle format.

Live Endpoint Tester

Try it livePOST /v1/audio/transcriptions

Drag & drop an audio file, or browse

MP3, M4A, WAV, OGG, FLAC, WebM, AAC · max 25 MB

Example Requests (cURL)

bash
# verbose_json (recommended — includes duration for accurate billing)
curl https://llm.rooyai.com/v1/audio/transcriptions \
  -H "Authorization: Bearer sk-your-api-key" \
  -F "file=@/path/to/audio.mp3" \
  -F "model=whisper-large-v3-turbo" \
  -F "language=en" \
  -F "response_format=verbose_json"

# Plain text output
curl https://llm.rooyai.com/v1/audio/transcriptions \
  -H "Authorization: Bearer sk-your-api-key" \
  -F "file=@/path/to/meeting.m4a" \
  -F "model=whisper-large-v3-turbo" \
  -F "response_format=text"

# SRT subtitle format
curl https://llm.rooyai.com/v1/audio/transcriptions \
  -H "Authorization: Bearer sk-your-api-key" \
  -F "file=@/path/to/video.webm" \
  -F "model=whisper-large-v3-turbo" \
  -F "response_format=srt"

Example Responses

json
{
  "task": "transcribe",
  "language": "english",
  "duration": 42.5,
  "text": "Welcome to the API. This is a transcription test.",
  "segments": [
    {
      "id": 0,
      "start": 0.0,
      "end": 4.2,
      "text": " Welcome to the API.",
      "avg_logprob": -0.21,
      "no_speech_prob": 0.003
    },
    {
      "id": 1,
      "start": 4.2,
      "end": 8.1,
      "text": " This is a transcription test.",
      "avg_logprob": -0.18,
      "no_speech_prob": 0.001
    }
  ]
}

Billing

ASR requests are billed by audio duration (seconds), not tokens. The duration field in the verbose_json response is used directly:

cost = duration_seconds × (input_price_per_1M ÷ 1,000,000)
💡
Use response_format=verbose_json (the default) to get the duration field for most accurate billing. For text, srt, and vtt formats, billing falls back to an estimate derived from output character count.

SDK Examples

python
from openai import OpenAI

client = OpenAI(
    base_url="https://llm.rooyai.com/v1",
    api_key="sk-your-api-key"
)

# verbose_json — includes duration for accurate billing
with open("/path/to/audio.mp3", "rb") as audio_file:
    transcript = client.audio.transcriptions.create(
        model="whisper-large-v3-turbo",
        file=audio_file,
        language="en",                   # optional — improves speed & accuracy
        response_format="verbose_json",  # recommended
    )

print(transcript.text)
print(f"Language : {transcript.language}")
print(f"Duration : {transcript.duration:.1f}s")
for seg in transcript.segments:
    print(f"  [{seg.start:.1f}s -> {seg.end:.1f}s] {seg.text}")

# Plain text (no duration field returned)
with open("/path/to/audio.mp3", "rb") as audio_file:
    text = client.audio.transcriptions.create(
        model="whisper-large-v3-turbo",
        file=audio_file,
        response_format="text",
    )
print(text)

Error Responses

StatusReason
400Missing file or model field, or malformed multipart/form-data body
401Invalid or missing API key
402Insufficient credits or budget limit exceeded
413Audio file exceeds the 25 MB limit
429Rate limit exceeded
504Upstream transcription timed out (audio processing took > 300s)

Text to Speech (TTS)

NEW

Converts text into spoken audio using AI voice synthesis models. Fully OpenAI-compatible — drop in your existing client.audio.speech.create() calls by changing base_url and api_key. Audio is streamed zero-copy from the upstream to your client — no intermediate buffering.

POST/v1/audio/speechapplication/jsonAuth requiredStreaming response

Request Parameters

ParameterTypeRequiredDefaultDescription
modelstringTTS model ID. Use GET /v1/models to list available speech synthesis models.
inputstringText to synthesize. Maximum 4,096 characters.
voicestringVoice ID to use for synthesis. Standard voices: alloy, echo, fable, onyx, nova, shimmer. Custom providers may support additional voice IDs.
response_formatstringmp3Audio output format: mp3 | opus | aac | flac | wav | pcm.
speednumber1.0Playback speed multiplier. Range: 0.25 – 4.0. Values outside this range are clamped.

Standard Voices

The following voices are available across all models that support the OpenAI TTS API. Custom providers may expose additional voice IDs — pass any valid voice string.

voiceCharacteristics
alloyNeutral, balanced — general-purpose.
echoClear, articulate — great for narration.
fableExpressive, warm — storytelling.
onyxDeep, authoritative — professional announcements.
novaFriendly, upbeat — conversational assistants.
shimmerSoft, gentle — meditative or calm content.

Audio Output Formats

response_formatContent-TypeNotes
mp3audio/mpeg(Default) Widely compatible. Best for web playback and streaming.
opusaudio/opusLow latency, high quality. Ideal for real-time or voice chat apps.
aacaudio/aacGood quality at low bitrates. Preferred on iOS / Apple platforms.
flacaudio/flacLossless compression. Largest files — use when audio fidelity is critical.
wavaudio/wavUncompressed PCM. Maximum compatibility — large files.
pcmaudio/pcmRaw 16-bit PCM samples at 24 kHz. For direct DSP pipelines or custom audio players.

Live Endpoint Tester

Try it livePOST /v1/audio/speech

Example Requests (cURL)

bash
# ── 1. Basic MP3 (default format) — save to file ─────────────────────────────
curl https://llm.rooyai.com/v1/audio/speech \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "rooyai-tts",
    "input": "Hello! This is a test of the RooyaLLM text-to-speech API.",
    "voice": "alloy"
  }' \
  --output speech.mp3

# ── 2. Specify voice and format ────────────────────────────────────────────
curl https://llm.rooyai.com/v1/audio/speech \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "rooyai-tts",
    "input": "مرحباً! هذا اختبار لتقنية تحويل النص إلى كلام.",
    "voice": "onyx",
    "response_format": "opus",
    "speed": 1.0
  }' \
  --output speech.opus

# ── 3. WAV at slower speed ─────────────────────────────────────────────────
curl https://llm.rooyai.com/v1/audio/speech \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "rooyai-tts",
    "input": "Slow and clear narration for accessibility.",
    "voice": "nova",
    "response_format": "wav",
    "speed": 0.75
  }' \
  --output speech.wav

SDK Examples

python
from openai import OpenAI
from pathlib import Path

client = OpenAI(
    base_url="https://llm.rooyai.com/v1",
    api_key="sk-your-api-key"
)

# ── Option A: Save directly to a file (OpenAI SDK streaming helper) ───────
with client.audio.speech.with_streaming_response.create(
    model="rooyai-tts",
    voice="alloy",
    input="Hello! This is a test of the RooyaLLM text-to-speech API.",
    response_format="mp3",   # mp3 | opus | aac | flac | wav | pcm
    speed=1.0,               # 0.25 – 4.0
) as response:
    response.stream_to_file(Path("speech.mp3"))
print("Saved speech.mp3")

# ── Option B: Read into memory (small inputs only) ────────────────────────
audio = client.audio.speech.create(
    model="rooyai-tts",
    voice="nova",
    input="Short text for in-memory synthesis.",
    response_format="mp3",
)
audio_bytes = audio.read()
print(f"Received {len(audio_bytes):,} bytes of MP3 audio")

# ── Option C: Stream using requests (for custom handling) ─────────────────
import requests

with requests.post(
    f"https://llm.rooyai.com/v1/audio/speech",
    headers={
        "Authorization": "Bearer sk-your-api-key",
        "Content-Type": "application/json",
    },
    json={
        "model": "rooyai-tts",
        "input": "Streaming audio delivery with zero buffering.",
        "voice": "shimmer",
        "response_format": "opus",
        "speed": 1.1,
    },
    stream=True,
    timeout=90,
) as r:
    r.raise_for_status()
    with open("speech.opus", "wb") as f:
        for chunk in r.iter_content(chunk_size=4096):
            f.write(chunk)
print("Saved speech.opus")

Billing

TTS requests are billed by input character count, not tokens or audio duration. Billing is committed before the audio stream is piped to you — so even if you disconnect mid-stream, the synthesis cost is recorded.

cost = char_count × (input_price_per_1M ÷ 1,000,000)
💡
The standard reference rates are $15 / 1M characters for standard quality and $30 / 1M characters for HD quality (matching OpenAI tts-1 / tts-1-hd pricing). Your admin can configure custom per-model rates in the model pricing table.
ℹ️
Concurrency: The gateway allows up to 200 simultaneous TTS streams. Requests beyond this limit immediately return 429 Too Many Requests. Each request holds one socket open for the duration of the stream — use connection pooling in your client for high-throughput workloads.
ℹ️
Input limit: Maximum 4,096 characters per request (OpenAI TTS spec). For longer texts, split your content into chunks and stitch the audio segments together client-side.

Error Responses

StatusReason
400Missing required field (model, input, or voice), input exceeds 4,096 chars, invalid response_format, or non-numeric speed
401Invalid or missing API key
402Insufficient credits or per-key budget limit exceeded
429Too many concurrent TTS requests — the gateway allows up to 200 simultaneous streams
502Could not reach the TTS upstream — upstream connection refused or reset
504TTS upstream timed out — no response headers received within 90 seconds

OCR — Document Text Extraction

NEW

Extract structured text from PDFs and images using Mistral OCR. Supports URL-based documents, inline base64 data URIs, and direct file uploads up to 50 MB. Results are returned as Markdown — tables, headings, and lists are preserved.

POST/v1/ocrapplication/jsonAuth required
POST/v1/ocr/uploadmultipart/form-dataAuth required
ℹ️
Billing: OCR is billed per page processed, not per token. The usage_info.pages_processed field in every response shows exactly how many pages were charged.

Request Parameters — /v1/ocr

ParameterTypeRequiredDefaultDescription
modelstringmistral-ocr-latestOCR model ID. Currently only mistral-ocr-latest is supported.
documentobjectDocument to process. See Document Object below.
pagesarray | stringallPage selection (0-indexed). Array [0,1,4] or range string "0-5,8,10-12". Omit to process all pages.
include_image_base64booleanfalseWhen true, each page object includes a base64-encoded image of the rendered page.
image_limitintegerMaximum number of images to extract per page.
image_min_sizeintegerMinimum pixel dimension for an image to be extracted.
include_blocksbooleanfalseInclude raw layout blocks (bounding boxes, type) in the response.
extract_headerbooleanfalseExtract page header text as a separate field.
extract_footerbooleanfalseExtract page footer text as a separate field.
table_formatstringFormat for extracted tables: "markdown" | "html".
confidence_scores_granularitystringInclude OCR confidence scores: "word" | "page".

Document Object Types

The document field must include a type and the corresponding URL or ID field:

typeFieldDescription
document_urldocument_urlURL pointing to a publicly accessible PDF. Also accepts a base64 data URI: data:application/pdf;base64,…
image_urlimage_urlURL pointing to an image (JPEG, PNG, WebP, TIFF, BMP, GIF). Also accepts a base64 data URI: data:image/jpeg;base64,…
filefile_idPre-uploaded Mistral file ID obtained from the Mistral Files API (/v1/files).

Request Parameters — /v1/ocr/upload

All parameters are sent as multipart/form-data fields (strings, not JSON).

ParameterTypeRequiredDefaultDescription
filefile (binary)PDF or image file to OCR. Maximum size: 50 MB.
modelstringmistral-ocr-latestOCR model ID.
pagesstringallPage range string, e.g. "0-5,8".
include_image_base64stringfalse"true" | "false" — include rendered page images in response.
image_limitstringInteger string — max images per page.
image_min_sizestringInteger string — min pixel dimension to extract an image.
include_blocksstringfalse"true" | "false" — include layout blocks in response.
extract_headerstringfalse"true" | "false" — extract page headers.
extract_footerstringfalse"true" | "false" — extract page footers.
table_formatstring"markdown" | "html".
confidence_scores_granularitystring"word" | "page".

Supported File Formats

ℹ️
Maximum file size: 50 MB. Files exceeding this limit return 413 Request Entity Too Large.
FormatMIME TypesNotes
PDFapplication/pdfAll standard PDFs — scanned or native text
JPEGimage/jpeg, image/jpgPhotographs, scanned documents
PNGimage/pngScreenshots, diagrams
WebPimage/webpModern image format
TIFFimage/tiff, image/tifHigh-quality scans
BMPimage/bmpBitmap images
GIFimage/gifStatic GIF images (first frame only)

Live Endpoint Tester

Try it livePOST /v1/ocr

Example Requests — URL (cURL)

bash
# OCR a PDF from a public URL
curl https://llm.rooyai.com/v1/ocr \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "mistral-ocr-latest",
    "document": {
      "type": "document_url",
      "document_url": "https://example.com/document.pdf"
    }
  }'

# OCR an image from a URL
curl https://llm.rooyai.com/v1/ocr \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "mistral-ocr-latest",
    "document": {
      "type": "image_url",
      "image_url": "https://example.com/invoice.jpg"
    }
  }'

# Process only pages 0-2 and extract tables as Markdown
curl https://llm.rooyai.com/v1/ocr \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "mistral-ocr-latest",
    "document": {
      "type": "document_url",
      "document_url": "https://example.com/report.pdf"
    },
    "pages": [0, 1, 2],
    "table_format": "markdown"
  }'

Example Requests — File Upload (cURL)

bash
# Upload a local PDF and OCR it in one request
curl https://llm.rooyai.com/v1/ocr/upload \
  -H "Authorization: Bearer sk-your-api-key" \
  -F "file=@/path/to/document.pdf;type=application/pdf" \
  -F "model=mistral-ocr-latest"

# Upload a local image
curl https://llm.rooyai.com/v1/ocr/upload \
  -H "Authorization: Bearer sk-your-api-key" \
  -F "file=@/path/to/scan.png;type=image/png" \
  -F "model=mistral-ocr-latest"

# Upload with page range and table extraction
curl https://llm.rooyai.com/v1/ocr/upload \
  -H "Authorization: Bearer sk-your-api-key" \
  -F "file=@/path/to/report.pdf;type=application/pdf" \
  -F "model=mistral-ocr-latest" \
  -F "pages=0-5" \
  -F "table_format=markdown"

Example Response

json
{
  "model": "mistral-ocr-latest",
  "pages": [
    {
      "index": 0,
      "markdown": "# Invoice\n\n| Item | Price |\n|------|-------|\n| Widget | $9.99 |",
      "dimensions": { "dpi": 300, "height": 1056, "width": 816 },
      "images": []
    }
  ],
  "usage_info": {
    "pages_processed": 1,
    "doc_size_bytes": 45231
  }
}

SDK Examples

python
import requests

BASE_URL = "https://llm.rooyai.com"
API_KEY  = "sk-your-api-key"

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

# ── Option A: OCR a PDF from a URL ──────────────────────────────────────────
response = requests.post(
    f"{BASE_URL}/v1/ocr",
    headers={**headers, "Content-Type": "application/json"},
    json={
        "model": "mistral-ocr-latest",
        "document": {
            "type": "document_url",
            "document_url": "https://example.com/document.pdf",
        },
        "table_format": "markdown",      # optional
    },
    timeout=300,
)
data = response.json()
print(f"Pages processed: {data['usage_info']['pages_processed']}")
for page in data["pages"]:
    print(f"\n--- Page {page['index'] + 1} ---")
    print(page["markdown"])             # full Markdown for this page

# ── Option B: Upload a local file and OCR ───────────────────────────────────
with open("/path/to/document.pdf", "rb") as f:
    upload_response = requests.post(
        f"{BASE_URL}/v1/ocr/upload",
        headers=headers,
        files={"file": ("document.pdf", f, "application/pdf")},
        data={
            "model": "mistral-ocr-latest",
            "pages": "0-9",              # first 10 pages only (optional)
        },
        timeout=300,
    )
upload_data = upload_response.json()
full_text = "\n\n".join(p["markdown"] for p in upload_data["pages"])
print(full_text)

Billing

OCR is billed by pages processed, not tokens. Each page in the response increments the counter regardless of page content size.

cost = pages_processed × (input_price_per_1M ÷ 1,000,000)
💡
Use the pages parameter to process only the pages you need — e.g. "pages": [0, 1, 2] for the first 3 pages only. This reduces both cost and processing time for large documents.
ℹ️
Concurrency: The gateway allows up to 100 simultaneous OCR requests. If capacity is exceeded, requests queue for up to 50 slots then return 429. Large documents may take up to 700 seconds — set your HTTP client timeout accordingly.

Error Responses

StatusReason
400Invalid request body, missing required fields, or unsupported document type
401Invalid or missing API key
402Insufficient credits or budget limit exceeded
413File exceeds the 50 MB limit (upload endpoint)
415Unsupported file type (upload endpoint — send PDF, JPEG, PNG, WebP, TIFF, BMP, or GIF)
429Too many concurrent OCR requests — retry in a moment
503OCR service not configured on this gateway
504OCR timed out — use the pages parameter to process fewer pages at once

Image Generation

NEW

Generate images from text prompts using diffusion models like FLUX.1 and others. Fully OpenAI-compatible — just change base_url and api_key in your existing image generation code.

ℹ️
Gateway-side format conversion: Pass output_format ("jpeg" or "webp") in your request body and the gateway converts the image after the model responds — no extra cost, no client-side processing needed. Use output_quality (1–100) to control compression.
POST/v1/images/generationsapplication/jsonAuth required

Request Parameters

ParameterTypeRequiredDefaultDescription
promptstringText description of the image to generate. Be descriptive for best results.
modelstringImage model ID. Use GET /v1/models to list all available image generation models.
ninteger1Number of images to generate (1–10).
sizestring1024x1024Output dimensions. Common values: 1024x1024, 1792x1024, 1024x1792. Support varies by model.
response_formatstringb64_json"b64_json" (recommended) or "url". Use b64_json to get the image bytes directly.
qualitystringModel quality hint (e.g. "hd"). Support and accepted values vary by model.
stylestringStyle hint (e.g. "vivid", "natural"). Support varies by model.
output_formatstring[Gateway-only] Convert the returned image to "png", "jpeg", or "webp". Applied after the upstream responds — no extra cost.
output_qualityinteger85[Gateway-only] Compression quality (1–100) for jpeg/webp output_format. Ignored for png. Default: 85.

Common Image Sizes

Supported sizes vary by model. Always check the model documentation or test with your chosen model.

sizeAspect RatioNotes
1024×10241:1Square. Default and most widely supported.
1792×102416:9Landscape / widescreen.
1024×17929:16Portrait / tall.
512×5121:1Smaller square. Supported by some models only.

Gateway-Side Output Formats

Set output_format to convert the returned image on the gateway before delivery. No additional model call is made.

FormatMIME TypeNotes
pngimage/pngLossless. Default output from most diffusion models.
jpegimage/jpegLossy. Smaller files. Good for web delivery. Use output_quality to tune.
webpimage/webpLossy (with lossless option). Excellent for modern browsers. Use output_quality to tune.

Live Endpoint Tester

Try it livePOST /v1/images/generations

Example Requests (cURL)

bash
# ── 1. Standard PNG — save directly to file ──────────────────────────────
curl https://llm.rooyai.com/v1/images/generations \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "flux-schnell",
    "prompt": "a majestic mountain at golden hour, photorealistic, 8k",
    "n": 1,
    "size": "1024x1024",
    "response_format": "b64_json"
  }' | python3 -c "
import sys, json, base64
d = json.load(sys.stdin)
open('output.png', 'wb').write(base64.b64decode(d['data'][0]['b64_json']))
print('Saved output.png')
"

# ── 2. JPEG with 80% quality (gateway converts for you) ───────────────────
curl https://llm.rooyai.com/v1/images/generations \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "flux-schnell",
    "prompt": "aerial view of a tropical island, turquoise water, cinematic",
    "n": 1,
    "size": "1792x1024",
    "response_format": "b64_json",
    "output_format": "jpeg",
    "output_quality": 80
  }' | python3 -c "
import sys, json, base64
d = json.load(sys.stdin)
open('output.jpg', 'wb').write(base64.b64decode(d['data'][0]['b64_json']))
print('Saved output.jpg')
"

# ── 3. WebP — smallest file size, great for web delivery ──────────────────
curl https://llm.rooyai.com/v1/images/generations \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "flux-schnell",
    "prompt": "futuristic city skyline at night, neon lights, ultra detailed",
    "n": 1,
    "size": "1024x1024",
    "response_format": "b64_json",
    "output_format": "webp",
    "output_quality": 90
  }' | python3 -c "
import sys, json, base64
d = json.load(sys.stdin)
open('output.webp', 'wb').write(base64.b64decode(d['data'][0]['b64_json']))
print('Saved output.webp')
"

# ── NOTE: n and output_quality MUST be numbers, not strings ───────────────
# ✅ Correct:  "n": 1,  "output_quality": 80
# ❌ Wrong:    "n": "1", "output_quality": "80"   (causes 400 validation error)
# In n8n: use "Send Body" → "JSON" mode, NOT key-value/form mode.

Example Response

The _format and _mime_type fields are only present when output_format was specified.

json
{
  "created": 1714000000,
  "data": [
    {
      "b64_json": "iVBORw0KGgoAAAANSUh…[base64-encoded image]",
      "_format": "jpeg",
      "_mime_type": "image/jpeg"
    }
  ],
  "_output_format": "jpeg",
  "_mime_type": "image/jpeg"
}

SDK Examples

python
import base64
import requests
from pathlib import Path
from openai import OpenAI

BASE_URL = "https://llm.rooyai.com"
API_KEY  = "sk-your-api-key"

client = OpenAI(base_url=f"{BASE_URL}/v1", api_key=API_KEY)

# ── Option A: Standard PNG via OpenAI SDK ────────────────────────────────────
response = client.images.generate(
    model="flux-schnell",
    prompt="a majestic mountain at golden hour, photorealistic, 8k",
    n=1,                      # integer — NOT a string
    size="1024x1024",
    response_format="b64_json",
)
Path("output.png").write_bytes(base64.b64decode(response.data[0].b64_json))
print("Saved output.png")

# ── Option B: JPEG with quality control (via requests — OpenAI SDK strips
#             unknown fields like output_format / output_quality) ────────────
resp = requests.post(
    f"{BASE_URL}/v1/images/generations",
    headers={"Authorization": f"Bearer {API_KEY}",
             "Content-Type": "application/json"},
    json={
        "model": "flux-schnell",
        "prompt": "aerial view of a tropical island, turquoise water, cinematic",
        "n": 1,               # integer
        "size": "1792x1024",
        "response_format": "b64_json",
        "output_format": "jpeg",
        "output_quality": 80, # integer 1–100
    },
    timeout=120,
)
resp.raise_for_status()
data = resp.json()
img_bytes = base64.b64decode(data["data"][0]["b64_json"])
Path("output.jpg").write_bytes(img_bytes)
print(f"Saved output.jpg ({len(img_bytes) // 1024} KB)")

# ── Option C: WebP (smallest for web delivery) ────────────────────────────
resp = requests.post(
    f"{BASE_URL}/v1/images/generations",
    headers={"Authorization": f"Bearer {API_KEY}",
             "Content-Type": "application/json"},
    json={
        "model": "flux-schnell",
        "prompt": "futuristic city skyline at night, neon lights, ultra detailed",
        "n": 1,
        "size": "1024x1024",
        "response_format": "b64_json",
        "output_format": "webp",
        "output_quality": 90,
    },
    timeout=120,
)
resp.raise_for_status()
data = resp.json()
Path("output.webp").write_bytes(base64.b64decode(data["data"][0]["b64_json"]))
print("Saved output.webp")

# ── Option D: Generate multiple images concurrently (asyncio + httpx) ────────
# The gateway supports up to 20 concurrent image gen requests per API key.
# Use asyncio to fire them in parallel — total wall time ≈ 1 image time.
import asyncio, httpx

async def generate_image(client: httpx.AsyncClient, prompt: str, index: int):
    resp = await client.post(
        f"{BASE_URL}/v1/images/generations",
        json={
            "model": "flux-schnell",
            "prompt": prompt,
            "n": 1,
            "size": "1024x1024",
            "response_format": "b64_json",
            "output_format": "jpeg",
            "output_quality": 80,
        },
        timeout=120,
    )
    resp.raise_for_status()
    data = resp.json()
    Path(f"batch_{index}.jpg").write_bytes(
        base64.b64decode(data["data"][0]["b64_json"])
    )
    print(f"Saved batch_{index}.jpg")

async def main():
    prompts = [
        "a red rose in the rain, macro photography",
        "a golden desert dune at sunrise, epic scale",
        "a futuristic Tokyo street at night, neon rain",
    ]
    headers = {"Authorization": f"Bearer {API_KEY}",
               "Content-Type": "application/json"}
    async with httpx.AsyncClient(headers=headers) as client:
        await asyncio.gather(*[
            generate_image(client, p, i) for i, p in enumerate(prompts)
        ])
    print("All images saved!")

asyncio.run(main())

Billing

Image generation is billed per image (per n), not per token. Cost is looked up from the model's input_price_per_1M field:

cost = n × (input_price_per_1M ÷ 1,000,000)
💡
Gateway-side format conversion (output_format / output_quality) is performed after billing — only the original image generation is charged. Converting PNG → JPEG or WebP incurs no additional API cost.
ℹ️
Tips for better images:
  • Be descriptive in your prompt — include style, lighting, composition, and quality hints.
  • Add keywords like photorealistic, 8k, cinematic, ultra detailed for higher quality outputs.
  • Use output_format: "webp" for web delivery — typically 25-35% smaller than PNG at near-identical quality.
  • Use output_quality: 75-85 for JPEG/WebP — a good balance of size vs quality.

Error Responses

StatusReason
400Missing or invalid request field (e.g. empty prompt, unsupported size)
401Invalid or missing API key
402Insufficient credits or budget limit exceeded
429Rate limit or concurrent request limit exceeded
503Service temporarily unavailable (circuit breaker open)
504Image generation timed out — the model took too long to respond

Health Check

Server health endpoint for monitoring and load balancers.

GET/health
json
{
  "status": "ok",
  "timestamp": "2026-04-25T02:00:00.000Z",
  "uptime": 86400,
  "memory": { "rss": 128, "heap": 64 }
}

Streaming (SSE)

Set "stream": true to receive responses as Server-Sent Events in real time.

How It Works

  1. The server responds with Content-Type: text/event-stream
  2. Each chunk is a data: line containing a JSON delta
  3. The stream terminates with data: [DONE]

cURL Example

bash
curl https://llm.rooyai.com/v1/chat/completions \\
  -H "Authorization: Bearer sk-your-api-key" \\
  -H "Content-Type: application/json" \\
  -d '{
    "model": "<your-chat-model>",
    "messages": [{"role": "user", "content": "Count to 5"}],
    "stream": true
  }'

Tool Calling

NEW

Tool calling (also called function calling) lets the model invoke your own functions during a conversation. The model decides when a tool is relevant, returns a structured invocation request, and you execute it — then pass the result back for a final human-readable response.

ℹ️
Supported models: Tool calling works on any model that supports the OpenAI function-calling spec. Check GET /v1/models for the full list of available models. Models that do not support tool calling will return a normal text response and ignore the tools field.

How It Works

  1. You define tools — describe functions as JSON Schema objects in the tools array.
  2. Model decides — if the user's message warrants a tool call, the response has finish_reason: "tool_calls" and a tool_calls array.
  3. You execute — call your actual function with the arguments the model provided.
  4. Send results back — append a role: "tool" message with the result and make a second API call.
  5. Final response — the model synthesises your tool result into a natural-language reply.

Request Parameters

ParameterTypeRequiredDefaultDescription
toolsarray-Array of tool definitions the model can call. Each entry has type=function and a function object.
tool_choicestring | object"auto""none" disables tool calling, "auto" lets the model decide, "required" forces a tool call, or { type: "function", function: { name } } to force a specific tool.
parallel_tool_callsbooleantrueWhether the model may invoke multiple tools in a single response turn.

Tool Definition Fields

Each entry in the tools array:

ParameterTypeRequiredDescription
type"function"Always "function" - the only tool type currently supported.
function.namestringName of the function. Use snake_case (e.g. get_weather).
function.descriptionstringHuman-readable description of what the function does. Helps the model decide when to call it.
function.parametersobjectJSON Schema object describing the function's parameters. Use 'required' array to mark mandatory args.

Tool Call Response Fields

When the model invokes a tool, each item in message.tool_calls contains:

ParameterTypeRequiredDescription
idstringUnique ID for this tool call. Must be passed back as tool_call_id in the follow-up message.
type"function"Always "function".
function.namestringName of the function the model wants to call.
function.argumentsstringJSON-encoded string containing the argument values. Parse with JSON.parse() before calling your function.

Basic Example — cURL

bash
curl https://llm.rooyai.com/v1/chat/completions \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "<your-chat-model>",
    "messages": [
      {"role": "system", "content": "You are a tool-calling assistant. Call tools when relevant."},
      {"role": "user",   "content": "What is the weather in Cairo right now?"}
    ],
    "tools": [
      {
        "type": "function",
        "function": {
          "name": "get_weather",
          "description": "Get current weather for a city",
          "parameters": {
            "type": "object",
            "properties": {
              "city": {"type": "string", "description": "City name"},
              "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
            },
            "required": ["city"]
          }
        }
      }
    ],
    "tool_choice": "auto",
    "parallel_tool_calls": true,
    "temperature": 0.2
  }'

Response

json
{
  "id": "chatcmpl-abc123",
  "object": "chat.completion",
  "model": "<model-id>",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "id": "call_xyz789",
            "type": "function",
            "function": {
              "name": "get_weather",
              "arguments": "{\"city\": \"Cairo\", \"unit\": \"celsius\"}"
            }
          }
        ]
      },
      "finish_reason": "tool_calls"
    }
  ],
  "usage": {
    "prompt_tokens": 95,
    "completion_tokens": 22,
    "total_tokens": 117
  }
}

Full Multi-Turn Example

A complete 4-step agentic loop: define tools → first call → execute function → send result back.

python
from openai import OpenAI
import json

client = OpenAI(
    base_url="https://llm.rooyai.com/v1",
    api_key="sk-your-api-key"
)

# -- Step 1: Define your tools ----------------------------
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Get current weather for a city",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {"type": "string", "description": "City name"},
                    "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
                },
                "required": ["city"]
            }
        }
    }
]

messages = [
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user",   "content": "What is the weather in Cairo?"}
]

# -- Step 2: First call - model returns tool_calls --------
response = client.chat.completions.create(
    model="<your-chat-model>",
    messages=messages,
    tools=tools,
    tool_choice="auto"
)

assistant_msg = response.choices[0].message
print(f"finish_reason: {response.choices[0].finish_reason}")
# Output: finish_reason: tool_calls

# -- Step 3: Execute your real function ------------------
def get_weather(city, unit="celsius"):
    return {"city": city, "temperature": 32, "unit": unit, "condition": "sunny"}

tool_results = []
for tc in assistant_msg.tool_calls:
    args = json.loads(tc.function.arguments)
    result = get_weather(**args)
    tool_results.append({
        "role": "tool",
        "tool_call_id": tc.id,
        "content": json.dumps(result)
    })

# -- Step 4: Second call - send results back --------------
messages.append(assistant_msg)
messages.extend(tool_results)

final = client.chat.completions.create(
    model="<your-chat-model>",
    messages=messages,
    tools=tools
)

print(final.choices[0].message.content)
# Output: "The current weather in Cairo is 32degC and sunny."

Parallel Tool Calls

Set parallel_tool_calls: true to let the model invoke multiple tools in a single response — useful for gathering independent data sources simultaneously.

finish_reason Values

finish_reasonMeaning
stopNormal text response — no tool was called.
tool_callsModel wants to invoke one or more tools. Parse message.tool_calls.
lengthResponse was cut off by max_tokens — retry with a higher limit.
content_filterResponse blocked by content policy.
💡
Tip: Always check finish_reason before reading message.content. When it is tool_calls, message.content will be null.

Vision / Images

NEW

Send images alongside text in any chat completion request. Vision-capable models can analyse, describe, compare, and reason about images from a public URL or an inline base64-encoded file.

ℹ️
Supported models: Vision works on any model that accepts multimodal inputs. Use GET /v1/models to check which models are available on your gateway. Text-only models will return a 400 error if you include images — check the model's capabilities before sending.

How It Works

Instead of passing content as a plain string, pass it as an array of content parts. Each part is either a text part or an image_url part. You can mix multiple text and image parts in any order.

Content Part Types

Part typeRequired fieldsDescription
texttype, textA plain text segment. Equivalent to a normal string message.
image_urltype, image_url.urlAn image. url is either a public HTTPS URL or a base64 data URL (data:image/jpeg;base64,…).

Image Detail Level

Set image_url.detail to control quality vs token cost. Defaults to "auto".

detailBehaviourToken cost
autoModel decides based on image size (recommended)Varies
lowResizes to 512×512 — fast and cheap~85 tokens flat
highFull resolution with tiled analysis — most accurateHigher — depends on image size

Example — Image from URL (cURL)

bash
curl https://llm.rooyai.com/v1/chat/completions \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "<your-chat-model>",
    "messages": [
      {
        "role": "user",
        "content": [
          { "type": "text", "text": "What is in this image?" },
          {
            "type": "image_url",
            "image_url": {
              "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/47/PNG_transparency_demonstration_1.png/280px-PNG_transparency_demonstration_1.png",
              "detail": "auto"
            }
          }
        ]
      }
    ],
    "max_tokens": 300
  }'

Example — Inline Base64 Image (cURL)

bash
curl https://llm.rooyai.com/v1/chat/completions \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "<your-chat-model>",
    "messages": [
      {
        "role": "user",
        "content": [
          { "type": "text", "text": "Describe this image in detail." },
          {
            "type": "image_url",
            "image_url": {
              "url": "data:image/jpeg;base64,<YOUR_BASE64_STRING>"
            }
          }
        ]
      }
    ]
  }'

SDK Examples

python
from openai import OpenAI
import base64

client = OpenAI(
    base_url="https://llm.rooyai.com/v1",
    api_key="sk-your-api-key"
)

# ── Option A: Image URL ──────────────────────────────────────────
response = client.chat.completions.create(
    model="<your-chat-model>",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "What is in this image?"},
                {
                    "type": "image_url",
                    "image_url": {
                        "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/47/PNG_transparency_demonstration_1.png/280px-PNG_transparency_demonstration_1.png",
                        "detail": "auto"  # "auto", "low", or "high"
                    }
                }
            ]
        }
    ],
    max_tokens=300
)
print(response.choices[0].message.content)

# ── Option B: Local file as base64 ──────────────────────────────
with open("image.jpg", "rb") as f:
    image_data = base64.b64encode(f.read()).decode("utf-8")

response = client.chat.completions.create(
    model="<your-chat-model>",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "Describe this image."},
                {
                    "type": "image_url",
                    "image_url": {"url": f"data:image/jpeg;base64,{image_data}"}
                }
            ]
        }
    ]
)
print(response.choices[0].message.content)

Base64-Only Examples (Local File)

Use these when you have a local image file that is not publicly accessible on the internet. The image is read from disk, encoded to a data:image/…;base64,… data URL, and sent inline in the request body.

python
from openai import OpenAI
import base64

client = OpenAI(
    base_url="https://llm.rooyai.com/v1",
    api_key="sk-your-api-key"
)

# Read the image file and encode it to a base64 data URL
with open("image.jpg", "rb") as f:
    image_data = base64.b64encode(f.read()).decode("utf-8")

# Build the data URL — the gateway accepts any image MIME type
data_url = f"data:image/jpeg;base64,{image_data}"

response = client.chat.completions.create(
    model="<your-chat-model>",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "What is in this image?"},
                {
                    "type": "image_url",
                    "image_url": {
                        "url": data_url,
                        "detail": "auto"  # "auto" | "low" | "high"
                    }
                }
            ]
        }
    ],
    max_tokens=300
)

print(response.choices[0].message.content)
# usage.prompt_tokens will include the image token cost
print(f"Tokens used: {response.usage.prompt_tokens} prompt / {response.usage.completion_tokens} completion")
💡
Tips:
  • Use public URLs when possible — no payload overhead.
  • Use base64 for private images or local files. Prefix: data:image/jpeg;base64,…
  • Supported formats: JPEG, PNG, GIF, WebP.
  • Images count against your prompt tokens. Use detail: "low" for large images when high accuracy is not needed.
  • You can include multiple images in a single message — just add more image_url parts.

Rate Limiting

The gateway enforces rate limits at two levels:

1. IP-Based Rate Limit

120 requests per minute per IP address. Applies to all requests regardless of authentication.

2. Per-Key Rate Limits

Each API key has configurable limits set at creation time:

LimitFreeProEnterprise
Requests per minute (RPM)6030010,000
Tokens per day (TPD)100,0001,000,000100,000,000

Rate Limit Headers

X-RateLimit-Limit-RPM: 60
X-RateLimit-Remaining-RPM: 55
X-RateLimit-Limit-TPD: 100000
X-RateLimit-Remaining-TPD: 98500

When Rate Limited

json
{
  "error": {
    "message": "Rate limit exceeded",
    "type": "rate_limit_error"
  }
}

HTTP Status: 429 Too Many Requests · Header: Retry-After: <seconds>

Budget Controls

API keys can have an optional budget limit to cap spending.

  • Set during key creation in the dashboard
  • Enforced in real time via Redis
  • When exceeded, requests return 402 Payment Required
json
{
  "error": {
    "message": "Budget limit exceeded for this API key",
    "type": "budget_exceeded"
  }
}

Error Handling

All errors follow a consistent format:

json
{
  "error": {
    "message": "Human-readable error description",
    "type": "error_type"
  }
}

Error Types & HTTP Status Codes

StatusTypeDescription
400invalid_request_errorMalformed request body or invalid parameters
401invalid_request_errorMissing, invalid, inactive, or expired API key
402budget_exceededAPI key budget limit reached
429rate_limit_errorRPM/TPD or IP rate limit exceeded
500server_errorInternal server error
504timeout_errorRequest timed out (30s default, 300s for streams)

SDK Integration

Python

bash
pip install openai
python
from openai import OpenAI

client = OpenAI(
    base_url="https://llm.rooyai.com/v1",
    api_key="sk-your-api-key"
)

# Chat completion
response = client.chat.completions.create(
    model="<your-chat-model>",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "What is machine learning?"}
    ],
    temperature=0.7,
    max_tokens=500
)
print(response.choices[0].message.content)

# Embeddings
embedding = client.embeddings.create(
    model="<your-embedding-model>",
    input="Hello world"
)
print(f"Dimensions: {len(embedding.data[0].embedding)}")

JavaScript / TypeScript

bash
npm install openai
typescript
import OpenAI from 'openai';

const client = new OpenAI({
  baseURL: 'https://llm.rooyai.com/v1',
  apiKey: 'sk-your-api-key',
});

// Chat completion
const response = await client.chat.completions.create({
  model: '<your-chat-model>',
  messages: [{ role: 'user', content: 'Explain REST APIs' }],
  max_tokens: 500,
});
console.log(response.choices[0].message.content);

// Streaming
const stream = await client.chat.completions.create({
  model: '<your-chat-model>',
  messages: [{ role: 'user', content: 'Hello!' }],
  stream: true,
});

for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content || '');
}

cURL

bash
# Chat completion
curl https://llm.rooyai.com/v1/chat/completions \\
  -H "Authorization: Bearer sk-your-api-key" \\
  -H "Content-Type: application/json" \\
  -d '{
    "model": "<your-chat-model>",
    "messages": [{"role": "user", "content": "Hello!"}]
  }'

# List models (no auth required)
curl https://llm.rooyai.com/v1/models

# Embeddings
curl https://llm.rooyai.com/v1/embeddings \\
  -H "Authorization: Bearer sk-your-api-key" \\
  -H "Content-Type: application/json" \\
  -d '{
    "model": "<your-embedding-model>",
    "input": "Hello world"
  }'

Best Practices

1. Use Streaming for Long Responses

Streaming provides a better UX and avoids timeouts for long completions. The stream timeout is 300 seconds vs 30 seconds for non-streaming.

2. Set Budget Limits

Protect against runaway costs by setting a budget limit on each API key in the dashboard.

3. Handle Rate Limits Gracefully

Implement exponential backoff when you receive 429 responses:

python
import time

def call_with_retry(fn, max_retries=3):
    for attempt in range(max_retries):
        try:
            return fn()
        except RateLimitError:
            wait = 2 ** attempt
            time.sleep(wait)
    raise Exception("Max retries exceeded")

4. Monitor Your Usage

Check the Dashboard for real-time cost tracking, per-model breakdowns, and request volume charts.

5. Rotate Keys Periodically

Create new keys and deactivate old ones regularly. Manage all keys from the dashboard without downtime.

6. Check Model Health Before Calling

Use GET /v1/models/status to check model availability before making requests.

Available Models

The table below is fetched live from your gateway. Use GET /v1/models for the authoritative up-to-date list at any time.

Model TypeDescription
Chat / InstructionLarge language models for conversational tasks, reasoning, and generation.
Text CompletionLegacy completion models for prompt-based text generation.
EmbeddingsVector embedding models for semantic search and similarity.
Speech-to-Text (ASR)Audio transcription models via /v1/audio/transcriptions.
Image GenerationDiffusion-based image models (FLUX, SDXL, etc.) via /v1/images/generations.
💡
Full list available via GET /v1/models
Request TypeTimeout
Non-streaming requests30 seconds
Streaming requests300 seconds (5 minutes)
HeaderDescription
X-RateLimit-Limit-RPMYour RPM limit for this key
X-RateLimit-Remaining-RPMRemaining requests this minute
X-RateLimit-Limit-TPDYour TPD limit for this key
X-RateLimit-Remaining-TPDRemaining tokens today
X-Request-IDUnique request identifier for debugging
Retry-AfterSeconds to wait (on 429 responses)

RooyaLLM — One API for Every AI Model