Ollama API Tutorial: Connect a Local LLM to Python or Node.js
Send a local chat request with explicit timeouts, validation and useful error handling.
Ollama's local API lets a Python application send messages to a supported model and read a generated response. Start with a non-streaming request, explicit timeout and error handling, then add conversation history or streaming only when needed. Keep the endpoint local and validate responses before connecting generated text to actions.
Prepare the service and verify the model
Install Ollama using its official instructions and run your chosen local model directly first. Record the exact model tag and licence. A working command-line conversation separates model problems from bugs in your application.
The chat endpoint documentation specifies a POST request to /api/chat, with a model and messages. Its local example uses port 11434. This loopback address is part of the local service interface, not a public website destination.
Choose a small model that fits your machine. The example below follows the documented gemma4 name as syntax; it is not a recommendation that the model fits every computer. Replace it only with a tag you have deliberately selected and made available locally.
Use harmless sample prompts while inspecting network behaviour. An interface that supports both local and cloud models needs an explicit local selection when privacy is required.
Make a bounded request with the standard library
Save this instructional example in a Python file. It needs a running compatible Ollama service; it was not executed for this guide.
import json
import urllib.error
import urllib.request
payload = {
"model": "gemma4",
"messages": [{"role": "user", "content": "Explain recursion briefly."}],
"stream": False,
}
request = urllib.request.Request(
"http://localhost:11434/api/chat",
data=json.dumps(payload).encode("utf-8"),
headers={"Content-Type": "application/json"},
method="POST",
)
try:
with urllib.request.urlopen(request, timeout=120) as response:
data = json.load(response)
text = data["message"]["content"]
if not isinstance(text, str):
raise ValueError("Expected a text response")
print(text)
except (urllib.error.URLError, TimeoutError, ValueError, KeyError) as error:
print(f"Local model request failed: {error}")
The timeout is an illustrative application choice, not an official performance expectation. Adjust it to your task and cancellation design. Do not remove timeouts simply because loading a large model occasionally takes too long.
Setting stream to false makes the first example easier to inspect. Streaming requires processing incremental messages and handling incomplete responses. Confirm the selected endpoint's current streaming format before adapting the parser.
Turn the example into a careful application
Preserve the distinction between a transport success and a correct answer. Receiving valid JSON proves that the service returned a parseable response, not that recursion was explained correctly or a generated fact is true.
For conversations, send the history needed for the next answer in the documented message structure. Avoid retaining unlimited transcripts. Longer histories consume context and memory, and saved messages may include information users expect to remain private.
Handle failures according to cause. A missing model, unavailable service, memory error and request timeout need different remedies. Show a useful user-facing error while keeping detailed diagnostics in restricted logs. Avoid logging the complete prompt by default.
Retries should be bounded and appropriate. Retrying a harmless generation request differs from retrying an action initiated by a generated response. Once tools can create files, send messages or change records, use explicit action identifiers and approval rules outside the model.
If the application expects structured data, request a defined shape where supported and validate it in code. Check required fields, allowed values and business rules. Valid syntax can still contain fabricated names or unsupported conclusions.
For multi-user operation, add authentication, request limits, cancellation and an access-controlled application layer. A local development endpoint should not be exposed broadly without a deployment design. Browser access and cross-origin settings do not replace user authorisation.
Your next step is to wrap one useful, checkable task around this request and write a few failure cases. Keep the integration small until you know which response properties and limits your application actually needs.
Quick questions, clear answers
Do I need a third-party Python SDK?
Not for a simple HTTP request. The standard library can demonstrate the protocol with few dependencies. An official client library may simplify streaming or richer integrations later, but choose it for a concrete benefit and confirm its interface against current documentation.
Why does the first request take longer?
It may include model loading or other initial work. Separate startup from warmed inference when measuring performance. Do not label that delay a network problem without inspecting service logs and memory, and avoid promising response times from a different machine's benchmark.
Can I let the answer execute commands?
Only through a separate controlled tool layer with validated arguments and appropriate approval. Generated text must not be passed directly to a shell. Keep permissions narrow, inspect proposed changes and test with synthetic data before connecting a model to valuable files or external services.
About this guide
Aniket Thakur prepared this guide from secondary research checked on 22 October 2026. No first-hand software testing or performance benchmarking was conducted. Examples are instructional and require validation in your environment.