Free model access, one link at a time.
Text, images, and vision — one link at a time, no API key anywhere. Call them from any app — Flutter, a website, a shortcut, curl. Everything runs through this server, so there's no CORS to fight and nothing to configure on your end.
None. Every endpoint below works anonymously out of the box — that was the whole point of this pass.
Optional: a free personal key at aihorde.net/register
(no payment) set as AI_HORDE_API_KEY in Netlify jumps you ahead of the anonymous queue for both
images and chat. Not required.
Runs on AI Horde — free,
crowdsourced Stable Diffusion, no signup, no watermark. Because it's community-donated GPUs
rather than dedicated servers, speed varies: usually returns the image directly, but if the queue is busy it
returns 202 with a checkUrl to poll instead of making you wait indefinitely.
| Param | Required | Default |
|---|---|---|
| prompt | yes | — |
| model | no | best available of a curated set (SDXL/Deliberate/DreamShaper/SD) |
| width / height | no | 512 / 512 |
| seed | no | random |
| safe | no | true |
| enhance | no | false — set true for face-fix + 4x upscale (slower) |
| sourceImage | no | — a URL to transform (img2img) instead of generating from scratch |
| denoise | no | 0.6 — only with sourceImage; higher = more different from the source |
GET /api/image?prompt=a+fox+asleep+in+a+teacup
GET /api/image?prompt=make+it+winter&sourceImage=https://example.com/photo.jpg&denoise=0.5
→ image bytes, OR if still queued:
{ "pending": true, "id": "...", "checkUrl": "/api/image/result?id=..." }
Poll this with the id from a pending /api/image response until it returns the image.
GET /api/image/result?id=abc123
→ image bytes once ready, or { "pending": true, "waitTime": 12, "queuePosition": 2 }
Runs the same prompt through several image models at once, using one shared seed so the results are a fair side-by-side. Returns links, not bytes — render whichever ones you get back.
| Param | Required | Default |
|---|---|---|
| prompt | yes | — |
| models | no | stable_diffusion, AlbedoBase XL (SDXL), Deliberate 3.0 |
| width / height / seed / safe | no | same as /api/image |
GET /api/image/ensemble?prompt=a+fox+asleep+in+a+teacup
→ { "prompt": "...", "seed": 4821093,
"images": [
{ "model": "stable_diffusion", "seed": 4821093, "url": ".../api/image?...&model=stable_diffusion&seed=4821093" },
{ "model": "AlbedoBase XL (SDXL)", "seed": 4821093, "url": ".../api/image?...&model=AlbedoBase+XL...&seed=4821093" },
{ "model": "Deliberate 3.0", "seed": 4821093, "url": ".../api/image?...&model=Deliberate+3.0&seed=4821093" }
]}
There's no way to merge independent model outputs into one "better" pixel-level image — each is its own attempt. This gives you all of them at once so you (or your app) can pick the best, instead of gambling on a single model. Each link can independently return a 202 pending too.
Quick reply as plain text — good for a simple link/shortcut. Runs on AI Horde's KoboldAI text generation — free, no signup. Simple things (greetings, thanks, "who are you", arithmetic, unit/temperature conversion) answer instantly without touching AI Horde at all — no point making "hi" or "10km to miles" wait on a volunteer GPU. Weather questions ("what's the weather in Tokyo") get real current data injected before the model answers, same idea as the world headlines already there — it can't otherwise know either one. Honest tradeoff on the model-backed replies: quality and speed depend on whatever volunteer model happens to pick up the job, so it's more variable than a dedicated frontier-model API.
GET /api/chat?prompt=explain+recursion+simply
→ plain text reply, or { "pending": true, "id": "..." } if the queue is slow
Full conversations. Send either prompt for a single message, or a full messages array to keep context. Two optional fields make this pluggable into other apps/bots:
| Field | What it does |
|---|---|
| model | Pass "nuvq-pro" for a slower, more careful mode: holds out longer for a more capable worker, allows much longer replies (won't truncate mid-function), lower temperature for precision, and an explicit instruction to write complete working code rather than snippets/placeholders. Leave unset for the normal fast mode. |
| systemPrompt | A custom persona/instructions for this integration — e.g. a different personality for a Telegram bot vs. your app. |
| userId | Any stable string you choose per end-user. When set and you send just the latest message (not a full messages array), the server remembers the actual back-and-forth automatically — no need to resend history yourself — plus long-term facts (see /api/memory) and "remember that..." detection. Sending your own full messages array still works exactly as before and skips the auto-history (you're already managing it). |
POST /api/chat
Content-Type: application/json
{ "model": "nuvq-pro",
"messages": [
{ "role": "user", "content": "Write a function that debounces a JS callback" }
]}
→ { "reply": "..." }
Real persistent memory (Netlify Blobs) — not in-request-only. Facts get added automatically when a chat message matches "remember that...", "note that...", or "my name is...", or you can add them directly.
GET /api/memory?userId=telegram-8213552
→ { "userId": "...", "facts": ["The user's name is Sam."] }
POST /api/memory { "userId": "...", "fact": "Prefers short answers." }
DELETE /api/memory?userId=... (clears everything stored for that id)
A real webhook, not just an API — Telegram calls this whenever someone messages your bot.
| 1. | Message @BotFather on Telegram, create a bot, copy its token. |
| 2. | In Netlify: add env var TELEGRAM_BOT_TOKEN = that token. Optional: TELEGRAM_SYSTEM_PROMPT for its personality, TELEGRAM_WEBHOOK_SECRET for basic request verification. |
| 3. | Redeploy, then point Telegram at it — once, from any browser or curl:https://api.telegram.org/bot<TOKEN>/setWebhook?url=https://YOUR-SITE.netlify.app/api/telegram |
That's it — message your bot and it replies for real. Same free AI Horde backend underneath, so the same speed/quality tradeoffs apply. It remembers the last ~10 exchanges per chat automatically, so it stays coherent across a real back-and-forth instead of treating every message as a fresh conversation. Personality is warm and a little playful by default (override with TELEGRAM_SYSTEM_PROMPT), and it recognizes its owner's Telegram username (kaos_king) to be a bit warmer with them specifically — everyone else gets the same helpful default.
Also accepts model: "nuvq-pro", systemPrompt, and userId, same as above.
Fires 3 independent completions in parallel (separate AI Horde jobs, not one job asking for
3 at once — that turned out not to be reliably supported and was actually why this endpoint used to hang),
then has a follow-up call merge whichever came back into one answer. Slower than /api/chat, and
quality depends on the horde the same way — this is the "combine several models" feature adapted to a fully
keyless backend, not a guarantee of frontier-model quality.
POST /api/chat/combined
Content-Type: application/json
{ "prompt": "What's the fastest way to learn Spanish?" }
→ { "reply": "...combined answer...", "synthesized": true,
"drafts": [
{ "label": "Model A", "content": "..." },
{ "label": "Model B", "content": "..." },
{ "label": "Model C", "content": "..." }
]}
The headlines chat quietly uses for current-events awareness, exposed directly in case you want a standalone headlines list or widget. Google News RSS under the hood — free, public, no key.
| Param | Required | Default |
|---|---|---|
| topic | no | top stories (or WORLD, NATION, BUSINESS, TECHNOLOGY, SPORTS, SCIENCE, HEALTH, ENTERTAINMENT) |
| limit | no | 8 (max 20) |
GET /api/news?topic=WORLD&limit=5
→ { "topic": "WORLD", "headlines": [
{ "title": "...", "pubDate": "...", "source": "Reuters" }
], "fetchedAt": "..." }
Real current weather and a 3-day outlook — something no chat model can know on its own. Powered by Open-Meteo, a free, open-source weather API — no key, official project, not a workaround.
| Param | Required | Default |
|---|---|---|
| location | yes (or lat/lon) | — |
| lat / lon | yes (or location) | — |
GET /api/weather?location=Lagos
→ { "location": "Lagos, Nigeria",
"current": { "temperatureC": 29.4, "feelsLikeC": 33.1, "humidityPct": 78, "windKph": 11.2, "condition": "partly cloudy" },
"next3Days": [ { "date": "2026-09-13", "highC": 31, "lowC": 24, "condition": "slight rain" } ] }
Runs on AI Horde's image interrogation, then a quick follow-up text pass turns the raw caption/tags into a natural couple of sentences of feedback. Honest limit: this is generated commentary on a caption, not the model actually reasoning over the image the way a vision-LLM would — so it can describe and react to what's there, but can't reliably answer an arbitrary custom question about specific details. There's no keyless open-ended visual Q&A service available, so this is the real tradeoff of staying fully key-free here.
POST /api/vision
Content-Type: application/json
{ "imageUrl": "https://example.com/photo.jpg" }
→ { "caption": "...", "tags": ["...", "..."], "feedback": "...", "note": "..." }
Dropped /api/transcribe too — same reasoning as TTS. AI Horde doesn't do audio at
all, and there's no other legitimate free, keyless, hosted STT worth building on. The genuinely better free
answer: run Whisper client-side
(via Transformers.js/WASM in a browser, or native bindings in a mobile app) — free, no key, no server round-trip,
and the audio never leaves the device. Or use the platform's built-in recognizer
(Android SpeechRecognizer, iOS SFSpeechRecognizer, the browser's
SpeechRecognition API) for something even simpler. Either beats proxying audio through a server.
Dropped the /api/speak endpoint — there's no legitimate free, keyless TTS API worth
building on (the ones that exist without signup are unofficial/reverse-engineered and can break without notice).
The better answer is free anyway: every phone and browser already has built-in text-to-speech
(Android TextToSpeech, iOS AVSpeechSynthesizer, the browser's
speechSynthesis API) — instant, offline, zero server calls. Use that in whatever app is calling
this API instead of round-tripping through a server for it.
Curated list of known-good image models. Text doesn't use named models by default on this backend.
→ { "image": ["stable_diffusion", "AlbedoBase XL (SDXL)", ...], "text": [], "textNote": "..." }A controller endpoint — reads what you're asking for and actually does it instead of just talking about it: generates an image, checks real weather, saves a memory, or falls through to normal chat.
POST /api/agent
Content-Type: application/json
{ "prompt": "draw me a picture of a fox in a teacup", "userId": "optional" }
→ { "action": "image", "reply": "...", "imageUrl": "https://.../api/image?prompt=..." }
Other actions: "weather" (real current conditions), "memory" (needs userId), or "chat" as the default fallback. Intent detection is pattern-based, not the language model guessing — more predictable, though it means very unusual phrasing might fall through to plain chat instead of triggering the action.
Built for wiring an AI assistant into your own operating system (or any app where you control execution). This endpoint only interprets a command into a structured action — it never runs anything. Your own app decides whether and how to act on the result, and should show a confirmation step for anything with requiresConfirmation: true. That split matters because this API has no login and no way to verify who's calling it — treat every response as a suggestion your app validates, never as an instruction to execute blindly.
POST /api/os
Content-Type: application/json
{ "command": "set brightness to 80" }
→ { "recognized": true, "action": "set_brightness", "params": { "level": "80" } }
{ "command": "shut down" }
→ { "recognized": true, "action": "system_shut_down", "params": {}, "requiresConfirmation": true }
{ "command": "what's the capital of France" }
→ { "recognized": false, "message": "..." } ← treat as a normal chat message instead
Recognized actions out of the box: open_app, close_app, set_volume, set_mute, set_brightness, set_wifi, set_bluetooth, take_screenshot, lock_screen, search_files, and destructive ones (system_shut_down, system_restart, etc.) which always come back with requiresConfirmation: true. This is a starting vocabulary, not a fixed one — the patterns live in netlify/functions/os.mts if you want to add actions specific to what your OS actually supports.
final res = await http.get(Uri.parse( 'https://YOUR-SITE.netlify.app/api/chat?prompt=hello')); print(res.body);