Sell from your own server

Run an open model on your own GPUs, put HTTPS in front, and paste one URL in the console. We test it, list it at your price, and send it buyers' requests when it is the best offer.

What you need

  • An OpenAI-compatible APIIt answers GET /v1/models with a data list of models and POST /v1/chat/completions, streamed (server-sent events) and not streamed.
  • Public HTTPSA host name on the internet with a certificate from a public authority (Let's Encrypt is fine), on port 443 or a port from 1024 up. Self-signed certificates and plain http are refused.
  • A base URL ending in /v1We add /models and /chat/completions to what you paste, so paste the part before them: https://llm.example.com/v1.
  • A key, if you want oneOptional. If your server asks for one, we send it as Authorization: Bearer <key> and nothing else. It is sealed with the URL when you save them, and never shown again.

Any model your server serves can be listed, as long as its licence allows commercial serving and no one else's API key is behind your server: that is the statement you accept before going live (own-server-v2).

Set up your server

Each setup below serves Qwen3 32B on 127.0.0.1:8000, where only the machine itself reaches it, and puts Caddy in front for HTTPS. Point a DNS record (llm.example.com) at the machine and open ports 80 and 443; Caddy gets the certificate on its first start.

HTTPS with Caddy (every setup)

# /etc/caddy/Caddyfile: Caddy gets and renews the certificate on its own
llm.example.com {
    reverse_proxy 127.0.0.1:8000
}

Then sudo systemctl reload caddy. Caddy passes streamed answers through as they come. Any other reverse proxy works if it has a public certificate and does not buffer server-sent events.

vLLM

export OWN_KEY=$(openssl rand -hex 32)   # keep it: you paste it in the console
vllm serve Qwen/Qwen3-32B \
  --host 127.0.0.1 --port 8000 \
  --api-key "$OWN_KEY" \
  --max-model-len 32768

It lists the model as Qwen/Qwen3-32B, which is qwen3-32b here, with its context length.

Paste in the console: https://llm.example.com/v1 and your key.

SGLang

export OWN_KEY=$(openssl rand -hex 32)
python -m sglang.launch_server \
  --model-path Qwen/Qwen3-32B \
  --host 127.0.0.1 --port 8000 \
  --api-key "$OWN_KEY"

Paste in the console: https://llm.example.com/v1 and your key.

llama.cpp server

export OWN_KEY=$(openssl rand -hex 32)
llama-server -hf Qwen/Qwen3-32B-GGUF:Q4_K_M \
  --alias qwen3-32b \
  --host 127.0.0.1 --port 8000 \
  --api-key "$OWN_KEY" \
  --jinja -c 32768

--alias is the id /v1/models lists: name it after the model you serve. --jinja uses the model's own chat template, which tool calls need.

Paste in the console: https://llm.example.com/v1 and your key.

Ollama, behind Caddy

Ollama has no key of its own and serves plain http, so Caddy does both: HTTPS, and a 401 for any request without your key.

# the model, by its size tag (listed as qwen3-32b), kept in memory
ollama pull qwen3:32b
OLLAMA_KEEP_ALIVE=-1 ollama serve        # listens on 127.0.0.1:11434

# /etc/caddy/Caddyfile: TLS, and the key Ollama itself does not check
llm.example.com {
    @nokey not header Authorization "Bearer {$OWN_KEY}"
    respond @nokey 401
    reverse_proxy 127.0.0.1:11434
}

Start Caddy with OWN_KEY set in its environment. Pull models by their size tag: qwen3:32b is listed as qwen3-32b, but qwen3:latest keeps “latest” in its id and matches no model in our catalog.

Paste in the console: https://llm.example.com/v1 and your key.

Test it yourself

From another machine, before you paste the URL: the two calls we make first.

export BASE=https://llm.example.com/v1
export OWN_KEY=…   # the key you set, if any

# 1. the model list: a "data" array with your model's id
curl -s $BASE/models -H "Authorization: Bearer $OWN_KEY"

# 2. a streamed answer: data: lines, then data: [DONE]; the last chunk carries "usage"
curl -sN $BASE/chat/completions -H "Authorization: Bearer $OWN_KEY" -H "Content-Type: application/json" \
  -d '{"model": "Qwen/Qwen3-32B", "stream": true, "stream_options": {"include_usage": true}, "max_tokens": 16,
       "messages": [{"role": "user", "content": "Say hi"}]}'

Use the id the first call lists as model (qwen3-32b for llama.cpp and Ollama). The second call should print data: lines as the answer streams, a last chunk with usage, and data: [DONE].

Model ids

We read the ids at GET /v1/models and map each one to a model in our catalog: lowercased, the organisation before the last / dropped, an Ollama size tag joined to the name, Instruct dropped, then our alias table. So Qwen/Qwen3-32B, qwen3-32b and Ollama's qwen3:32b are the same model, and meta-llama/Llama-3.3-70B-Instruct is llama-3.3-70b. If two of your ids map to the same model, the first is offered.

A model in our catalog takes its official price as the reference, and you sell into its market. An id that maps to nothing we know is listed too, as its own model, at a price you set. Context length is read from your list (max_model_len, context_length): nothing to fill in.

What happens next

  1. 1
    Check key
    In Sell › Providers (open it), pick Your own server, paste the URL and the key, and Check key. The URL and key are sealed together, and we read your model list: a few seconds.
  2. 2
    Health test and identity checks
    One short streamed request per model, whose answer must name the model, then a fixed prompt that checks token counts and max_tokens. A model that fails is not added; the wizard says why and links the fix below.
  3. 3
    Pricing
    Every model starts on Match the best offer: we keep your price one step under the best other offer, never under your floor. Or set your own price; a model with no reference needs one. Pricing has the details.
  4. 4
    Go live
    Accept the own-server statement and go live, within about a minute. A request comes to you when your offer is the best one for its model; you earn your price on the tokens the buyer is billed for.
  5. 5
    Random checks
    We test your models at random with real prompts, about once an hour per model. A model that fails is checked again at once with another prompt; if it fails again, a full identity check runs. It is not unlisted. A model that errors or times out while serving gets no traffic for a short pause that grows with each failure, until a request or our 5-minute ping succeeds. A request that fails is not billed to the buyer, and you are not paid for it.
  6. 6
    Payouts
    Earnings are payable 7 days after the request is billed (pending until then, in case a buyer's card payment is reversed). Payouts are sent in batches once your balance reaches $5. They are USDC on Base, to a wallet signed in to your account. Earnings and payouts explains the rest.
The checks run on your server and cost you its compute: a ping every 5 minutes, about 24 random checks a day per model (each up to 512 output tokens), and an identity set every 6 hours.

Troubleshooting

The wizard links the entry for each failure.

  • TLS or connection errors

    We connect over HTTPS only and check the certificate like a browser: it must come from a public authority and name your host. Self-signed and private-CA certificates fail, and so does a bare IP address without one. Check that the port is open to the internet (443, or 1024 and up), that a firewall does not drop us, and that curl https://llm.example.com/v1/models works from another network. Caddy in front of your server solves most of this.

  • 401 or 403

    Your server refused the key. The key in the console must be the one your server expects (vLLM's and SGLang's --api-key, llama.cpp's --api-key, the Caddy check for Ollama), and it is sent as Authorization: Bearer <key>. A server with no key needs the field left empty. While live, a 401 or 403 stops traffic to the server for an hour; fix the key with Rotate on Positions.

  • 404 on /models, or no models: the URL misses /v1

    Paste the base URL, ending in /v1: we add /models to it, so https://llm.example.com asks for /models and most servers answer 404. Paste nothing after /v1 either. GET /v1/models must answer 200 with {"data": [{"id": "…"}]}. We do not follow redirects: paste the final https address. An empty list means the server has not loaded a model yet.

  • A model id we don't know

    Every id your server lists is offered, mapped by the rule in Model ids. An id that maps to no model in our catalog has no reference price: you set your own price for it. To sell into a known model's market, serve it under that model's id (vLLM and SGLang --served-model-name, llama.cpp --alias). If the health test says unavailable, your server answered 404 for the id: chat completions must accept the exact id /v1/models lists.

  • Timeouts

    /v1/models must answer within 20 seconds. The health test waits 30 seconds for the first streamed token and 60 seconds in all. When live, a request waits about 10 seconds for your first token before it moves to the next source, so keep the model loaded and warm: a cold start or a model swapped out of memory looks like a timeout. For Ollama, set OLLAMA_KEEP_ALIVE=-1.

  • The address was refused

    We only reach public addresses. The host name must resolve, and every address it resolves to must be public: private (10.x, 172.16-31.x, 192.168.x), loopback, link-local, carrier-grade NAT (100.64.x), IPv6 unique-local and other reserved ranges are refused. Names like localhost or *.local never work, and neither does a URL with a user name, a password, a query string or a port under 1024 other than 443.

  • Bad request, stream error or server error

    Your server must take an OpenAI chat request as it is: stream: true with stream_options.include_usage, and temperature, seed, max_tokens and tools when a buyer sends them. A stream must be server-sent events ending with data: [DONE]. Run the test above: a 400 or a broken stream there is what the health test saw. Answers without usage still work, but we then estimate the tokens.

  • Answers as another model, or identity checks failed

    The model field of each answer must be the id you listed (or left out). The identity checks then send a fixed prompt and compare the token counts your server reports with that model's, check that max_tokens is respected, and that nothing long is added to the prompt. A different model under a known model's id, a chat template that adds a long system prompt, or usage counts that are off fail them. List the model under its own id instead.