Ollama: Run Open-Weight LLMs Locally, REST API and Modelfile Reference Guide
By DevShelfHub
Run open-weight LLMs locally — CLI, REST API, Modelfiles, structured output, multimodal, and Python client. Ollama's OpenAI-compatible endpoint lets you swap it into any LangChain, AutoGen, or llama-index application with a single base_url change.
105 items
◷ 8 min
Local
REST
Modelfile
Start hereQuick start · 6 you’ll reach for daily
Pull a modelollama pull llama3.2:3b
Chat REPLollama run llama3.2:3b
List localollama list
HTTP chatPOST /api/chat
EmbeddingsPOST /api/embed
Custom modelollama create m -f Modelfile
Target versions · paceVersions
Targets:ollama ≥ 0.4ollama-python ≥ 0.4ollama-js ≥ 0.5OpenAI-compat API ≥ v1
Ollama exposes both a native REST API on :11434 and an
OpenAI-compatible surface at /v1. Names changed in 0.3+:
/api/embeddings (singular, legacy) is now
/api/embed (preferred, batched). Tool calling needs a
capable model (Llama 3.1+, Qwen2.5, Mistral). This sheet pins to behavior current as of May 2026.
Legacy Single-input version. Migrate to /api/embed.
GET /api/tags
List local models.
GET /api/ps
Currently loaded models.
POST /api/pull
Download a model. Streams progress.
POST /api/show
Modelfile, parameters, template.
DELETE /api/delete
Remove a model.
stream: false
Default streams; set false for one JSON object.
keep_alive: "30m" | 0 | -1
Keep loaded (30m), unload now (0), or forever (-1).
OpenAI-compatible
base_url="http://127.0.0.1:11434/v1"
Drop-in for any OpenAI SDK.
api_key="ollama"
Required field, value ignored.
POST /v1/chat/completions
Same shape as OpenAI. Tools, JSON mode supported.
POST /v1/embeddings
OpenAI-shaped embeddings response.
ollama-pythonPython client
python
from ollama import Client
client = Client(host="http://127.0.0.1:11434")
# Chat — multi-turn
resp = client.chat(
model="llama3.2:3b",
messages=[
{"role": "system", "content": "Answer in one sentence."},
{"role": "user", "content": "What is RAG?"},
],
options={"temperature": 0.2, "num_ctx": 4096},
)
print(resp.message.content)
# Stream
for chunk in client.chat(model="llama3.2:3b",
messages=[{"role": "user", "content": "Count to 5"}],
stream=True):
print(chunk.message.content, end="", flush=True)
# Embed
e = client.embed(model="nomic-embed-text", input=["hello", "world"])
print(len(e.embeddings), len(e.embeddings[0]))
Client(host="…")
Sync client. Override OLLAMA_HOST.
AsyncClient()
asyncio variant; await client.chat(…).
client.list()
Like ollama list.
client.pull("llama3.2")
Programmatic pull. Streams progress events.
client.show("llama3.2")
Modelfile + params dict.
format="json"
Constrain output to JSON.
format=Schema.model_json_schema()
Constrain to a Pydantic schema. 0.4+
Declarative model specModelfile
bash
# Modelfile — declarative spec for a custom model
FROM llama3.2:3b
# System prompt baked in
SYSTEM """
You are a senior Python engineer. Reply with code first, prose second.
"""
# Generation defaults
PARAMETER temperature 0.3
PARAMETER top_p 0.9
PARAMETER num_ctx 8192
PARAMETER repeat_penalty 1.1
PARAMETER stop "<|end|>"
# Optional: chat template override
TEMPLATE """{{ if .System }}<|system|>{{ .System }}<|end|>
{{ end }}<|user|>{{ .Prompt }}<|end|>
<|assistant|>"""
# Build it:
# ollama create py-coder -f Modelfile
# ollama run py-coder "factorial in python"
FROM <model | path/to/gguf>
Base. Can be a registry tag or local GGUF file.
SYSTEM "…"
Default system prompt.
PARAMETER temperature 0.3
Sampling default.
PARAMETER num_ctx 8192
Context window. Bigger = more VRAM.
PARAMETER num_predict 512
Max new tokens.
PARAMETER stop "<|end|>"
Stop sequence. Repeat for multiple.
TEMPLATE """…"""
Override the chat template (Go template syntax).
ADAPTER ./lora.safetensors
Attach a LoRA adapter.
LICENSE "…"
Embed a license string.
MESSAGE user "…" / assistant "…"
Bake few-shot turns.
Constrained JSONStructured output
python
from ollama import Client
from pydantic import BaseModel
class Person(BaseModel):
name: str
age: int
city: str
client = Client()
resp = client.chat(
model="llama3.2:3b",
messages=[{"role": "user",
"content": "Extract: Ada is 29 and lives in Berlin."}],
format=Person.model_json_schema(), # constrained JSON output
options={"temperature": 0},
)
person = Person.model_validate_json(resp.message.content)
print(person)
# Person(name='Ada', age=29, city='Berlin')
format="json" just nudges the model. Passing a JSON schema uses constrained sampling — the model literally cannot emit invalid JSON. Use the schema form for anything you’ll parse.
Function callingTool calling
python
from ollama import Client
def get_weather(city: str) -> str:
return f"{city}: 22C, clear"
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Current weather for a city",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
},
}]
client = Client()
resp = client.chat(
model="llama3.2:3b", # must be a tool-calling capable model
messages=[{"role": "user", "content": "Weather in Tokyo?"}],
tools=tools,
)
for call in resp.message.tool_calls or []:
name, args = call.function.name, call.function.arguments
result = {"get_weather": get_weather}[name](**args)
print(name, "->", result)
tools=[{"type":"function","function":…}]
OpenAI-shaped tool spec.
resp.message.tool_calls
List of calls the model wants to make. Loop & execute.
{"role":"tool","content":…,"tool_name":…}
Send the result back for the next turn.
Capable models
Llama 3.1+, Qwen2.5, Mistral-Instruct, Command-R. Small Phi/Gemma will not call tools.
Per-request knobsInference options
temperature: 0.0–1.5
Higher = more diverse. 0 = greedy.
top_p: 0.9
Nucleus sampling. Pair with temperature.
top_k: 40
Sample from top-k tokens. Lower = stricter.
repeat_penalty: 1.1
>1 discourages repetition. 1.0 = off.
num_ctx: 4096
Context size for this call. Overrides Modelfile.
num_predict: -1 | n
Max output tokens. -1 = until stop/EOS.
num_gpu: 999
Layers to offload to GPU. 0 = CPU-only.
seed: 42
Reproducible sampling.
mirostat: 1 | 2
Alt sampler with perplexity target.
Local RAG in ~25 linesEnd-to-end · tiny local RAG
Pulls double duty: nomic-embed-text for vectors, llama3.2:3b for the answer. Zero external dependencies beyond NumPy.
python
from ollama import Client
import numpy as np
client = Client(host="http://127.0.0.1:11434")
# 1. Embed a tiny corpus
corpus = [
"Ollama runs LLMs locally.",
"FastAPI is a Python web framework.",
"Vector databases store embeddings for retrieval.",
]
M = np.array(
client.embed(model="nomic-embed-text", input=corpus).embeddings,
dtype=np.float32,
)
M /= np.linalg.norm(M, axis=1, keepdims=True)
# 2. Retrieve
question = "how do I run a model on my laptop?"
q = np.array(client.embed(model="nomic-embed-text", input=question).embeddings[0])
q /= np.linalg.norm(q)
top = (M @ q).argsort()[::-1][:2]
context = "\n".join(corpus[i] for i in top)
# 3. Answer
resp = client.chat(
model="llama3.2:3b",
messages=[
{"role": "system", "content": f"Use context:\n{context}"},
{"role": "user", "content": question},
],
)
print(resp.message.content)
Best practiceGood to know
Treat Ollama as a server, not a CLI.
For anything beyond a REPL, hit /api/chat directly or via the SDK. Spawning ollama run per request reloads the model every time.
Use the OpenAI-compat endpoint for portability.
Pointing OpenAI(base_url="…/v1") at Ollama means the same code runs against OpenAI in prod by swapping one env var.
Keep one big context, not many small ones.num_ctx allocates a KV cache per loaded model. Setting 32k on a 3B model is fine; on a 70B it eats your VRAM. Match it to your real prompts.
Common trapsWatch out for
Default context is small.
Llama 3.1’s metadata says 128k but Ollama defaults num_ctx=2048 to save RAM. Long-context tasks silently truncate — raise num_ctx in the Modelfile or per-request.
:latest drifts.
Tags get rebuilt on the registry. Pin to a specific quant (llama3.1:8b-instruct-q4_K_M) so behavior doesn’t shift mid-incident.
Ollama is an open-source tool that lets you run large language models locally on your own hardware. It handles model downloading, GPU acceleration (Apple Metal, NVIDIA CUDA, AMD ROCm), and exposes a simple CLI and OpenAI-compatible REST API. Use it for private AI, offline development, and cost-free LLM experimentation.
How do I run a model with Ollama?
Install Ollama, then run ollama run llama3.2 to download and start an interactive chat session. For one-shot queries use ollama run mistral "explain async/await". To serve the REST API in the background, run ollama serve — it listens on port 11434 by default.
What models does Ollama support?
Ollama supports hundreds of open-weight models including Llama 3, Mistral, Gemma, Phi, Qwen, DeepSeek, and Nomic Embed. Find the full list at ollama.com/library. Models with vision support (llava, llama3.2-vision) accept image inputs. Quantised variants (Q4, Q8) trade quality for smaller GPU memory footprint.
What is an Ollama Modelfile?
A Modelfile is a text recipe that customises or extends an existing model. It sets the base model (FROM), a system prompt (SYSTEM), parameters like temperature and context size (PARAMETER), and a chat template. Build your custom model with ollama create my-model -f Modelfile and share it with ollama push.
How do I use the Ollama REST API?
Ollama exposes a REST API at http://localhost:11434. POST to /api/generate for single-turn completions or /api/chat for multi-turn conversations. The chat endpoint is OpenAI-compatible, so any library that supports a custom base_url (LangChain, LlamaIndex, openai-python) works without modification.
How do I use Ollama with Python?
Install the official client (pip install ollama) then call ollama.chat(model="llama3.2", messages=[{"role":"user","content":"Hello"}]). For streaming responses add stream=True and iterate the generator. The client also exposes ollama.generate(), ollama.embeddings(), and ollama.pull() matching the REST API endpoints.