Updated ยท Sources & corrections
In this guide
images array. A path such as ./photo.png is not the image content. SDK conveniences and raw HTTP requests are different interfaces.If an image works in the CLI or Python SDK but fails in your HTTP client, first check what your client actually sends. This guide builds a request from one local file, verifies its encoding offline and sends it only when you explicitly enable that step.
Choose the interface before copying an example
Ollama’s vision documentation distinguishes SDK file inputs from REST base64 data. This page uses the native POST /api/chat endpoint, with the image attached to a user message and streaming disabled. It is not a recipe for OpenAI-compatible image content blocks.
| Interface | Input to examine |
|---|---|
| CLI | Image-path handling documented for the installed CLI and model. |
| SDK | The image inputs accepted and serialized by that SDK version. |
| Native REST chat | The message’s images array containing encoded image bytes. |
You need Python 3.10 or later and a readable image file. The offline step needs no Ollama server. Sending requires a local server and an installed vision-capable checkpoint. Choose a candidate using our vision-model evaluation guide; an arbitrary text-only tag is insufficient.
Build and inspect the payload without sending it
Save the following as vision_request.py. It reads a bounded amount of data, uses Python’s base64 module and checks that decoding produces the original bytes. The 10 MiB cap is this example’s application policy, not a documented Ollama input limit.
import argparse
import base64
import json
from pathlib import Path
import urllib.request
MAX_BYTES = 10 * 1024 * 1024 # Example application limit, not Ollama's limit.
def build_payload(model, image_path):
if not isinstance(model, str) or not model.strip():
raise ValueError("Provide an exact installed vision-model tag")
path = Path(image_path).expanduser().resolve(strict=True)
if not path.is_file():
raise ValueError("Image path must be a regular file")
with path.open("rb") as image_file:
raw = image_file.read(MAX_BYTES + 1)
if not raw or len(raw) > MAX_BYTES:
raise ValueError("File is empty or exceeds this example's 10 MiB limit")
encoded = base64.b64encode(raw).decode("ascii")
# Check transport encoding; this is not an image decoder or quality test.
if base64.b64decode(encoded, validate=True) != raw:
raise ValueError("Base64 round-trip failed")
payload = {"model": model, "stream": False, "messages": [{
"role": "user", "content": "Describe the visible image briefly.",
"images": [encoded]}]}
return payload, len(raw)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("model")
parser.add_argument("image_path")
parser.add_argument("--send", action="store_true")
args = parser.parse_args()
payload, byte_count = build_payload(args.model, args.image_path)
body = json.dumps(payload).encode("utf-8")
if not args.send:
print(json.dumps({"file_bytes": byte_count, "json_bytes": len(body),
"image_count": 1, "request_sent": False}))
return
request = urllib.request.Request(
"http://127.0.0.1:11434/api/chat", data=body,
headers={"Content-Type": "application/json"}, method="POST")
with urllib.request.urlopen(request, timeout=120) as response:
result = json.load(response)
if not isinstance(result, dict) or result.get("done") is not True:
raise ValueError("Incomplete or malformed chat response")
message = result.get("message")
text = message.get("content") if isinstance(message, dict) else None
if result.get("error") or result.get("done_reason") != "stop" or not isinstance(text, str) or not text.strip():
raise ValueError("No completed text answer; inspect the response separately")
print(text)
if __name__ == "__main__":
main()
Run python vision_request.py YOUR_LOCAL_VISION_TAG "path/to/image.png" with your own tag and file. The default prints only a size/count summary and request_sent: false. It does not print the image’s base64 or contact a model. Relative paths resolve from the Python process’s working directory; use an absolute path when that location is uncertain.
The pathlib reference documents filesystem path operations. A Windows host path will not automatically exist inside a Linux container. The file must be readable where this Python process runs, before any network request is attempted.
Send one controlled local request
After the offline check, add --send: python vision_request.py YOUR_LOCAL_VISION_TAG "path/to/image.png" --send. Use a non-sensitive image you have permission to process. Confirm the tag is a local vision model and the destination is your intended local service. This script does not download a checkpoint or switch to a paid fallback.
The helper stops on HTTP or connection errors. It accepts text only from a completed response ending with stop; other response types need separate inspection. A transport timeout is not evidence of an invalid image. For server setup, see our Ollama configuration guide; for missing tags, use the model-not-found diagnostic.
Interpret failures at the correct layer
| Failure | What to check |
|---|---|
| File not found or unreadable | Client working directory, exact path, container mount and file access. The server has not received the file. |
| Empty file or example size-limit error | The selected input and your application’s size policy. Do not treat the example cap as the model’s maximum. |
| Base64 or image-decoding error from the server | The actual request field, original image file and supported format for the installed deployment. Do not encode the path string instead of the file bytes. |
| Text-only checkpoint or missing model | The exact installed tag and its documented capabilities. |
| JSON parser reports extra data | Whether the actual request disabled streaming. For streamed replies, use our NDJSON parsing guide. |
| Plausible answer that misses visible details | The image, model and task-specific evaluation. A well-formed request does not establish visual accuracy. |
This code does not decode pixels or prove an image is valid, safe or supported. Encoding arbitrary nonempty bytes can pass the offline check. A web upload service needs its own file-type validation, resource controls and authorization rather than accepting arbitrary filesystem paths from users.
What was checked
On 26 October 2026, four local unittest cases passed: byte/JSON round-trip using a small constructed PNG fixture and a filename containing spaces, rejection of an empty file, a missing file and an application-limit violation. The article’s embedded Python was syntax-checked and matched the tested file. No live Ollama server, model or image-recognition result was tested.
Your next step is to check one known image locally, then record the checkpoint, server version, expected visible facts and actual answer. Evaluate encoding success and answer quality separately.
Sources and verification scope
AI Models News reviewed the linked official Ollama and Python references on 26 October 2026. The script is original application code; fixture checks validate request preparation, not model compatibility or accuracy. Read our editorial policy.