Skip to main content

AgentRAM Python SDK

Persistent memory for AI agents, in two API calls. This is the official Python client for AgentRAM - a simple, credit-based HTTP API that gives your agents long-term memory. No vector database, no embeddings, no infrastructure to run.

Zero third-party dependencies (standard library only).

Install

pip install agentram-sdk

Installs as agentram-sdk on PyPI, but you import it as agentram in code (the install name and import name differ, which is common for Python packages).

Get a key

Sign up at agentram.dev for an API key. New accounts start with 1,000 free credits, no card required.

Quickstart

from agentram import AgentRAM

ram = AgentRAM(api_key="agentram_...", agent_id="agent-01")

# Store something (1 credit)
ram.store("user_language", "French")

# Read it back later, even in a brand-new session (1 credit)
lang = ram.recall("user_language")   # -> "French"  (or None if missing/expired)

That's the whole idea: one call to remember, one to recall.

Everything you can do

# Personal memory (scoped to an agent_id)
ram.store("tone", "formal", ttl_days=30)   # auto-expire after 30 days
ram.recall("tone")                          # -> "formal" | None
ram.delete("tone")                          # -> True | False
ram.list(limit=50)                          # -> [{"key","value","created_at","expires_at"}, ...]
ram.search("lang")                          # -> matching records (text search, no embeddings)

# Shared memory (several agents reading/writing one pool)
ns = ram.create_namespace("team-alpha")     # -> {"namespace_key": "ns_...", "label": "team-alpha"}
ram.store_shared(ns["namespace_key"], "goal", "ship v1")
ram.recall_shared(ns["namespace_key"], "goal")   # -> "ship v1" | None
ram.list_shared(ns["namespace_key"])

# Temporal memory: facts that change over time, with history
ram.update_fact("invoice_number", "1044")   # replaces what's current (2 credits)
ram.current("invoice_number")               # -> the assertion that is true now | None
ram.retire("invoice_number")                # -> True | False (ends it, keeps the trail)
ram.list_facts()                            # -> everything currently true, one per key
ram.history("invoice_number")               # -> every version, newest first

# Account
ram.credits()               # -> current balance (free)
ram.credits_remaining       # last known balance, updated after every call (no extra request)

You can override the agent per call: ram.store("k", "v", agent_id="agent-02").

Temporal memory: facts that change

store() and recall() overwrite in place. That is the right shape for most things, but some facts have a history that matters: the last invoice number, the model an agent is currently using, the deploy target for a project. When one of those changes you often want to know what it used to be, who changed it, and when.

Assertions are an append-only log for exactly that. Each write records a value plus who wrote it and which earlier value it replaced.

ram.update_fact("invoice_number", "1043", written_by="billing-agent")
ram.update_fact("invoice_number", "1044", written_by="billing-agent")

fact = ram.current("invoice_number")
fact["value"]        # '1044'
fact["written_by"]   # 'billing-agent'
fact["written_at"]   # '2026-07-31T...'

for a in ram.history("invoice_number"):
    print(a["written_at"], a["value"], a["state"])   # live / superseded / retired

update_fact() is the everyday call: it reads what is current and links the new value to it, so the chain stays intact. It costs 2 credits because it is a read plus a write.

Seeing everything at once

list_facts() returns one entry per key, with the value and who wrote it:

for fact in ram.list_facts():
    if fact["contested"]:
        print(fact["key"], "needs resolving")
    else:
        print(fact["key"], "=", fact["value"], "by", fact["written_by"])

A contested key comes back flagged and without a value, for the same reason current() refuses one: guessing across a list is the same mistake as guessing on a single read. Pass resolve=LAST_WRITE_WINS to fill those in with the newest value. Retired and expired keys do not appear.

When two writers disagree

If two agents write the same key without either knowing about the other, the key is contested: there are two live values and neither replaced the other. The store will not pick one for you, because silently returning whichever came back first is the exact bug this is meant to prevent. It tells you instead:

from agentram import ConflictError, LAST_WRITE_WINS

try:
    fact = ram.current("invoice_number")
except ConflictError as conflict:
    for a in conflict.assertions:        # newest first
        print(a["value"], "from", a["written_by"], "at", a["written_at"])
    winner = conflict.assertions[0]
    ram.assert_fact("invoice_number", "1045", supersedes=winner["assertion_id"])

Asserting a value that supersedes one of them resolves the conflict: the others stop being current too.

If you would rather never handle this and just take the newest value, ask for it explicitly:

fact = ram.current("invoice_number", resolve=LAST_WRITE_WINS)

That is safe to pass on every call, since it does nothing unless there is an actual conflict. It is spelled out in full on purpose. It is last-write-wins, with last-write-wins's failure mode, and that should be a decision you made rather than a default you inherited.

retire() is not delete()

delete() erases a flat memory and leaves nothing behind. retire() ends a fact while keeping its history: current() returns None afterwards, but the retirement is itself recorded, with who did it and when, so the trail survives.

A separate keyspace

Assertions and flat memories do not see each other. An assertion called "invoice_number" and a memory called "invoice_number" are two unrelated things. Use store()/recall() for facts you are happy to overwrite, and assertions for facts whose history you care about.

Errors

Everything inherits from AgentRAMError, so one except catches all of it:

from agentram import AgentRAM, InsufficientCreditsError, RateLimitError, AgentRAMError

try:
    ram.store("k", "v")
except InsufficientCreditsError:
    ...  # balance hit zero - top up at agentram.dev/#pricing
except RateLimitError:
    ...  # 60 requests/minute per key - back off and retry
except AgentRAMError as e:
    print(e.status_code, e.message)

recall() and recall_shared() return None for a missing or expired memory rather than raising, and delete() returns False - so the common "not there" case stays out of your try/except. current() and retire() behave the same way for assertions.

ConflictError is the one error that carries extra data: .assertions holds every competing value when a key is contested, which is what you need to resolve it. See Temporal memory above.

Notes

  • Rate limit: 60 requests/minute per API key. The client automatically retries 429 and 5xx responses a couple of times with backoff.
  • Credits: writes and reads cost 1 credit; update_fact() costs 2 (a read plus a write); create_namespace() and credits() are free. Full pricing at agentram.dev.
  • TTL: pass ttl_days to expire a memory automatically.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

agentram_sdk-0.2.0.tar.gz (15.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

agentram_sdk-0.2.0-py3-none-any.whl (14.0 kB view details)

Uploaded Python 3

File details

Details for the file agentram_sdk-0.2.0.tar.gz.

File metadata

  • Download URL: agentram_sdk-0.2.0.tar.gz
  • Upload date:
  • Size: 15.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for agentram_sdk-0.2.0.tar.gz
Algorithm Hash digest
SHA256 50e8c569b84e7ce4e26dd02e5826b83d9938aedae457455af0576bba1d8b7926
MD5 b3d0de868d16a5d5edb3c404f18fccc6
BLAKE2b-256 3a837a21911108f9aeef6e3b7b727bab390d2a411d40394b2e3f757fd8109c6b

See more details on using hashes here.

File details

Details for the file agentram_sdk-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: agentram_sdk-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 14.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for agentram_sdk-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 aaddfd1c8a5acda9081004745c65099d7c52be9a31b656e8fab385a794b4e29e
MD5 b43167d84b08c744cdec873051ab3af0
BLAKE2b-256 fa2fbe4aa63238be74b78394c1925149e72925a8716c8a8f6f8dc547fe88b8c6

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page