// documentation

AgentRAM API Reference

Everything your AI agent needs to read and write persistent memory. All endpoints return JSON. Authentication is handled via an API key in the request header.

Prefer an SDK? Official clients wrap this API so your agent stores and recalls in one call, with no raw HTTP: Python pip install agentram-sdk and JavaScript/TypeScript npm install agentram-sdk. The examples below map one-to-one to the SDK methods. Full usage on the Python and npm package pages.

Quickstart

Three steps to give your agent a working memory.

Step 1: Register, and we email your API key

curl
curl -X POST https://api.agentram.dev/register \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com"}'
response
{
  "success": true,
  "message": "Check your email for your API key."
}
Your API key is emailed to you, not returned in the response. This keeps it out of request logs and stops anyone from probing which emails are registered. The email contains your key and your 1,000 free credits.

Step 2: Store a memory

Use the API key from your welcome email in the x-api-key header (shown below as agentram_7f3a9b2c...).

curl
curl -X POST https://api.agentram.dev/memory \
  -H "x-api-key: agentram_7f3a9b2c..." \
  -H "Content-Type: application/json" \
  -d '{"agent_id":"agent-01","key":"user_pref","value":"prefers short answers"}'

Step 3: Read it back in any later session

curl
curl https://api.agentram.dev/memory?agent_id=agent-01&key=user_pref \
  -H "x-api-key: agentram_7f3a9b2c..."
That's it. Your agent now has persistent memory that survives across sessions, devices, and restarts.

Authentication

Every request except /register requires your API key in the request header.

header
x-api-key: agentram_your_key_here

Requests without a valid API key return a 401 Unauthorized response. Keep your API key private. Treat it like a password.


Credits

AgentRAM uses a credit system. Each operation costs one credit. Credits are deducted only on success. If a request fails for any reason, your credit is returned automatically.

OperationCredit cost
POST /memory1 credit
GET /memory1 credit
GET /memories1 credit
GET /memory/search1 credit
DELETE /memory1 credit
POST /memory/shared1 credit
GET /memory/shared1 credit
GET /memories/shared1 credit
POST /namespace0 credits
GET /credits0 credits
POST /register0 credits
POST /recover0 credits
POST /recover/confirm0 credits
Running low? Check your balance with GET /credits at any time. Top up at agentram.dev/#pricing.

Endpoints

Register

POST /register

Creates a new account and emails you an API key with 1,000 free credits. For security, the key is sent by email rather than in the response, and the response is identical whether or not the email is already registered. One account per email address.

Body paramTypeRequiredDescription
email string required Your email address. Used to identify your account.
response · 200
{
  "success": true,
  "message": "Check your email for your API key."
}

Write a memory

POST /memory

Stores a value under a key for a given agent. Writing to an existing key updates the value. No duplicates are created.

Body paramTypeRequiredDescription
agent_id string required A unique identifier for the agent. Max 100 characters.
key string required The memory label. Max 200 characters.
value string required What to remember. Max 5,000 characters.
ttl_days number optional Days until this memory expires automatically. Once expired, reads return 404 and the memory is excluded from lists and searches. Omit for memories that should not expire.
response · 200
{
  "success": true,
  "data": {
    "agent_id": "agent-01",
    "key": "user_pref",
    "credits_remaining": 999
  }
}

Read a memory

GET /memory

Retrieves a stored value by agent ID and key. Returns 404 if the memory does not exist, and refunds the credit.

Query paramTypeRequiredDescription
agent_id string required The agent identifier used when writing the memory.
key string required The memory label to retrieve.
response · 200
{
  "success": true,
  "data": {
    "agent_id": "agent-01",
    "key": "user_pref",
    "value": "prefers short answers",
    "created_at": "2026-05-09T18:00:00.000Z",
    "credits_remaining": 998
  }
}

Delete a memory

DELETE /memory

Permanently removes a stored memory. This cannot be undone.

Body paramTypeRequiredDescription
agent_idstringrequiredThe agent identifier.
keystringrequiredThe memory label to delete.
response · 200
{
  "success": true,
  "data": { "deleted": true, "credits_remaining": 997 }
}

Check credit balance

GET /credits

Returns the current credit balance for your account. This endpoint never deducts a credit. Safe to call as often as needed.

response · 200
{
  "success": true,
  "data": {
    "email": "you@example.com",
    "credits": 997
  }
}

List all memories for an agent

GET /memories

Returns all memories stored under a given agent ID, ordered by most recently written. Expired memories are silently excluded.

Query paramTypeRequiredDescription
agent_idstringrequiredThe agent identifier.
limitintegeroptionalMax results to return. Default 50, max 200.
response · 200
{
  "success": true,
  "data": [
    { "key": "user_lang", "value": "French", "created_at": "...", "expires_at": null },
    { "key": "tone", "value": "formal", "created_at": "...", "expires_at": null }
  ],
  "credits_remaining": 996
}
GET /memory/search

Case-insensitive text search across both the key and value fields for a given agent. Returns matching memories ordered by most recent. No embeddings or configuration required.

Query paramTypeRequiredDescription
agent_idstringrequiredThe agent identifier.
qstringrequiredThe search term. Matched against key and value fields.
response · 200
{
  "success": true,
  "data": [
    { "key": "user_lang", "value": "French", "created_at": "..." }
  ],
  "credits_remaining": 995
}

Shared memory

Shared namespaces allow multiple agents or API key holders to read and write to a common memory pool. Create a namespace first, then share the namespace_key with any agent that needs access.

Create a namespace

POST /namespace

Creates a new shared namespace and returns a unique namespace key. Free, costs no credits. Any agent with a valid API key and the namespace key can then read and write to that namespace.

Body paramTypeRequiredDescription
labelstringoptionalA human-readable name for the namespace. Max 100 characters.
response · 201
{
  "success": true,
  "data": { "namespace_key": "ns_a1b2c3d4...", "label": "team-alpha" }
}

Write a shared memory

POST /memory/shared

Writes a value to a shared namespace. Any API key holder who knows the namespace key can write. Supports optional TTL.

Body paramTypeRequiredDescription
namespace_keystringrequiredThe namespace key returned from POST /namespace.
keystringrequiredThe memory label. Max 200 characters.
valuestringrequiredWhat to store. Max 5,000 characters.
ttl_daysnumberoptionalDays until this memory expires automatically.

Read a shared memory

GET /memory/shared

Retrieves a value from a shared namespace by key.

Query paramTypeRequiredDescription
namespace_keystringrequiredThe namespace key.
keystringrequiredThe memory label to retrieve.

List all shared memories

GET /memories/shared

Returns all memories in a shared namespace, ordered by most recent. Expired memories are silently excluded.

Query paramTypeRequiredDescription
namespace_keystringrequiredThe namespace key.
limitintegeroptionalMax results. Default 50, max 200.

Account recovery

If you lose your API key, these two endpoints issue a replacement by email. Both are free and require no authentication. The flow is: call POST /recover with your email, receive a link by email, then that link's page calls POST /recover/confirm with the token to issue a new key. Most developers never call these directly, since the recovery page handles it, but they are documented here for completeness.

Request a recovery link

POST /recover

Sends a single-use recovery link to the given email if an account exists. Always returns the same generic success response, whether or not the email is registered, to prevent anyone discovering which addresses have accounts.

Body paramTypeRequiredDescription
email string required The email address on the account you want to recover.
response · 200
{
  "success": true,
  "message": "If this email exists in our system, you'll receive recovery instructions shortly."
}

The recovery link expires 30 minutes after it is sent and can be used only once. Requesting a new link invalidates any previous one for the same email.

Confirm a recovery link

POST /recover/confirm

Validates a recovery token and issues a new API key for the account. The token is consumed on first use, so a second call with the same token is rejected. Because keys are stored hashed and never in plain text, the original key cannot be shown again, so recovery rotates it instead. The old key stops working immediately, which also means a leaked key is revoked the moment you recover. Update anywhere the old key was deployed.

Body paramTypeRequiredDescription
token string required The token from the recovery link (the value after ?token= in the emailed URL).
response · 200
{
  "success": true,
  "data": {
    "email": "you@example.com",
    "api_key": "agentram_7f3a9b2c...",
    "credits": 1000
  }
}

A used, expired, or invalid token returns success: false with a message explaining that a new link must be requested. Recovering also sends a notification email to the account, so an unexpected recovery is visible to the owner.


Temporal memory

POST /memory overwrites in place, which is what you want for most things. Some facts have a history that matters: the last invoice number, the model an agent is 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. Reads return the value that is currently true, and the full chain stays available.

Assertions are a separate keyspace. An assertion named invoice_number and a memory named invoice_number are unrelated and do not see each other. Use flat memory for values you are happy to overwrite, and assertions for values whose history you care about.

Assert a fact

POST /assertion

Records a value as a new assertion. Pass supersedes to say which earlier assertion this one replaces. Without it the write simply appends: if something is already live for the key, the key becomes contested and the next read returns 409 rather than guessing between the two.

Body paramTypeRequiredDescription
agent_id string required A unique identifier for the agent.
key string required The fact label. Max 256 characters.
value string required The value being asserted.
supersedes uuid optional The assertion_id this write replaces. It must be a live assertion under the same agent and key. Superseding also clears any other live assertion for that key, which is how a contested key gets resolved.
valid_until string optional An ISO 8601 instant when the fact stops being true. Past that moment it no longer appears as current.
ttl_days number optional The same thing as a rolling window from now. Give valid_until or ttl_days, never both.
written_by string optional Provenance: who wrote this, usually an agent name. Max 256 characters.
context string optional Provenance: a project or session tag. Max 256 characters.
response · 200
{
  "success": true,
  "data": {
    "assertion_id": "9c1f...  ",
    "agent_id": "agent-01",
    "key": "invoice_number",
    "value": "1044",
    "state": "live",
    "written_by": "billing-agent",
    "written_at": "2026-07-31T09:14:22Z"
  },
  "credits_remaining": 998
}

Returns 409 if the assertion named in supersedes is no longer live, meaning another writer changed the key first. Re-read the key and retry. Returns 400 if it does not reference an assertion under this agent and key.

Retire a fact

POST /assertion/retire

Ends a fact with nothing replacing it. Use this when something stopped being true and no new value took its place. Afterwards the key has no current value, but the history survives: the retirement is itself recorded as an assertion with its own author and timestamp, so you can always see who ended it and when. That is the difference from DELETE /memory, which erases and leaves no trace.

Body paramTypeRequiredDescription
agent_idstringrequiredA unique identifier for the agent.
keystringrequiredThe fact to retire.
written_bystringoptionalWho retired it.
contextstringoptionalA project or session tag.
response · 200
{
  "success": true,
  "data": {
    "assertion_id": "4b7e...  ",
    "key": "invoice_number",
    "state": "retired",
    "cleared": 1
  },
  "credits_remaining": 997
}

cleared is how many live assertions the retirement ended. More than one means the key was contested at that moment. Returns 404 if nothing was live to retire.

Read the current fact

GET /assertion

Returns the assertion that is currently true for a key, with its provenance and the assertion_id you need to supersede it. Returns 404 when nothing is live, whether the key was never asserted, was retired, or passed its valid_until.

Query paramTypeRequiredDescription
agent_idstringrequiredA unique identifier for the agent.
keystringrequiredThe fact to read.
resolvestringoptionalOnly last-write-wins is accepted. On a contested key it returns the newest value instead of a 409. Any other value is rejected with 400. Safe to pass on every read, since it does nothing unless there is a conflict.
response · 200
{
  "success": true,
  "data": {
    "key": "invoice_number",
    "value": "1044",
    "assertion_id": "9c1f...  ",
    "written_by": "billing-agent",
    "written_at": "2026-07-31T09:14:22Z"
  },
  "credits_remaining": 996
}

When a key is contested. If two agents write the same key without either knowing about the other, there are two live values and neither replaced the other. Returning one of them would be a guess, and silently reading a stale value is the exact failure assertions exist to prevent. So the read returns 409 with both, newest first, and you decide.

response · 409
{
  "success": false,
  "error": "Key has conflicting live assertions",
  "conflict": {
    "key": "invoice_number",
    "assertions": [
      { "assertion_id": "9c1f...  ", "value": "1044", "written_by": "agent-a" },
      { "assertion_id": "3d80...  ", "value": "1099", "written_by": "agent-b" }
    ]
  }
}

Resolve it by asserting a new value that supersedes one of them. The others stop being current at the same time, so the key is settled in one write.

List current facts

GET /assertions

Everything currently true for an agent, one entry per key. Retired keys and keys past their valid_until do not appear.

Query paramTypeRequiredDescription
agent_idstringrequiredA unique identifier for the agent.
resolvestringoptionalOnly last-write-wins. Fills in values for contested keys instead of withholding them.
limitnumberoptionalDefault 50, maximum 200.
response · 200
{
  "success": true,
  "data": {
    "assertions": [
      {
        "key": "deploy_target",
        "value": "production",
        "contested": false,
        "assertion_count": 1,
        "assertion_id": "7a22...  ",
        "written_by": "deploy-agent"
      },
      {
        "key": "invoice_number",
        "contested": true,
        "assertion_count": 2
      }
    ],
    "truncated": false
  },
  "credits_remaining": 995
}

A contested key is flagged and its value is withheld, for the same reason the single-key read refuses one: guessing across a list is the same mistake as guessing on one key. Read that key on its own to see the competing values. truncated is true when more entries exist beyond the limit.

Read a fact's history

GET /assertion/history

Every assertion ever written for a key, newest first. This is the audit trail: what each value was, who wrote it, when, and which one it replaced. It is what turns "why does the agent still think that?" into something you can read.

Query paramTypeRequiredDescription
agent_idstringrequiredA unique identifier for the agent.
keystringrequiredThe fact whose history you want.
limitnumberoptionalDefault 50, maximum 200.
response · 200
{
  "success": true,
  "data": {
    "key": "invoice_number",
    "assertions": [
      { "value": "1044", "state": "live",       "written_by": "agent-a" },
      { "value": "1043", "state": "superseded", "written_by": "agent-a" }
    ],
    "truncated": false
  },
  "credits_remaining": 994
}

state is live for the current value, superseded for one that was replaced, and retired for a retirement marker.


Error codes

All errors return a consistent shape with a success: false flag and a plain-language message.

StatusMeaningCommon cause
200SuccessRequest completed, memory read or written.
201CreatedNew account registered successfully.
400Bad requestMissing or invalid body parameters.
401UnauthorizedMissing or invalid API key.
402Payment requiredCredit balance is zero. Top up to continue.
404Not foundMemory does not exist. Credit refunded.
409ConflictTwo live assertions share a key and neither replaces the other, or the assertion you tried to supersede is no longer current. Credit refunded. See GET /assertion.
429Rate limitedToo many requests. Slow down and retry.
500Server errorSomething went wrong on our end. Try again shortly.
error shape
{
  "success": false,
  "error": "Insufficient credits"
}

Rate limits

All memory endpoints share one simple limit: 60 requests per minute, per API key. That is fast enough to sit inside an agent loop without getting in your way. A few sensitive endpoints have their own stricter limits to prevent abuse, listed below.

EndpointLimitWindow
All memory endpoints60 requestsPer minute, per API key
POST /register10 requestsPer hour, per IP
POST /recover3 requestsPer hour, per email
POST /recover10 requestsPer hour, per IP

The 60-per-minute limit covers storing, retrieving, listing, searching, deleting, shared-namespace operations, and credit checks, everything except registration and recovery. Rate-limited requests return a 429 status. Credits are not deducted for rate-limited requests.


Code examples

Python

python
# pip install requests
import requests

API_KEY = "agentram_your_key_here"
BASE = "https://api.agentram.dev"
HEADERS = {"x-api-key": API_KEY, "Content-Type": "application/json"}

# Write a memory
requests.post(f"{BASE}/memory", json={
    "agent_id": "agent-01",
    "key": "user_language",
    "value": "French"
}, headers=HEADERS)

# Read it back
res = requests.get(f"{BASE}/memory",
    params={"agent_id": "agent-01", "key": "user_language"},
    headers=HEADERS)
print(res.json()["data"]["value"])  # → "French"

JavaScript / Node

javascript
const API_KEY = "agentram_your_key_here";
const BASE = "https://api.agentram.dev";
const headers = { "x-api-key": API_KEY, "Content-Type": "application/json" };

// Write a memory
await fetch(`${BASE}/memory`, {
  method: "POST", headers,
  body: JSON.stringify({ agent_id: "agent-01", key: "tone", value: "formal" })
});

// Read it back
const res = await fetch(`${BASE}/memory?agent_id=agent-01&key=tone`, { headers });
const { data } = await res.json();
console.log(data.value); // → "formal"
Need help? Email hello@agentram.dev and we'll get back to you within one business day.

© 2026 AgentRAM. All rights reserved.