Semurg is on PyPI: pip install semurg, and your Python code gets a memory that cites its sources
21 October 2026 · Semurg · 4 min read · Markdown
pythonsdkpypigetting-startedhow-to
The Python client for Semurg is on PyPI. One pip install semurg and your code can store what it learns, find it again by name or by question, and get back the passages that support an answer, each cited to where it came from. The memory runs on your own machine; the client talks to it and to nothing else.
Install
The client needs Python 3.9 or newer and nothing outside the standard library. It is Apache-2.0 licensed, and it runs wherever Python runs. The node it talks to runs on x86-64 Linux, natively or in Docker; from a Mac or an ARM machine, point the client at a node on an x86-64 Linux machine (for example over an SSH tunnel: ssh -L 4000:127.0.0.1:4000 you@that-machine).
python3 -m venv .venv && . .venv/bin/activate
pip install semurgOn Ubuntu, sudo apt install python3-venv first if venv is missing.
Start a node
No node yet? One Docker command starts one on your machine, listening only there. The token it prints is the key to its memory:
docker run -d --name semurg -p 127.0.0.1:4000:4000 \
--ulimit memlock=-1 --ulimit nofile=1048576:1048576 --security-opt seccomp=unconfined \
-v semurg-data:/var/lib/semurg one.semurg.io/semurg
until docker logs semurg 2>&1 | grep -q 'Your API token'; do sleep 1; done
export SEMURG_API_TOKEN=$(docker exec semurg cat /var/lib/semurg/.api_token)Prefer a native install on Ubuntu? The install page has the one-line installer.
Store, recall, ask
The program below ran as written against a fresh node of release r11.121.20261021.3619d35, with semurg 0.1.1 from PyPI. Save it as quickstart.py and run it with python quickstart.py:
import os
from semurg import Client
with Client("http://127.0.0.1:4000", token=os.environ["SEMURG_API_TOKEN"]) as mem:
print(mem.health()["build"]["release"])
# facts you extracted, stored as typed entities and a relationship
mem.remember("research", facts={
"entities": [{"name": "Ada Lovelace", "type": "person"},
{"name": "Analytical Engine", "type": "machine"}],
"relations": [{"source": "Ada Lovelace", "target": "Analytical Engine", "type": "wrote_notes_on"}],
})
# text, stored as cited passages, linked to the names it mentions
mem.remember("research", source="notes-1843", date="1843-09-01",
content="Ada Lovelace published notes on the Analytical Engine. "
"Her Note G describes an algorithm for Bernoulli numbers.")
# everything connected to one entity
answer = mem.recall("research", subject="Ada Lovelace", compact=True)
print(answer["state"])
for item in answer["items"]:
if item["kind"] == "fact":
print(item["subject"], item["relation"], item["object"])
# a question in plain words: the evidence, cited to its source
ctx = mem.context("research", query="What did Ada Lovelace describe in Note G?")
print(ctx["grounding"]["state"])
for item in ctx["items"]:
if item["kind"] == "passage":
print(item["citation"]["source"]["name"], "|", item["text"][:70])It prints:
r11.121.20261021.3619d35
answered
Ada Lovelace wrote_notes_on Analytical Engine
grounded
notes-1843 | Ada Lovelace published notes on the Analytical Engine. Her Note G descFour things happened. remember with facts stored two typed entities and the relationship between them, exactly as sent: no model guessed at them. remember with content stored the text as passages under the source label notes-1843, dated 1843-09-01, and linked each passage to the names it mentions. recall found everything connected to Ada Lovelace and said so in state. And context answered a question in plain words with the passage that holds the answer, cited to its source, with a grounding verdict: grounded here, because the passage covers the question.
Read the state before the items
Every answer starts with state: answered when something matched, nothing_stored when nothing did, and could_not_judge when a step could not run, named in the answer. An empty list is never presented as an answer. context adds grounding, which tells your agent whether the evidence really answers the question (grounded), only shares some of its words (related), or is missing, so it can say "I do not know" instead of guessing.
Where to go next
- One memory, many programs: a
scopeis a named memory, and every program or agent that uses the same scope shares it. - Each of your users apart: pass
user_idand every call stays inside that user's own memory. See one memory per end user. - Time: what was true on a date, and what was known then. See memory with a timeline.
- Reasoning: facts that follow from a relationship's meaning. See facts that follow.
- Agents: the same memory over MCP for Claude Code and other agents, in five minutes.
- Every memory call the client makes is in the API description.