Horizon API docs

An OpenAI-compatible API. If a library can talk to OpenAI, it can talk to Horizon.

Quickstart

  1. Sign in with Hack Club. New accounts start with zero quota until an admin approves you.
  2. Open Keys and create a key. It looks like sk-... and is shown only once.
  3. Use the base URL below with any OpenAI client.
SettingValue
Base URLhttps://horizon-api.dino.icu/v1
Chat completionshttps://horizon-api.dino.icu/v1/chat/completions
Model listhttps://horizon-api.dino.icu/v1/models
Auth headerAuthorization: Bearer sk-your-key

Models

Use the slug as the model value. Live status comes from the status page.

ModelSlugStatus
Loading...

Code examples

Replace sk-your-key with your key. Better: put it in an environment variable called HORIZON_API_KEY.

curl https://horizon-api.dino.icu/v1/chat/completions \
  -H "Authorization: Bearer $HORIZON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek/deepseek-v4.1-flash",
    "stream": true,
    "messages": [
      {"role": "system", "content": "You are a helpful assistant."},
      {"role": "user", "content": "Explain DNS in one paragraph."}
    ]
  }'
# pip install openai
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://horizon-api.dino.icu/v1",
    api_key=os.environ["HORIZON_API_KEY"],
)

stream = client.chat.completions.create(
    model="deepseek/deepseek-v4.1-flash",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Explain DNS in one paragraph."},
    ],
    stream=True,
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
// npm install openai
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://horizon-api.dino.icu/v1",
  apiKey: process.env.HORIZON_API_KEY,
});

const stream = await client.chat.completions.create({
  model: "deepseek/deepseek-v4.1-flash",
  messages: [
    { role: "system", content: "You are a helpful assistant." },
    { role: "user", content: "Explain DNS in one paragraph." },
  ],
  stream: true,
});
for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
$body = @{
  model    = "deepseek/deepseek-v4.1-flash"
  messages = @(@{ role = "user"; content = "Explain DNS in one paragraph." })
} | ConvertTo-Json -Depth 4
$answer = Invoke-RestMethod -Method Post -Uri "https://horizon-api.dino.icu/v1/chat/completions" `
  -Headers @{ Authorization = "Bearer $env:HORIZON_API_KEY" } -ContentType "application/json" -Body $body
$answer.choices[0].message.content

Coding agents

Any agent or editor that accepts an OpenAI-compatible provider works with Horizon: Cline, Roo Code, Kilo Code, OpenCode, Continue, Aider, Zed and similar tools. Tool calling and streaming are passed through, which is what agents need.

  1. In the tool's model settings choose OpenAI Compatible (sometimes called "Custom OpenAI" or "OpenAI API").
  2. Base URL: https://horizon-api.dino.icu/v1
  3. API key: your sk-... key. Model: any slug from the model list, for example deepseek/deepseek-v4.1-flash.

Example for Aider and other tools that read the standard environment variables:

export OPENAI_API_BASE=https://horizon-api.dino.icu/v1
export OPENAI_API_KEY=sk-your-key
aider --model openai/deepseek/deepseek-v4.1-flash

Agents send many requests in a row. If you hit a limit you get HTTP 429 and the tool retries; the per-user limits are in the Limits section.

Streaming, tools, reasoning

API keys

A key is shown once, when you create it. After that nobody, not even the admin, can show it to you again. Store it in a password manager or an environment variable.

Lost a key? Replace it

  1. Open Keys and create a new key. Copy it immediately.
  2. Put the new key into your apps and check they work.
  3. Delete the old key. Anything still using it stops working right away.

Keys can have a model allowlist, an expiry date and an IP allowlist. You can have up to 5 keys. To give one key a lower personal speed limit, add #rpm=N to its name, for example laptop #rpm=5.

Honest note: keys are stored in plain text in the server database (that is how New API works). Only displaying them is blocked after creation. The admin team keeps the server and its backups locked down.

Limits

These are the defaults; the admin can change them for you. When you hit one you get HTTP 429 with a Retry-After hint where possible.

LimitDefault
Requests per minute15 per user
Requests per day500 per user (resets 00:00 UTC)
Tokens per day1,000,000 per user (resets 00:00 UTC)
Parallel requests3 per user
max_tokenscapped at 16,384 (higher values are lowered)
Request size2 MB
Busy serveryou wait in a fair queue up to 30 s, then get horizon_server_busy

Your usage per day is on the dashboard and in usage logs.

Errors

StatusTypeMeaning
400upstream_client_errorThe request itself is invalid (for example too long for the model). Fix the request; retrying will not help.
401-Missing or wrong key.
403 / quota-Your account is not approved yet or has no quota left.
429horizon_user_concurrency, horizon_daily_limit, horizon_daily_token_limit, horizon_user_rate_limited, horizon_key_rate_limited, horizon_server_busyA limit was hit. Wait and retry.
503horizon_all_endpoints_failed, horizon_no_endpoint_availableEvery free endpoint for that model failed or is cooling down. Try another model or retry in a minute.
504horizon_chain_deadlineNo endpoint produced a first token within about 4 minutes.
in streamhorizon_stream_interruptedThe provider cut the stream after the answer had started. Retry the request.

How fallback works

Every model has an ordered chain of free endpoints. For each request Horizon tries them top to bottom:

Privacy

Prompts and answers are never stored. Horizon logs only metadata: time, user, key, model, token counts, duration, which hop served the request and error types.

Your request is forwarded to a third-party free provider to be answered. Those providers have their own policies, and one fallback endpoint uses plain HTTP. Do not send passwords, secrets or personal data you would not want a third party to see.