Before you start
You need Ollama running, as set up in lesson 1, and one model on disk. The examples use gemma4, the model in most of Ollama's API examples, so pull it first or swap in any tag you already have. For the code you also need Python 3 with two packages, ollama and openai.
An API here is a set of web addresses that take JSON and return JSON. The local server answers on port 11434. Requests from your own machine need no key, and the server listens on 127.0.0.1 only, so another computer cannot reach it until you change OLLAMA_HOST.
Ollama does not version this API strictly. Its introduction page says the API is expected to stay stable and backwards compatible, and there are official libraries for Python and JavaScript.
There are two ways in, and the choice decides what you can reach. The native routes under /api expose everything Ollama does, including per-request options, keep_alive and thinking control. The /v1 routes copy OpenAI's format, so code and tools written for OpenAI run unchanged, minus a few fields.
The same API also runs in Ollama's cloud. For a cloud model the base becomes https://ollama.com/api, and each request carries an Authorization: Bearer header with an OLLAMA_API_KEY from your account settings. That key belongs on a server, never in browser code or in source control. Everything below uses the local server, where no key exists to leak.
The API grew in steps, each announced on Ollama's blog. A tutorial written before a step will not mention it, so check its date against this list.
ollama pull gemma4
pip install ollama openaiOpenAI compatibility
The
/v1routes let code written for OpenAI's API call a local model.Source: Ollama blog: OpenAI compatibility (read November 5, 2026)
Tool support
/api/chattakestools, and a model that supports them answers withtool_calls.Source: Ollama blog: Tool support (read November 5, 2026)
Structured outputs
formataccepts a JSON schema, so the reply comes back in the shape you define.Source: Ollama blog: Structured outputs (read November 5, 2026)
Streaming with tool calls
Replies keep streaming while the model calls tools.
Source: Ollama blog: Streaming responses with tool calling (read November 5, 2026)
Anthropic API compatibility
Claude Code runs on Ollama from v0.14.0, through an Anthropic-style Messages API.
Source: Ollama blog: Claude Code with Anthropic API compatibility (read November 5, 2026)
Claude Desktop support
Select Claude in the Ollama app and switch it on. The gateway sets itself up, for local and cloud models.
Source: Ollama blog: Claude Desktop support with Ollama (read November 5, 2026)
Send your first request with curl
Start with the simplest endpoint. The generate endpoint, /api/generate, takes one prompt and returns one answer. Adding "stream": false makes the server wait and send a single JSON object, which is easier to read the first time. The chat endpoint, /api/chat, takes a list of messages instead, each with a role and its content.
Neither endpoint remembers earlier calls. With /api/chat you send the whole conversation every time: the earlier questions, the assistant's earlier answers, then the new question. Leave a turn out and the model never sees it. Every turn also makes the prompt longer, and the prompt_eval_count in each reply shows it growing.
Both endpoints accept more than text. An options object holds the settings for one request: temperature, top_p, a seed for repeatable output, stop sequences, num_predict to cap the length of the reply and num_ctx for the context length. For a model that reads pictures, images takes a list of base64-encoded files.
Thinking models add one more field. With think you ask for the model's reasoning or switch it off, and some models, such as gpt-oss, take a level like "low" instead of true or false. The reasoning comes back in its own thinking field, apart from the answer. To see which values a model accepts, ask /api/show.
A request with a model name and nothing else is useful too. It loads the model without generating anything, so the first real question does not wait for the load.
# is the server up, and which models does it have?
curl http://localhost:11434/api/tags
# one prompt, one answer
curl http://localhost:11434/api/generate -d '{
"model": "gemma4",
"prompt": "Why is the sky blue?",
"stream": false
}'
# a conversation: send the messages so far
curl http://localhost:11434/api/chat -d '{
"model": "gemma4",
"messages": [
{
"role": "user",
"content": "why is the sky blue?"
}
],
"stream": false
}'- Run the
/api/tagscall. It returns amodelslist with each model's name, its size on disk in bytes and details such asparameter_sizeandquantization_level. An error here means the server is not running. - Run the
/api/generaterequest. The answer is in theresponsefield, anddoneistrue. - Run the
/api/chatrequest. The answer now sits inmessage.content, and itsroleisassistant. - Append that assistant message and a follow-up question to
messages, then send the request again. The model can now refer to its own first answer. - Remove
"stream": falsefrom either request and run it once more. The reply arrives as many lines, one JSON object per line, each holding the next piece of text.
/api/generate | /api/chat | |
|---|---|---|
| What you send | prompt, one string | messages, a list of role and content pairs |
| System prompt | The system field | A message whose role is system |
| Earlier turns | None, each call stands alone | Whatever history you put in messages |
| Where the answer is | response | message.content |
| Extra inputs | suffix for fill-in-the-middle models, raw to skip the prompt template | tools for function calling, and tool messages for the results |
Call it from Python: the Ollama library or the OpenAI SDK
The official Python library wraps the same endpoints. Its chat() function takes a model name and a list of messages. With stream=True the call returns chunks instead of one answer, and each chunk holds the next piece of the reply in message.content. The loop below prints them as they arrive, so the first words show up before the model has finished.
Streaming is the server's default, and it uses newline-delimited JSON: one complete JSON object per line, sent as soon as there is more text. A chunk carries its piece of the answer and done: false. The last chunk has done: true, a done_reason such as stop, and the statistics for the whole run, as the streaming page shows.
Those statistics answer most questions about speed. Every duration is in nanoseconds. Tokens per second, the usual speed figure, is eval_count divided by eval_duration, times one billion. A token is a piece of text, often part of a word, and what tokens are covers why models count in them.
The generate reference prints one reply in full as its example. It came from gemma4 and ends with done_reason: stop, prompt_eval_count: 11 and eval_count: 18, so that prompt cost eleven input tokens and the answer eighteen.
Chat is one call of many. Next to it sit generate, list, show, create, copy, delete, pull, push, embed and ps, which cover what the command line does, so a script can pull a model before it uses it.
To reach a server on another address, build a client yourself: Client(host='http://localhost:11434') takes the host and any extra headers.
Plenty of tools already speak OpenAI's API, and Ollama answers the same requests under /v1. You change the base URL and keep the rest of the code. The client refuses to start without an API key, and Ollama ignores whatever you pass, so any string works.
The second snippet is the compatibility page's own example with the model swapped for gemma4. The routes it lists are /v1/chat/completions, /v1/completions, /v1/models, /v1/embeddings and /v1/responses, plus audio routes for speech, transcription and translation.
Chat completions ignore logprobs, tool_choice, logit_bias, user and n, and images must be base64 data, not URLs. The Responses route is stateless, so previous_response_id and conversation don't work and you resend the history each time.
Nothing in the OpenAI API sets the context length. The workaround is a new model built from a Modelfile with a larger num_ctx, which any OpenAI client can then name in model.
JavaScript works the same way. The OpenAI client there takes baseURL: "http://localhost:11434/v1/" and apiKey: "ollama", and the rest of the call does not change.
Code written for Claude has a route as well. Ollama supports a subset of Anthropic's Messages API, so the Anthropic client works with its base URL set to http://localhost:11434. That client adds /v1/messages to the address by itself.
from ollama import chat
stream = chat(
model='gemma4',
messages=[{'role': 'user', 'content': 'Why is the sky blue?'}],
stream=True,
)
for chunk in stream:
print(chunk['message']['content'], end='', flush=True)
# the same model through the OpenAI SDK
from openai import OpenAI
client = OpenAI(
base_url='http://localhost:11434/v1/',
api_key='ollama', # required but ignored
)
chat_completion = client.chat.completions.create(
messages=[
{
'role': 'user',
'content': 'Say this is a test',
}
],
model='gemma4',
)
print(chat_completion.choices[0].message.content)| Field | What it holds |
|---|---|
prompt_eval_count | Input tokens in the prompt |
prompt_eval_cached_count | Prompt tokens read from the cache instead of processed again |
eval_count | Tokens the model generated |
load_duration | Time spent loading the model, which shows up on a cold start |
prompt_eval_duration | Time spent on the prompt tokens that were not cached |
eval_duration | Time spent generating the answer |
total_duration | Time for the whole request |
Save a system prompt in a Modelfile
A Modelfile is a short text file that builds a new model from one you already have. The FROM line names the base model. Each PARAMETER line sets an option, such as num_ctx, the context length in tokens, or temperature, which controls how random the wording gets. The SYSTEM line holds the system prompt that every request to the new model carries.
ollama create reads the file and registers the name. From then on the new model works everywhere a model name does: in ollama run, in /api/chat and in the OpenAI SDK. The SYSTEM line is a prompt like any other, and the system prompt lesson shows how to write a good one.
A model pulled from Hugging Face takes the same settings another way. A file named system in its repository sets the system prompt, and a JSON file named params sets the parameters.
The Modelfile reference lists more instructions: TEMPLATE for the full prompt template, MESSAGE for message history, LICENSE and REQUIRES for the minimum Ollama version.
FROM gemma4
PARAMETER num_ctx 8192
PARAMETER temperature 0.3
SYSTEM You answer questions about the user's notes in plain English. If the notes do not say, answer that you do not know.- Save the four lines above as
Modelfilein an empty folder. - Run
ollama create notes-helper -f ./Modelfile. - Run
ollama run notes-helperand ask it something your notes would not cover. - Print what you built with
ollama show --modelfile notes-helper. Running the same command ongemma4shows the template and parameters your file inherits.
Georgi Gerganov on why local models seem to fail
Georgi Gerganov builds ggml-org, the GitHub organization behind llama.cpp, and works as an engineer at Hugging Face. In a reply on X in March, he answered a question about local models. He recommended Qwen 3.5 for its range of sizes. Then he warned that most problems people hit sit outside the model: in the program that drives it, in the chat template and in the way the prompt is built.
His advice was to start with full-quality models that fit your hardware, and to know what the program around the model does. Hooking Claude Code or Codex to a local model and expecting magic, he wrote, does not work. Quantize for speed only once things click. For his own tests he picked Q8_0 variants of most models to keep most of the original quality.
The API puts the template in your hands. On /api/chat, Ollama applies the model's own chat template to your messages. The raw option on /api/generate skips it, so leave raw off unless you rebuild the template yourself. ollama show --modelfile prints the template a model uses. When a local model fails a task, read that template and the request your client sends before you blame the model.
I think the consensus is that Qwen3.5 is a step change so atm I would recommend explore that, given that it covers a range of sizes suitable for all devices.
A screenshot of X's own embed of the post, taken on 5 November 2026, with the time in UTC. X cuts the post short with Show more. Select it to open the post on X.
Common mistakes
- Parsing a streamed reply as one JSON document. Without
"stream": falsethe body is many objects, one per line, so read it line by line. - Sending only the newest message to
/api/chat. The server keeps no conversation, so the model forgets every turn you leave out. - Calling the API from a web page or an extension and getting blocked. By default Ollama accepts browser requests only from
127.0.0.1and0.0.0.0origins. Add others toOLLAMA_ORIGINS, for examplechrome-extension://*. - Ignoring
done_reason. A reply capped bynum_predictcan stop mid-sentence, anddone_reasonis the field that says why the reply ended. - Letting the model unload in the middle of a batch job. Each request can carry
keep_alive: a negative value keeps the model loaded, and0unloads it right after the answer. - Putting a cloud key in a desktop app or a web page. Calls to
https://ollama.com/apineedOLLAMA_API_KEY, so send them from your own server instead. - Asking for JSON in the prompt and hoping. Set
formatto"json", or to a JSON schema object, and the reply comes back in that structure. - Naming a model in
/v1code thatollama lsdoes not list. The local server runs only models on disk, so the fix isollama pull, not a new key.
Try it yourself
- Send the same prompt to
/api/generatetwice, once with"stream": falseand once without. How many lines does the streamed reply have? - Work out tokens per second from
eval_countandeval_durationin one response. - List your models from Python with the library's
listcall and print each name. - Change the OpenAI example's
base_urlback and forth between Ollama and a cloud service. Which lines changed? - Add
PARAMETER num_ctxto your Modelfile, create the model again and check the CONTEXT column inollama pswhile it runs. - Send
"keep_alive": 0with a request and watch the model leaveollama psstraight after the answer. - Ask a thinking model the same question with
thinkon and off, and compare thethinkingfield with the answer.
Next: LM Studio runs the same kind of local server inside a desktop app, with a model browser and an OpenAI-compatible endpoint of its own. Lesson 3 sets it up and compares its lms command line with the Ollama commands you know now.
Check yourself
Where is the answer in a non-streaming /api/chat response?
Show answer
B. message.content The chat endpoint returns an assistant message, so the text is in message.content, while /api/generate uses response.
Your second /api/chat call ignores the first question. What is missing?
Show answer
C. The earlier turns in the messages list The server keeps no conversation, so every call must carry the history you want the model to see.
What do you pass as the API key when the OpenAI SDK talks to Ollama?
Show answer
A. Any string, since Ollama ignores it The client requires a value, and the local server ignores it.
How do you give an OpenAI-style client a longer context window?
Show answer
D. Create a model from a Modelfile with PARAMETER num_ctx The OpenAI API has no context-length field, so the setting goes into a new model the client can name.
Frequently asked questions
How do I call the Ollama API from PowerShell?
Use Invoke-WebRequest, as the Windows page does: (Invoke-WebRequest -method POST -Body '{"model":"llama3.2", "prompt":"Why is the sky blue?", "stream": false}' -uri http://localhost:11434/api/generate ).Content | ConvertFrom-json. Swap llama3.2 for a model you have pulled. Keep "stream": false in the body, because ConvertFrom-json expects one JSON object, not one per line.
Can another computer on my network use the Ollama API?
Yes. Bind it to every network interface with OLLAMA_HOST=0.0.0.0:11434, set where the app or service reads its environment, then restart Ollama. Anyone who reaches the port can then send requests, so add a firewall rule that admits only your own machines.
Does the Ollama API support tool calling?
Yes, through /api/chat. Pass function definitions in tools, and when the reply contains tool_calls, run the function and send the result back as a message with the role tool. The chat reference shows it with qwen3 and a get_current_weather function. The model only asks for the call, and your code decides whether to run it.
Can the Ollama API create embeddings?
Yes. Pull an embedding model such as embeddinggemma, then send text to /v1/embeddings or call embed in the Python library. Each text comes back as a list of numbers, the form a search index stores, and the RAG course builds on it.
Sources
- Ollama Docs: API introduction https://docs.ollama.com/api/introduction
- Ollama Docs: Generate a response https://docs.ollama.com/api/generate
- Ollama Docs: Generate a chat message https://docs.ollama.com/api/chat
- Ollama Docs: List models https://docs.ollama.com/api/tags
- Ollama Docs: Streaming https://docs.ollama.com/api/streaming
- Ollama Docs: OpenAI compatibility https://docs.ollama.com/api/openai-compatibility
- Ollama Docs: Anthropic compatibility https://docs.ollama.com/api/anthropic-compatibility
- Ollama Docs: Modelfile reference https://docs.ollama.com/modelfile
- Ollama Docs: FAQ https://docs.ollama.com/faq
- Ollama Docs: Windows https://docs.ollama.com/windows
- GitHub: ollama/ollama-python https://github.com/ollama/ollama-python
- Ollama blog: OpenAI compatibility https://ollama.com/blog/openai-compatibility
- Ollama blog: Tool support https://ollama.com/blog/tool-support
- Ollama blog: Structured outputs https://ollama.com/blog/structured-outputs
- Ollama blog: Streaming responses with tool calling https://ollama.com/blog/streaming-tool
- Ollama blog: Claude Code with Anthropic API compatibility https://ollama.com/blog/claude
- Ollama blog: Claude Desktop support with Ollama https://ollama.com/blog/claude-desktop
- Georgi Gerganov on X: the main issues with local models https://x.com/ggerganov/status/2038674698809102599

