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

Metadata

Release files for agentram-sdk 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for agentram-sdk 0.2.0
File Size Uploaded Provenance
agentram_sdk-0.2.0.tar.gz 15.3 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for agentram-sdk 0.2.0
File Interpreter ABI Platform
agentram_sdk-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 29.4 kB

Release files / agentram_sdk-0.2.0.tar.gz

Download URL agentram_sdk-0.2.0.tar.gz
Size 15.3 kB
Tags Source
SHA-256 checksum
How to use checksums
50e8c569b84e7ce4e26dd02e5826b83d9938aedae457455af0576bba1d8b7926
BLAKE2b-256 checksum
How to use checksums
3a837a21911108f9aeef6e3b7b727bab390d2a411d40394b2e3f757fd8109c6b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Release files / agentram_sdk-0.2.0-py3-none-any.whl

Download URL agentram_sdk-0.2.0-py3-none-any.whl
Size 14.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
aaddfd1c8a5acda9081004745c65099d7c52be9a31b656e8fab385a794b4e29e
BLAKE2b-256 checksum
How to use checksums
fa2fbe4aa63238be74b78394c1925149e72925a8716c8a8f6f8dc547fe88b8c6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Security for agentram-sdk 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files No provenance

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