APIarium / app /templates /docs.html
rnilkyway's picture
feat(images,videos): unified OpenAI-style image+video endpoints
7a00ea1
Raw
History Blame Contribute Delete
51.8 kB
<!DOCTYPE html>
<html lang="en" class="antialiased scroll-smooth">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>APIarium | API Documentation</title>
<meta name="description" content="APIarium API documentation β€” OpenAI-compatible endpoints, supported models (Veo 3 video, Gemini 3 image, GPT, Claude, etc.), and code examples.">
<!-- Professional Typography -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'sans-serif'],
mono: ['JetBrains Mono', 'monospace'],
},
colors: {
gray: {
900: '#0A0A0A',
800: '#171717',
700: '#262626',
600: '#404040',
}
}
}
}
}
</script>
<style>
body {
background-color: #000000;
color: #ededed;
}
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #262626; border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: #404040; }
@keyframes slideUp {
from { transform: translate(-50%, 100%); opacity: 0; }
to { transform: translate(-50%, 0); opacity: 1; }
}
.toast-enter { animation: slideUp 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards; }
.bg-grid-pattern {
background-image: linear-gradient(to right, #ffffff05 1px, transparent 1px),
linear-gradient(to bottom, #ffffff05 1px, transparent 1px);
background-size: 40px 40px;
mask-image: radial-gradient(circle at center, black, transparent 80%);
-webkit-mask-image: radial-gradient(circle at center, black, transparent 80%);
}
/* Code block styling */
pre {
font-family: 'JetBrains Mono', monospace;
font-size: 12.5px;
line-height: 1.55;
white-space: pre;
overflow-x: auto;
color: #d4d4d4;
}
pre .tk-key { color: #93c5fd; } /* JSON keys β†’ light blue */
pre .tk-str { color: #86efac; } /* strings β†’ green */
pre .tk-num { color: #fbbf24; } /* numbers β†’ amber */
pre .tk-bool { color: #f0abfc; } /* booleans β†’ pink */
pre .tk-cmt { color: #6b7280; font-style: italic; }
pre .tk-fn { color: #fbbf24; } /* function names */
pre .tk-kw { color: #c4b5fd; } /* keywords */
/* Anchor offset under sticky header */
section[id] { scroll-margin-top: 80px; }
/* TOC active state */
.toc-link.active {
color: #fff;
background: rgba(255,255,255,0.05);
border-left-color: #fbbf24;
}
</style>
</head>
<body class="min-h-screen flex flex-col relative">
<div class="absolute inset-0 bg-grid-pattern pointer-events-none z-0"></div>
<header class="sticky top-0 z-50 bg-black/80 backdrop-blur-md border-b border-white/10">
<div class="max-w-7xl mx-auto px-6 h-16 flex items-center justify-between">
<a href="/" class="flex items-center gap-3 group">
<span class="text-2xl" aria-label="APIarium">🐝</span>
<span class="font-semibold text-lg tracking-tight group-hover:text-amber-300 transition-colors">APIarium</span>
<span class="ml-2 px-2 py-0.5 text-[10px] font-mono font-medium bg-white/10 text-white rounded-full border border-white/20">v1.0.0</span>
</a>
<nav class="hidden sm:flex items-center gap-6 text-sm font-medium text-gray-400">
<a href="/" class="hover:text-white transition-colors">Home</a>
<a href="/docs" class="text-white">Documentation</a>
<a href="/openapi-docs" class="hover:text-white transition-colors">Swagger</a>
</nav>
</div>
</header>
<main class="flex-grow w-full max-w-7xl mx-auto px-6 py-12 relative z-10">
<!-- Hero -->
<section class="pb-10 border-b border-gray-800 mb-10">
<div class="flex items-center gap-2 text-xs font-mono uppercase tracking-wider text-amber-400 mb-3">
<span class="w-1.5 h-1.5 rounded-full bg-amber-400"></span>
Reference
</div>
<h1 class="text-4xl sm:text-5xl font-semibold tracking-tight text-white mb-4 leading-tight">
API Documentation
</h1>
<p class="text-lg text-gray-400 leading-relaxed max-w-3xl">
Everything you need to integrate with APIarium β€” an OpenAI-compatible gateway that fans out to GPT, Claude, Gemini, DeepSeek, Veo 3 video, Gemini 3 image and more, behind a single Bearer token.
</p>
<div class="flex flex-wrap gap-2 mt-6">
<span class="px-3 py-1 text-xs font-mono bg-blue-500/10 text-blue-300 border border-blue-500/20 rounded-md">OpenAI-compatible</span>
<span class="px-3 py-1 text-xs font-mono bg-emerald-500/10 text-emerald-300 border border-emerald-500/20 rounded-md">Streaming</span>
<span class="px-3 py-1 text-xs font-mono bg-amber-500/10 text-amber-300 border border-amber-500/20 rounded-md">Veo 3 video</span>
<span class="px-3 py-1 text-xs font-mono bg-fuchsia-500/10 text-fuchsia-300 border border-fuchsia-500/20 rounded-md">Gemini 3 image</span>
<span class="px-3 py-1 text-xs font-mono bg-white/5 text-gray-300 border border-white/10 rounded-md">IP-bound keys</span>
</div>
</section>
<div class="grid grid-cols-1 lg:grid-cols-[220px_1fr] gap-12">
<!-- TOC sidebar -->
<aside class="hidden lg:block">
<nav class="sticky top-24 space-y-1 text-sm">
<p class="text-[10px] font-mono uppercase tracking-wider text-gray-500 mb-3 px-3">On this page</p>
<a href="#quickstart" class="toc-link block px-3 py-1.5 border-l border-gray-800 text-gray-400 hover:text-white transition-colors">Quick start</a>
<a href="#auth" class="toc-link block px-3 py-1.5 border-l border-gray-800 text-gray-400 hover:text-white transition-colors">Authentication</a>
<a href="#base-url" class="toc-link block px-3 py-1.5 border-l border-gray-800 text-gray-400 hover:text-white transition-colors">Base URL</a>
<a href="#endpoints" class="toc-link block px-3 py-1.5 border-l border-gray-800 text-gray-400 hover:text-white transition-colors">Endpoints</a>
<a href="#models" class="toc-link block px-3 py-1.5 border-l border-gray-800 text-gray-400 hover:text-white transition-colors">Models catalog</a>
<a href="#chat" class="toc-link block px-3 py-1.5 border-l border-gray-800 text-gray-400 hover:text-white transition-colors">Chat completions</a>
<a href="#veo3" class="toc-link block px-3 py-1.5 border-l border-gray-800 text-gray-400 hover:text-white transition-colors">Veo 3 β€” video</a>
<a href="#gemini3-image" class="toc-link block px-3 py-1.5 border-l border-gray-800 text-gray-400 hover:text-white transition-colors">Gemini 3 β€” image</a>
<a href="#gemini3-text" class="toc-link block px-3 py-1.5 border-l border-gray-800 text-gray-400 hover:text-white transition-colors">Gemini 3 β€” text</a>
<a href="#gpt-image" class="toc-link block px-3 py-1.5 border-l border-gray-800 text-gray-400 hover:text-white transition-colors">gpt-image-*</a>
<a href="#errors" class="toc-link block px-3 py-1.5 border-l border-gray-800 text-gray-400 hover:text-white transition-colors">Errors &amp; limits</a>
<a href="#sdks" class="toc-link block px-3 py-1.5 border-l border-gray-800 text-gray-400 hover:text-white transition-colors">SDK examples</a>
</nav>
</aside>
<!-- Content -->
<article class="space-y-16 min-w-0">
<!-- QUICK START -->
<section id="quickstart">
<h2 class="text-2xl font-semibold text-white mb-2">Quick start</h2>
<p class="text-gray-400 mb-6">Three steps and you're sending requests.</p>
<ol class="space-y-5">
<li class="flex gap-4">
<span class="shrink-0 w-7 h-7 rounded-full bg-amber-500/10 border border-amber-500/30 text-amber-300 text-xs font-mono flex items-center justify-center">1</span>
<div>
<p class="text-sm font-medium text-white mb-1">Grab an API key</p>
<p class="text-sm text-gray-400">Visit <a class="text-amber-300 hover:underline" href="/">the homepage</a> β€” a key is automatically generated for your IP. Keys are bound to that IP.</p>
</div>
</li>
<li class="flex gap-4">
<span class="shrink-0 w-7 h-7 rounded-full bg-amber-500/10 border border-amber-500/30 text-amber-300 text-xs font-mono flex items-center justify-center">2</span>
<div>
<p class="text-sm font-medium text-white mb-1">Point your OpenAI SDK at us</p>
<p class="text-sm text-gray-400">Override <code class="font-mono bg-gray-900 px-1.5 py-0.5 rounded text-amber-300">base_url</code> and you're done β€” everything else is identical to OpenAI.</p>
</div>
</li>
<li class="flex gap-4">
<span class="shrink-0 w-7 h-7 rounded-full bg-amber-500/10 border border-amber-500/30 text-amber-300 text-xs font-mono flex items-center justify-center">3</span>
<div>
<p class="text-sm font-medium text-white mb-1">Pick any model</p>
<p class="text-sm text-gray-400">From <code class="font-mono bg-gray-900 px-1.5 py-0.5 rounded text-amber-300">gpt-5</code> to <code class="font-mono bg-gray-900 px-1.5 py-0.5 rounded text-amber-300">veo-3</code>. See the <a class="text-amber-300 hover:underline" href="#models">full catalog</a>.</p>
</div>
</li>
</ol>
<div class="mt-8 bg-gray-900 border border-gray-800 rounded-xl overflow-hidden">
<div class="px-4 py-2.5 border-b border-gray-800 flex items-center justify-between">
<span class="text-[10px] font-mono uppercase tracking-wider text-gray-500">curl Β· Generate a Veo 3 video</span>
<button class="copy-btn text-[10px] font-mono uppercase tracking-wider text-gray-500 hover:text-white transition-colors px-2 py-1 rounded border border-gray-800">Copy</button>
</div>
<pre class="px-4 py-4"><span class="tk-cmt"># Returns OpenAI-shaped { "data": [{ "url": "https://…/video.mp4" }] }</span>
curl <span class="tk-str">"<span id="base-url-1">https://your-apiarium.example.com</span>/v1/videos/generations"</span> \
-H <span class="tk-str">"Authorization: Bearer api-xxxxxxxxxxxxxxxx"</span> \
-H <span class="tk-str">"Content-Type: application/json"</span> \
-d <span class="tk-str">'{
"model": "veo-3",
"prompt": "a red apple rotating on white, studio lighting",
"duration_seconds": "8",
"resolution": "1080p",
"size": "16:9"
}'</span></pre>
</div>
</section>
<!-- AUTH -->
<section id="auth">
<h2 class="text-2xl font-semibold text-white mb-2">Authentication</h2>
<p class="text-gray-400 mb-4">Every request to <code class="font-mono bg-gray-900 px-1.5 py-0.5 rounded text-amber-300">/v1/*</code> needs a Bearer token.</p>
<div class="bg-gray-900 border border-gray-800 rounded-xl p-4 mb-4">
<pre><span class="tk-kw">Authorization:</span> Bearer <span class="tk-str">api-xxxxxxxxxxxxxxxx</span></pre>
</div>
<ul class="text-sm text-gray-400 space-y-2 leading-relaxed">
<li>πŸ”’ Each key is <strong class="text-white font-medium">bound to one IP address</strong>. Requests from a different network are rejected with HTTP 401.</li>
<li>⏱️ Default rate limit: <strong class="text-white font-medium">4 RPM</strong> per key. The master key bypasses limits.</li>
<li>πŸͺͺ Lose your key? Visit <a class="text-amber-300 hover:underline" href="/">the homepage</a> again β€” your IP will get the same key back.</li>
</ul>
</section>
<!-- BASE URL -->
<section id="base-url">
<h2 class="text-2xl font-semibold text-white mb-2">Base URL</h2>
<p class="text-gray-400 mb-4">All endpoints below are relative to:</p>
<div class="bg-gray-900 border border-gray-800 rounded-xl p-4 flex items-center justify-between">
<code class="font-mono text-sm text-amber-300" id="base-url-display">https://your-apiarium.example.com</code>
<button id="copy-base-url" class="text-[10px] font-mono uppercase tracking-wider text-gray-500 hover:text-white transition-colors px-2 py-1 rounded border border-gray-800">Copy</button>
</div>
<p class="text-xs text-gray-500 mt-2 font-mono">↑ this page auto-detects the running host</p>
</section>
<!-- ENDPOINTS -->
<section id="endpoints">
<h2 class="text-2xl font-semibold text-white mb-6">Endpoints</h2>
<div class="bg-gray-900 border border-gray-800 rounded-xl overflow-hidden">
<table class="w-full text-sm">
<thead class="bg-gray-900/50 border-b border-gray-800 text-[10px] font-mono uppercase tracking-wider text-gray-500">
<tr>
<th class="text-left px-4 py-3 w-16">Method</th>
<th class="text-left px-4 py-3">Path</th>
<th class="text-left px-4 py-3">Purpose</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-800">
<tr><td class="px-4 py-3"><span class="px-1.5 py-0.5 text-[10px] font-mono font-bold bg-blue-500/10 text-blue-400 rounded">GET</span></td> <td class="px-4 py-3 font-mono text-gray-300">/v1/models</td> <td class="px-4 py-3 text-gray-400">List all available model aliases</td></tr>
<tr><td class="px-4 py-3"><span class="px-1.5 py-0.5 text-[10px] font-mono font-bold bg-amber-500/10 text-amber-400 rounded">POST</span></td><td class="px-4 py-3 font-mono text-gray-300">/v1/chat/completions</td> <td class="px-4 py-3 text-gray-400">Chat with any text/image/video model</td></tr>
<tr><td class="px-4 py-3"><span class="px-1.5 py-0.5 text-[10px] font-mono font-bold bg-amber-500/10 text-amber-400 rounded">POST</span></td><td class="px-4 py-3 font-mono text-gray-300">/v1/completions</td> <td class="px-4 py-3 text-gray-400">Legacy text completions</td></tr>
<tr><td class="px-4 py-3"><span class="px-1.5 py-0.5 text-[10px] font-mono font-bold bg-amber-500/10 text-amber-400 rounded">POST</span></td><td class="px-4 py-3 font-mono text-gray-300">/v1/images/generations</td><td class="px-4 py-3 text-gray-400">Image generation (gpt-image-*, gemini-3-image*)</td></tr>
<tr><td class="px-4 py-3"><span class="px-1.5 py-0.5 text-[10px] font-mono font-bold bg-amber-500/10 text-amber-400 rounded">POST</span></td><td class="px-4 py-3 font-mono text-gray-300">/v1/videos/generations</td><td class="px-4 py-3 text-gray-400">Video generation (veo-3, veo-3-fast, veo-3.1-fast)</td></tr>
<tr><td class="px-4 py-3"><span class="px-1.5 py-0.5 text-[10px] font-mono font-bold bg-blue-500/10 text-blue-400 rounded">GET</span></td> <td class="px-4 py-3 font-mono text-gray-300">/v1/keys/me</td> <td class="px-4 py-3 text-gray-400">Get / auto-generate the key bound to your IP</td></tr>
<tr><td class="px-4 py-3"><span class="px-1.5 py-0.5 text-[10px] font-mono font-bold bg-blue-500/10 text-blue-400 rounded">GET</span></td> <td class="px-4 py-3 font-mono text-gray-300">/health</td> <td class="px-4 py-3 text-gray-400">Health check</td></tr>
</tbody>
</table>
</div>
</section>
<!-- MODELS -->
<section id="models">
<h2 class="text-2xl font-semibold text-white mb-2">Models catalog</h2>
<p class="text-gray-400 mb-6">Live snapshot from <code class="font-mono bg-gray-900 px-1.5 py-0.5 rounded text-amber-300">GET /v1/models</code>. Click any tile to copy the model id.</p>
<div class="mb-4 flex items-center justify-between">
<div class="flex flex-wrap gap-2 text-xs font-mono">
<button data-filter="all" class="model-filter px-3 py-1.5 rounded-md border border-gray-700 bg-gray-900 text-gray-200 hover:border-amber-500/50 transition-colors active-filter">All</button>
<button data-filter="chat" class="model-filter px-3 py-1.5 rounded-md border border-gray-800 bg-gray-900/50 text-gray-400 hover:border-gray-600 hover:text-white transition-colors">Chat / text</button>
<button data-filter="image" class="model-filter px-3 py-1.5 rounded-md border border-gray-800 bg-gray-900/50 text-gray-400 hover:border-gray-600 hover:text-white transition-colors">Image</button>
<button data-filter="video" class="model-filter px-3 py-1.5 rounded-md border border-gray-800 bg-gray-900/50 text-gray-400 hover:border-gray-600 hover:text-white transition-colors">Video</button>
</div>
<span id="docs-models-badge" class="text-xs text-gray-500 font-mono">Loading…</span>
</div>
<div id="docs-models-grid" class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-3">
<div class="col-span-full py-10 text-center text-gray-500 text-sm font-mono">Loading model registry…</div>
</div>
</section>
<!-- CHAT -->
<section id="chat">
<h2 class="text-2xl font-semibold text-white mb-2">Chat completions</h2>
<p class="text-gray-400 mb-6">Drop-in replacement for <code class="font-mono bg-gray-900 px-1.5 py-0.5 rounded text-amber-300">POST https://api.openai.com/v1/chat/completions</code>. Streaming via <code class="font-mono bg-gray-900 px-1.5 py-0.5 rounded text-amber-300">"stream": true</code> works out of the box.</p>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="bg-gray-900 border border-gray-800 rounded-xl overflow-hidden">
<div class="px-4 py-2.5 border-b border-gray-800 text-[10px] font-mono uppercase tracking-wider text-gray-500">Request body</div>
<pre class="px-4 py-4">{
<span class="tk-key">"model"</span>: <span class="tk-str">"gpt-5"</span>,
<span class="tk-key">"messages"</span>: [
{<span class="tk-key">"role"</span>: <span class="tk-str">"user"</span>, <span class="tk-key">"content"</span>: <span class="tk-str">"Hello"</span>}
],
<span class="tk-key">"stream"</span>: <span class="tk-bool">true</span>,
<span class="tk-key">"temperature"</span>: <span class="tk-num">0.7</span>
}</pre>
</div>
<div class="bg-gray-900 border border-gray-800 rounded-xl overflow-hidden">
<div class="px-4 py-2.5 border-b border-gray-800 text-[10px] font-mono uppercase tracking-wider text-gray-500">Response (non-stream)</div>
<pre class="px-4 py-4">{
<span class="tk-key">"id"</span>: <span class="tk-str">"chatcmpl-…"</span>,
<span class="tk-key">"object"</span>: <span class="tk-str">"chat.completion"</span>,
<span class="tk-key">"model"</span>: <span class="tk-str">"gpt-5"</span>,
<span class="tk-key">"choices"</span>: [{
<span class="tk-key">"index"</span>: <span class="tk-num">0</span>,
<span class="tk-key">"message"</span>: {
<span class="tk-key">"role"</span>: <span class="tk-str">"assistant"</span>,
<span class="tk-key">"content"</span>: <span class="tk-str">"Hi! How can I help?"</span>
},
<span class="tk-key">"finish_reason"</span>: <span class="tk-str">"stop"</span>
}]
}</pre>
</div>
</div>
</section>
<!-- VEO 3 -->
<section id="veo3">
<div class="flex items-center gap-2 text-xs font-mono uppercase tracking-wider text-amber-400 mb-2">
<span class="w-1.5 h-1.5 rounded-full bg-amber-400"></span>
Video generation
</div>
<h2 class="text-2xl font-semibold text-white mb-2">Veo 3 β€” video</h2>
<p class="text-gray-400 mb-6">
Dedicated endpoint:
<code class="font-mono bg-gray-900 px-1.5 py-0.5 rounded text-amber-300">POST /v1/videos/generations</code>.
Models:
<code class="font-mono bg-gray-900 px-1.5 py-0.5 rounded text-amber-300">veo-3</code>,
<code class="font-mono bg-gray-900 px-1.5 py-0.5 rounded text-amber-300">veo-3-fast</code>,
<code class="font-mono bg-gray-900 px-1.5 py-0.5 rounded text-amber-300">veo-3.1-fast</code>.
Returns an OpenAI-style payload: <code class="font-mono">{"data": [{"url": "https://…/video.mp4"}]}</code>.
</p>
<p class="text-sm text-gray-500 mb-6">
You can also send <code class="font-mono text-amber-300">model: "veo-3"</code> to
<code class="font-mono text-amber-300">/v1/chat/completions</code> β€” the assistant message will then
contain the bare MP4 URL β€” but the dedicated endpoint is more ergonomic.
</p>
<h3 class="text-sm font-medium text-white uppercase tracking-wider mb-3">Supported parameters</h3>
<div class="bg-gray-900 border border-gray-800 rounded-xl overflow-hidden mb-6">
<table class="w-full text-sm">
<thead class="bg-gray-900/50 border-b border-gray-800 text-[10px] font-mono uppercase tracking-wider text-gray-500">
<tr>
<th class="text-left px-4 py-3">Field</th>
<th class="text-left px-4 py-3">Type</th>
<th class="text-left px-4 py-3">Default</th>
<th class="text-left px-4 py-3">Notes</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-800 text-gray-300">
<tr>
<td class="px-4 py-3 font-mono text-amber-300">messages[].content</td>
<td class="px-4 py-3 text-gray-400 font-mono">string</td>
<td class="px-4 py-3 text-gray-500 font-mono">β€”</td>
<td class="px-4 py-3 text-gray-400">The video prompt. We flatten all user/system turns into one string.</td>
</tr>
<tr>
<td class="px-4 py-3 font-mono text-amber-300">duration_seconds</td>
<td class="px-4 py-3 text-gray-400 font-mono">"4" | "6" | "8"</td>
<td class="px-4 py-3 text-gray-500 font-mono">"8"</td>
<td class="px-4 py-3 text-gray-400">Even seconds only. Longer = more credits.</td>
</tr>
<tr>
<td class="px-4 py-3 font-mono text-amber-300">resolution</td>
<td class="px-4 py-3 text-gray-400 font-mono">"720p" | "1080p"</td>
<td class="px-4 py-3 text-gray-500 font-mono">"720p"</td>
<td class="px-4 py-3 text-gray-400">1080p costs more.</td>
</tr>
<tr>
<td class="px-4 py-3 font-mono text-amber-300">aspect_ratio</td>
<td class="px-4 py-3 text-gray-400 font-mono">"16:9" | "9:16"</td>
<td class="px-4 py-3 text-gray-500 font-mono">"16:9"</td>
<td class="px-4 py-3 text-gray-400">Landscape or portrait.</td>
</tr>
<tr>
<td class="px-4 py-3 font-mono text-amber-300">image_url</td>
<td class="px-4 py-3 text-gray-400 font-mono">string</td>
<td class="px-4 py-3 text-gray-500 font-mono">β€”</td>
<td class="px-4 py-3 text-gray-400">Image-to-video: seed frame that gets animated.</td>
</tr>
</tbody>
</table>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="bg-gray-900 border border-gray-800 rounded-xl overflow-hidden">
<div class="px-4 py-2.5 border-b border-gray-800 text-[10px] font-mono uppercase tracking-wider text-gray-500">POST /v1/videos/generations Β· request</div>
<pre class="px-4 py-4">{
<span class="tk-key">"model"</span>: <span class="tk-str">"veo-3"</span>,
<span class="tk-key">"prompt"</span>: <span class="tk-str">"a red apple rotating on white, studio lighting"</span>,
<span class="tk-key">"duration_seconds"</span>: <span class="tk-str">"8"</span>,
<span class="tk-key">"resolution"</span>: <span class="tk-str">"1080p"</span>,
<span class="tk-key">"size"</span>: <span class="tk-str">"16:9"</span>
}</pre>
</div>
<div class="bg-gray-900 border border-gray-800 rounded-xl overflow-hidden">
<div class="px-4 py-2.5 border-b border-gray-800 text-[10px] font-mono uppercase tracking-wider text-gray-500">Response</div>
<pre class="px-4 py-4">{
<span class="tk-key">"created"</span>: <span class="tk-num">1779570172</span>,
<span class="tk-key">"data"</span>: [{
<span class="tk-key">"url"</span>: <span class="tk-str">"https://…/video.mp4"</span>,
<span class="tk-key">"revised_prompt"</span>: <span class="tk-str">"a red apple rotating on white…"</span>
}]
}</pre>
</div>
</div>
<div class="mt-4 p-4 rounded-xl border border-amber-500/20 bg-amber-500/5 text-sm text-amber-200">
⚠️ Video generation takes <strong>30 s – 2 min</strong>. Set an HTTP timeout of at least <strong>10 minutes</strong> client-side. Veo 3's safety filter may reject some prompts β€” you'll get a <code class="font-mono bg-amber-500/10 px-1 rounded">400</code> with a clear error.
</div>
</section>
<!-- GEMINI 3 IMAGE -->
<section id="gemini3-image">
<div class="flex items-center gap-2 text-xs font-mono uppercase tracking-wider text-fuchsia-300 mb-2">
<span class="w-1.5 h-1.5 rounded-full bg-fuchsia-300"></span>
Image generation
</div>
<h2 class="text-2xl font-semibold text-white mb-2">Gemini 3 β€” image</h2>
<p class="text-gray-400 mb-6">
Dedicated endpoint:
<code class="font-mono bg-gray-900 px-1.5 py-0.5 rounded text-amber-300">POST /v1/images/generations</code>.
Models:
<code class="font-mono bg-gray-900 px-1.5 py-0.5 rounded text-amber-300">gemini-3-image</code>,
<code class="font-mono bg-gray-900 px-1.5 py-0.5 rounded text-amber-300">gemini-3-flash-image</code>,
<code class="font-mono bg-gray-900 px-1.5 py-0.5 rounded text-amber-300">gemini-3.1-flash-image</code>.
Append <code class="font-mono bg-gray-900 px-1.5 py-0.5 rounded text-amber-300">-pro</code> to any of them for the higher-quality variant (~2 min).
</p>
<h3 class="text-sm font-medium text-white uppercase tracking-wider mb-3">Supported parameters</h3>
<div class="bg-gray-900 border border-gray-800 rounded-xl overflow-hidden mb-6">
<table class="w-full text-sm">
<thead class="bg-gray-900/50 border-b border-gray-800 text-[10px] font-mono uppercase tracking-wider text-gray-500">
<tr>
<th class="text-left px-4 py-3">Field</th>
<th class="text-left px-4 py-3">Type</th>
<th class="text-left px-4 py-3">Default</th>
<th class="text-left px-4 py-3">Notes</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-800 text-gray-300">
<tr><td class="px-4 py-3 font-mono text-amber-300">prompt</td> <td class="px-4 py-3 text-gray-400 font-mono">string</td><td class="px-4 py-3 text-gray-500 font-mono">β€”</td> <td class="px-4 py-3 text-gray-400">Image description.</td></tr>
<tr><td class="px-4 py-3 font-mono text-amber-300">size</td> <td class="px-4 py-3 text-gray-400 font-mono">aspect or WxH</td><td class="px-4 py-3 text-gray-500 font-mono">"1024x1024"</td><td class="px-4 py-3 text-gray-400">Accepts ratios (1:1, 16:9, 9:16, 4:3, 3:4) or OpenAI sizes (1024x1024, 1792x1024, …).</td></tr>
<tr><td class="px-4 py-3 font-mono text-amber-300">n</td> <td class="px-4 py-3 text-gray-400 font-mono">integer</td><td class="px-4 py-3 text-gray-500 font-mono">1</td> <td class="px-4 py-3 text-gray-400">Repeated entries in <code class="font-mono">data[]</code>; upstream still produces one URL.</td></tr>
</tbody>
</table>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="bg-gray-900 border border-gray-800 rounded-xl overflow-hidden">
<div class="px-4 py-2.5 border-b border-gray-800 text-[10px] font-mono uppercase tracking-wider text-gray-500">POST /v1/images/generations Β· request</div>
<pre class="px-4 py-4">{
<span class="tk-key">"model"</span>: <span class="tk-str">"gemini-3-image"</span>,
<span class="tk-key">"prompt"</span>: <span class="tk-str">"a glass of orange juice on a sunny kitchen counter"</span>,
<span class="tk-key">"size"</span>: <span class="tk-str">"1:1"</span>
}</pre>
</div>
<div class="bg-gray-900 border border-gray-800 rounded-xl overflow-hidden">
<div class="px-4 py-2.5 border-b border-gray-800 text-[10px] font-mono uppercase tracking-wider text-gray-500">Response</div>
<pre class="px-4 py-4">{
<span class="tk-key">"created"</span>: <span class="tk-num">1779570172</span>,
<span class="tk-key">"data"</span>: [{
<span class="tk-key">"url"</span>: <span class="tk-str">"https://…/image.png"</span>,
<span class="tk-key">"revised_prompt"</span>: <span class="tk-str">"a glass of orange juice…"</span>
}]
}</pre>
</div>
</div>
</section>
<!-- GEMINI 3 TEXT -->
<section id="gemini3-text">
<h2 class="text-2xl font-semibold text-white mb-2">Gemini 3 β€” text (RAG)</h2>
<p class="text-gray-400 mb-4">
Models: <code class="font-mono bg-gray-900 px-1.5 py-0.5 rounded text-amber-300">gemini-3</code>,
<code class="font-mono bg-gray-900 px-1.5 py-0.5 rounded text-amber-300">gemini-3-flash</code>,
<code class="font-mono bg-gray-900 px-1.5 py-0.5 rounded text-amber-300">gemini-3-pro</code>.
</p>
<p class="text-sm text-gray-500 mb-6">
These route to Gemini Hub's RAG-grounded <code class="font-mono">/query</code> endpoint. For free-form chat without a knowledge base, prefer
<code class="font-mono text-amber-300">gemini-2.5-pro</code>, <code class="font-mono text-amber-300">gemini-3.1-pro</code> or one of the GPT/Claude aliases.
</p>
</section>
<!-- gpt-image-* -->
<section id="gpt-image">
<h2 class="text-2xl font-semibold text-white mb-2">gpt-image-1 / gpt-image-2</h2>
<p class="text-gray-400 mb-6">
Same endpoint as Gemini 3 image:
<code class="font-mono bg-gray-900 px-1.5 py-0.5 rounded text-amber-300">POST /v1/images/generations</code>.
Just swap the <code class="font-mono text-amber-300">model</code> id.
</p>
<div class="bg-gray-900 border border-gray-800 rounded-xl overflow-hidden">
<div class="px-4 py-2.5 border-b border-gray-800 text-[10px] font-mono uppercase tracking-wider text-gray-500">Request</div>
<pre class="px-4 py-4">{
<span class="tk-key">"model"</span>: <span class="tk-str">"gpt-image-1"</span>,
<span class="tk-key">"prompt"</span>: <span class="tk-str">"a watercolor of Istanbul rooftops"</span>,
<span class="tk-key">"n"</span>: <span class="tk-num">1</span>,
<span class="tk-key">"size"</span>: <span class="tk-str">"1024x1024"</span>
}</pre>
</div>
</section>
<!-- ERRORS -->
<section id="errors">
<h2 class="text-2xl font-semibold text-white mb-2">Errors &amp; limits</h2>
<div class="bg-gray-900 border border-gray-800 rounded-xl overflow-hidden">
<table class="w-full text-sm">
<thead class="bg-gray-900/50 border-b border-gray-800 text-[10px] font-mono uppercase tracking-wider text-gray-500">
<tr>
<th class="text-left px-4 py-3 w-20">Status</th>
<th class="text-left px-4 py-3">Meaning</th>
<th class="text-left px-4 py-3">Fix</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-800 text-gray-300">
<tr><td class="px-4 py-3 font-mono text-red-400">401</td> <td class="px-4 py-3">IP mismatch or missing/invalid key</td><td class="px-4 py-3 text-gray-400">Hit <a class="text-amber-300 hover:underline" href="/v1/keys/me">/v1/keys/me</a> from your real IP.</td></tr>
<tr><td class="px-4 py-3 font-mono text-amber-400">403</td><td class="px-4 py-3">VPN / proxy detected</td> <td class="px-4 py-3 text-gray-400">Disable VPN, retry.</td></tr>
<tr><td class="px-4 py-3 font-mono text-amber-400">429</td><td class="px-4 py-3">Rate limit exceeded (4 RPM)</td> <td class="px-4 py-3 text-gray-400">Back off &amp; retry; <code class="font-mono">content</code> field shows wait time.</td></tr>
<tr><td class="px-4 py-3 font-mono text-red-400">502</td> <td class="px-4 py-3">Upstream provider unreachable</td> <td class="px-4 py-3 text-gray-400">Try again or pick another model.</td></tr>
</tbody>
</table>
</div>
</section>
<!-- SDK EXAMPLES -->
<section id="sdks">
<h2 class="text-2xl font-semibold text-white mb-6">SDK examples</h2>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
<div class="bg-gray-900 border border-gray-800 rounded-xl overflow-hidden">
<div class="px-4 py-2.5 border-b border-gray-800 text-[10px] font-mono uppercase tracking-wider text-gray-500 flex items-center justify-between">
<span>🐍 Python · openai</span>
<button class="copy-btn text-[10px] font-mono uppercase tracking-wider text-gray-500 hover:text-white transition-colors px-2 py-1 rounded border border-gray-800">Copy</button>
</div>
<pre class="px-4 py-4"><span class="tk-kw">from</span> openai <span class="tk-kw">import</span> OpenAI
client = <span class="tk-fn">OpenAI</span>(
base_url=<span class="tk-str">"<span class="base-url-inline">https://your-apiarium.example.com</span>/v1"</span>,
api_key=<span class="tk-str">"api-xxxxxxxxxxxxxxxx"</span>,
)
<span class="tk-cmt"># 1) chat</span>
chat = client.chat.completions.<span class="tk-fn">create</span>(
model=<span class="tk-str">"gpt-5"</span>,
messages=[{<span class="tk-str">"role"</span>: <span class="tk-str">"user"</span>, <span class="tk-str">"content"</span>: <span class="tk-str">"hi"</span>}],
)
<span class="tk-fn">print</span>(chat.choices[<span class="tk-num">0</span>].message.content)
<span class="tk-cmt"># 2) image (Gemini 3 or gpt-image)</span>
img = client.images.<span class="tk-fn">generate</span>(
model=<span class="tk-str">"gemini-3-image"</span>,
prompt=<span class="tk-str">"a glass of orange juice on a sunny kitchen counter"</span>,
size=<span class="tk-str">"1:1"</span>,
)
<span class="tk-fn">print</span>(img.data[<span class="tk-num">0</span>].url)
<span class="tk-cmt"># 3) video (Veo 3) β€” /v1/videos/generations</span>
<span class="tk-kw">import</span> httpx
vid = httpx.<span class="tk-fn">post</span>(
<span class="tk-str">"<span class="base-url-inline">https://your-apiarium.example.com</span>/v1/videos/generations"</span>,
headers={<span class="tk-str">"Authorization"</span>: <span class="tk-str">"Bearer api-xxxxxxxxxxxxxxxx"</span>},
json={<span class="tk-str">"model"</span>: <span class="tk-str">"veo-3"</span>,
<span class="tk-str">"prompt"</span>: <span class="tk-str">"a red apple rotating on white"</span>,
<span class="tk-str">"duration_seconds"</span>: <span class="tk-str">"8"</span>,
<span class="tk-str">"resolution"</span>: <span class="tk-str">"1080p"</span>,
<span class="tk-str">"size"</span>: <span class="tk-str">"16:9"</span>},
timeout=<span class="tk-num">600</span>,
).<span class="tk-fn">json</span>()
<span class="tk-fn">print</span>(vid[<span class="tk-str">"data"</span>][<span class="tk-num">0</span>][<span class="tk-str">"url"</span>])</pre>
</div>
<div class="bg-gray-900 border border-gray-800 rounded-xl overflow-hidden">
<div class="px-4 py-2.5 border-b border-gray-800 text-[10px] font-mono uppercase tracking-wider text-gray-500 flex items-center justify-between">
<span>🟨 JavaScript · openai</span>
<button class="copy-btn text-[10px] font-mono uppercase tracking-wider text-gray-500 hover:text-white transition-colors px-2 py-1 rounded border border-gray-800">Copy</button>
</div>
<pre class="px-4 py-4"><span class="tk-kw">import</span> OpenAI <span class="tk-kw">from</span> <span class="tk-str">"openai"</span>;
<span class="tk-kw">const</span> BASE_URL = <span class="tk-str">"<span class="base-url-inline">https://your-apiarium.example.com</span>"</span>;
<span class="tk-kw">const</span> API_KEY = <span class="tk-str">"api-xxxxxxxxxxxxxxxx"</span>;
<span class="tk-kw">const</span> client = <span class="tk-kw">new</span> <span class="tk-fn">OpenAI</span>({
baseURL: <span class="tk-str">`${BASE_URL}/v1`</span>,
apiKey: API_KEY,
});
<span class="tk-cmt">// 1) image β€” uses the OpenAI SDK's images.generate</span>
<span class="tk-kw">const</span> img = <span class="tk-kw">await</span> client.images.<span class="tk-fn">generate</span>({
model: <span class="tk-str">"gemini-3-image"</span>,
prompt: <span class="tk-str">"a glass of orange juice on a sunny kitchen counter"</span>,
size: <span class="tk-str">"1:1"</span>,
});
<span class="tk-fn">console.log</span>(img.data[<span class="tk-num">0</span>].url);
<span class="tk-cmt">// 2) video β€” bypass the SDK, hit /v1/videos/generations directly</span>
<span class="tk-kw">const</span> vid = <span class="tk-kw">await</span> <span class="tk-fn">fetch</span>(<span class="tk-str">`${BASE_URL}/v1/videos/generations`</span>, {
method: <span class="tk-str">"POST"</span>,
headers: {
<span class="tk-str">"Authorization"</span>: <span class="tk-str">`Bearer ${API_KEY}`</span>,
<span class="tk-str">"Content-Type"</span>: <span class="tk-str">"application/json"</span>,
},
body: <span class="tk-fn">JSON.stringify</span>({
model: <span class="tk-str">"veo-3"</span>,
prompt: <span class="tk-str">"a serene mountain sunset timelapse"</span>,
duration_seconds: <span class="tk-str">"8"</span>,
resolution: <span class="tk-str">"1080p"</span>,
size: <span class="tk-str">"16:9"</span>,
}),
}).<span class="tk-fn">then</span>(r => r.<span class="tk-fn">json</span>());
<span class="tk-fn">console.log</span>(vid.data[<span class="tk-num">0</span>].url);</pre>
</div>
</div>
</section>
</article>
</div>
</main>
<!-- Footer -->
<footer class="border-t border-gray-900 bg-black mt-20 relative z-10">
<div class="max-w-7xl mx-auto px-6 py-8 flex flex-col sm:flex-row justify-between items-center gap-4">
<div class="flex items-center gap-2 text-gray-500 text-sm">
<span class="text-base">🐝</span>
&copy; 2026 APIarium Systems. All rights reserved.
</div>
<div class="flex items-center gap-4 text-sm text-gray-500">
<a href="/" class="hover:text-gray-300 transition-colors">Home</a>
<a href="/openapi-docs" class="hover:text-gray-300 transition-colors">Swagger</a>
<a href="/openapi.json" class="hover:text-gray-300 transition-colors">OpenAPI JSON</a>
</div>
</div>
</footer>
<div id="toast" class="fixed bottom-8 left-1/2 -translate-x-1/2 z-50 bg-white text-black px-4 py-3 rounded-lg shadow-2xl font-medium text-sm flex items-center gap-2 hidden">
<svg class="w-5 h-5 text-emerald-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>
<span id="toast-text">Copied to clipboard</span>
</div>
<script>
// ── Auto-inject the current host into every "base-url" placeholder ─
const origin = window.location.origin;
document.querySelectorAll('#base-url-display, #base-url-1, .base-url-inline').forEach(el => el.textContent = origin);
// ── Copy buttons ──────────────────────────────────────────────────
function flash(msg) {
const t = document.getElementById('toast');
const txt = document.getElementById('toast-text');
txt.textContent = msg || 'Copied to clipboard';
t.classList.remove('hidden');
t.classList.add('toast-enter');
setTimeout(() => { t.classList.add('hidden'); t.classList.remove('toast-enter'); }, 1800);
}
function copy(text, label) {
const done = () => flash(label || 'Copied');
if (navigator.clipboard && window.isSecureContext) navigator.clipboard.writeText(text).then(done);
else {
const ta = document.createElement('textarea');
ta.value = text; ta.style.position = 'fixed'; ta.style.left = '-9999px';
document.body.appendChild(ta); ta.select();
try { document.execCommand('copy'); done(); } catch (e) {}
ta.remove();
}
}
document.getElementById('copy-base-url').addEventListener('click', () => copy(origin, 'Base URL copied'));
document.querySelectorAll('.copy-btn').forEach(btn => btn.addEventListener('click', (e) => {
const pre = e.target.closest('div').nextElementSibling || e.target.closest('div').parentElement.querySelector('pre');
if (pre) copy(pre.textContent.trim(), 'Snippet copied');
}));
// ── TOC active-link tracking via IntersectionObserver ─────────────
const tocLinks = document.querySelectorAll('.toc-link');
const sections = Array.from(tocLinks).map(a => document.querySelector(a.getAttribute('href'))).filter(Boolean);
const obs = new IntersectionObserver((entries) => {
entries.forEach(e => {
if (e.isIntersecting) {
tocLinks.forEach(l => l.classList.remove('active'));
const active = document.querySelector('.toc-link[href="#' + e.target.id + '"]');
if (active) active.classList.add('active');
}
});
}, { rootMargin: '-30% 0px -60% 0px' });
sections.forEach(s => obs.observe(s));
// ── Models catalog (live from /v1/models) ─────────────────────────
const grid = document.getElementById('docs-models-grid');
const badge = document.getElementById('docs-models-badge');
let allModels = [];
let currentFilter = 'all';
const TYPE_RULES = [
{ key: 'video', test: id => /^veo-/i.test(id) },
{ key: 'image', test: id => /image/i.test(id) || /^gpt-image-/i.test(id) },
{ key: 'chat', test: () => true }, // catch-all last
];
function typeOf(id) {
for (const r of TYPE_RULES) if (r.test(id)) return r.key;
return 'chat';
}
function badgeClass(t) {
if (t === 'video') return 'bg-amber-500/10 text-amber-300 border-amber-500/30';
if (t === 'image') return 'bg-fuchsia-500/10 text-fuchsia-300 border-fuchsia-500/30';
return 'bg-blue-500/10 text-blue-300 border-blue-500/30';
}
function renderModels() {
const filtered = allModels.filter(m => currentFilter === 'all' || typeOf(m.id) === currentFilter);
badge.textContent = `${filtered.length} of ${allModels.length} model${allModels.length === 1 ? '' : 's'}`;
if (filtered.length === 0) {
grid.innerHTML = `<div class="col-span-full py-10 text-center text-gray-500 text-sm font-mono">No models match this filter.</div>`;
return;
}
grid.innerHTML = filtered.map(m => {
const t = typeOf(m.id);
return `
<button onclick="copy('${m.id.replace(/'/g, "&#39;")}', 'Model id copied')"
class="text-left bg-gray-900/50 border border-gray-800 hover:border-amber-500/40 rounded-xl p-4 transition-colors group">
<div class="flex items-start justify-between gap-2 mb-2">
<code class="font-mono text-sm text-gray-100 group-hover:text-amber-200 truncate">${m.id}</code>
<span class="shrink-0 px-2 py-0.5 text-[10px] font-mono border rounded ${badgeClass(t)}">${t}</span>
</div>
<div class="text-[10px] font-mono uppercase tracking-wider text-gray-500 truncate">${m.owned_by || 'system'}</div>
</button>`;
}).join('');
}
document.querySelectorAll('.model-filter').forEach(btn => {
btn.addEventListener('click', (e) => {
document.querySelectorAll('.model-filter').forEach(b => b.classList.remove('active-filter', 'border-gray-700', 'text-gray-200'));
document.querySelectorAll('.model-filter').forEach(b => b.classList.add('border-gray-800', 'text-gray-400'));
e.currentTarget.classList.add('active-filter', 'border-gray-700', 'text-gray-200');
e.currentTarget.classList.remove('border-gray-800', 'text-gray-400');
currentFilter = e.currentTarget.getAttribute('data-filter');
renderModels();
});
});
(async function loadModels() {
try {
const r = await fetch('/v1/models');
const d = await r.json();
allModels = (d.data || []).sort((a, b) => a.id.localeCompare(b.id));
renderModels();
} catch (e) {
grid.innerHTML = `<div class="col-span-full py-10 text-center text-red-400 text-sm font-mono">Failed to load model registry.</div>`;
badge.textContent = 'error';
}
})();
</script>
</body>
</html>