Setup recipes
Copy-paste blocks for the Ollama Cloud API, the OpenAI SDKs, coding agents, fallback chains, n8n and the usage API.
Before you start
Keep your key in an environment variable, not in code or in a browser. The examples read it as OLLAMA_API_KEY.
export OLLAMA_API_KEY="your-key-here"- Direct API calls name models as they appear at
https://ollama.com/api/tags, for examplegemma4:31b. - In the Ollama app and CLI, add the
:cloudsuffix, for examplegemma4:cloud.
Direct API and the OpenAI SDKs
The native API is at https://ollama.com/api/chat, with a Bearer header. The OpenAI-compatible base URL is https://ollama.com/v1, and both SDK examples point there.
curl https://ollama.com/api/chat \
-H "Authorization: Bearer $OLLAMA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gemma4:31b",
"messages": [{"role": "user", "content": "Say hello in five words."}],
"stream": false
}'import os
from openai import OpenAI
client = OpenAI(
base_url="https://ollama.com/v1",
api_key=os.environ["OLLAMA_API_KEY"],
)
reply = client.chat.completions.create(
model="gemma4:31b",
messages=[{"role": "user", "content": "Say hello in five words."}],
)
print(reply.choices[0].message.content)import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://ollama.com/v1",
apiKey: process.env.OLLAMA_API_KEY,
});
const reply = await client.chat.completions.create({
model: "gemma4:31b",
messages: [{ role: "user", content: "Say hello in five words." }],
});
console.log(reply.choices[0].message.content);Coding agents
Each command starts a coding agent that is already signed in to Ollama. Run them in a terminal where the Ollama CLI is installed. The integrations page lists the rest.
ollama launch claude
ollama launch codex
ollama launch opencodeA fallback chain
- Try the first model. A reply ends the chain.
- On 429 or 502, retry with exponential backoff, then move to the next model.
- The last entry is a local model you run yourself.
- If the error says “monthly usage limit”, stop. The account is capped, and another model will not get past it before the reset or until credits are added.
import os
import time
from openai import APIConnectionError, APIStatusError, OpenAI
# max_retries=0: the SDK must not retry on its own, or it would also retry a monthly-limit 429.
CLOUD = OpenAI(base_url="https://ollama.com/v1", api_key=os.environ["OLLAMA_API_KEY"], max_retries=0)
LOCAL = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama", max_retries=0) # local server ignores the key
# Tried in order. Cloud names come from https://ollama.com/api/tags, local names from "ollama list".
CHAIN = [
(CLOUD, "glm-5.3-flash"),
(CLOUD, "gemma4:31b"),
(LOCAL, "your-local-model"),
]
def ask(prompt: str) -> str:
for client, model in CHAIN:
for attempt in range(3):
try:
reply = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
max_tokens=2048, # thinking models need room for reasoning and the answer
)
return reply.choices[0].message.content or ""
except APIConnectionError:
break # this endpoint cannot be reached: next model in the chain
except APIStatusError as err:
if err.status_code == 429 and "monthly usage limit" in str(err):
raise # the account is capped: no model switch or retry helps until the reset or credits
if err.status_code not in (429, 502):
raise # a bug in the request, not a capacity problem
if attempt < 2:
time.sleep(2 ** attempt) # exponential backoff: 1 s, then 2 s
raise RuntimeError("Every model in the chain failed.")import OpenAI from "openai";
// maxRetries: 0 stops the SDK retrying on its own, so the loop below decides every retry.
const CLOUD = new OpenAI({ baseURL: "https://ollama.com/v1", apiKey: process.env.OLLAMA_API_KEY, maxRetries: 0 });
const LOCAL = new OpenAI({ baseURL: "http://localhost:11434/v1", apiKey: "ollama", maxRetries: 0 }); // ignores the key
// Tried in order. Cloud names come from https://ollama.com/api/tags, local names from "ollama list".
const CHAIN = [
{ client: CLOUD, model: "glm-5.3-flash" },
{ client: CLOUD, model: "gemma4:31b" },
{ client: LOCAL, model: "your-local-model" },
];
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
export async function ask(prompt) {
for (const { client, model } of CHAIN) {
for (let attempt = 0; attempt < 3; attempt++) {
try {
const reply = await client.chat.completions.create({
model,
messages: [{ role: "user", content: prompt }],
max_tokens: 2048, // thinking models need room for reasoning and the answer
});
return reply.choices[0].message.content ?? "";
} catch (err) {
const text = String(err?.message ?? err);
if (err?.status === 429 && text.includes("monthly usage limit")) throw err; // capped: stop
if (err?.status === 429 || err?.status === 502) {
if (attempt < 2) await sleep(1000 * 2 ** attempt); // exponential backoff: 1 s, then 2 s
continue;
}
if (err?.status === undefined) break; // connection failure: next model in the chain
throw err; // a bug in the request, not a capacity problem
}
}
}
throw new Error("Every model in the chain failed.");
}Both versions turn off the SDK’s own retries. Otherwise the SDK would retry a monthly-limit 429 as well. The reasons are in Agents and automation and When capped.
n8n
Two things to set up: an OpenAI credential for model calls, and a usage monitor for your account.
- OpenAI credential. Use your Ollama key, and set the base URL to https://ollama.com/v1.
Credential type: OpenAI
API key: <your Ollama API key>
Base URL: https://ollama.com/v1- Usage monitor. The download is a single-account workflow. It reads usage every five hours, spaces its reads 12 seconds apart, and posts a plain-text report to a webhook you choose. It needs one Header Auth credential named “Ollama API key”.
Credential type: Header Auth
Name: Ollama API key
Header name: Authorization
Header value: Bearer <your Ollama API key>- Import the file in n8n (Workflows, then Import from file). Choose your Ollama API key credential for the three Ollama nodes.
- Replace
https://example.com/your-webhookin the last node with your own webhook URL. - Activate the workflow. It runs every five hours, and its webhook trigger runs it on demand.
For a new plan the report shows exact dollars against the included credits: $60 on Pro, $300 on Max and $1,000 on Team. The reset is on the subscription day, which is an estimate. For an old plan it shows estimated five-hour and weekly windows, labelled EST.
The usage API
GET https://ollama.com/api/usage?range=24h (or 7d, or 30d) returns totals and buckets. A 24-hour read gives hourly buckets, and the 7-day and 30-day reads give daily buckets. Any other range returns 400.
The limit is about five reads a minute. Past that, the API returns 429 “too many requests”. Space reads 12 seconds apart. Eight seconds is too fast for about five reads a minute.
# Read the three usage ranges, one read every 12 seconds (about five reads a minute is the limit).
for range in 24h 7d 30d; do
curl -s "https://ollama.com/api/usage?range=$range" \
-H "Authorization: Bearer $OLLAMA_API_KEY"
echo
sleep 12
done
# Account and plan. POST only: a GET returns 405.
curl -s -X POST https://ollama.com/api/me \
-H "Authorization: Bearer $OLLAMA_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'| Field | Old plan | New plan |
|---|---|---|
| request_count | Yes | Yes |
| usage_usd | No | Yes, dollars |
| input_tokens, cached_input_tokens, output_tokens | No | Yes |
| Quota percentages (limits.session, limits.weekly) | Removed about 7 Oct 2026 | Removed about 7 Oct 2026 |
Use POST for the account read. GET returns 405. The plan name in its reply tells you which plan the key is on, and whether the usage read carries dollars tells you the same thing.
Check the real numbers
Paste the same key on the home page to see the windows that apply to your plan, read from the usage API.