API documentation

Build long-term memory into your agents

FishMem is a mem0-compatible memory API. Use API keys from the FishMem dashboard to add memories from conversations, run hybrid graph + vector search, and manage scoped memory stores from your backend.

Get started

Quickstart

Two calls cover most agents: write conversation turns withPOST /v1/memories, then recall relevant facts withPOST /v1/memories/searchbefore each model call. Every memory lives under a scope key (user, agent, or run) so retrieval stays per-user.

const API_KEY = process.env.FISHMEM_API_KEY;
const BASE_API_URL = "https://fishmem.com";

async function addMemories() {
  const response = await fetch(`${BASE_API_URL}/v1/memories`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${API_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      messages: [
        { role: "user", content: "I'm vegetarian and I'm allergic to nuts." },
        { role: "assistant", content: "Got it — vegetarian, no nuts." }
      ],
      user_id: "alice"
    })
  });

  if (!response.ok) {
    throw new Error(`add failed: ${response.status} ${await response.text()}`);
  }

  return response.json(); // { results: [{ id, memory, event }] }
}
async function searchMemories(query: string) {
  const response = await fetch(`https://fishmem.com/v1/memories/search`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.FISHMEM_API_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ query, user_id: "alice", top_k: 5 })
  });

  const { results } = await response.json();
  // Feed the memories into your prompt before calling the model.
  return results.map((r) => r.memory).join("\n");
}

Security

Authentication

Create a backend key

Open API Keys in the dashboard, create a named key, and copy the secret immediately. Keys start withrm_and are shown once.

Send a Bearer token

Include the API key in theAuthorizationheader for every server-side request.

Authorization: Bearer rm_your_api_key

Keep API keys in a secret manager or backend environment variable. Do not embed them in browsers, mobile apps, public repos, support tickets, or analytics logs. Every key is bound to one workspace and only reads that workspace's memories.

Write

Add memories

POST/v1/memories

Add memories from a conversation

Withinfer: true(the default) an LLM extracts dated atomic facts from the input and reconciles them against the scope's existing memories, deciding per fact whether to ADD, UPDATE, INVALIDATE, or DELETE. An INVALIDATE keeps the superseded fact with a closed validity interval, so "used to live in Berlin" stays searchable after the user moves. Withinfer: falsethe input is stored verbatim as a single memory — cheaper, and useful for notes or pre-extracted facts.

Request body

messages

Required · array | string

Conversation turns as [{role, content}] objects, or a plain string. One of messages or content is required.

content

Optional · string

Alternative to messages: a single string to remember. Ignored when messages is present.

user_id

Optional · string

Scope key identifying the end user the memory belongs to. At least one of user_id, agent_id, run_id is required.

agent_id

Optional · string

Scope key identifying the agent. Useful for memories that belong to an assistant rather than a person.

run_id

Optional · string

Scope key identifying a single session or run. Use it for short-lived working memory.

metadata

Optional · object

Arbitrary JSON stored with each extracted memory and returned on reads. Useful for tags, sources, or app-side ids.

infer

Optional · boolean

When true, an LLM extracts dated atomic facts and reconciles them against existing memories. When false, the input is stored verbatim as a single memory.

Values: true (default), false

Add response

results

Required · array

One entry per memory the engine touched while processing the request.

results[].id

Optional · string

Memory id. Store it to fetch, update, or delete the memory later.

results[].memory

Optional · string

The extracted (or verbatim) memory text that was written.

results[].event

Optional · enum

What the engine did: ADD created a new fact, UPDATE rewrote an existing one, INVALIDATE closed a superseded fact's validity interval (it stays searchable), DELETE removed a contradicted fact.

Values: ADD, UPDATE, INVALIDATE, DELETE

curl --request POST "https://fishmem.com/v1/memories" \
  --header "Authorization: Bearer $FISHMEM_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "messages": [
      { "role": "user", "content": "I moved to Lisbon last month." },
      { "role": "assistant", "content": "Nice, how is Lisbon treating you?" }
    ],
    "user_id": "alice",
    "metadata": { "source": "support-chat" }
  }'
{
  "results": [
    {
      "id": "mem_8f2c1a",
      "memory": "Moved to Lisbon in May 2026",
      "event": "ADD"
    },
    {
      "id": "mem_31bd07",
      "memory": "Lives in Berlin",
      "event": "INVALIDATE"
    }
  ]
}
// Store a fact verbatim, skipping LLM extraction (1 credit instead of 2).
await fetch("https://fishmem.com/v1/memories", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.FISHMEM_API_KEY}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    content: "Prefers TypeScript examples over Python",
    user_id: "alice",
    infer: false
  })
});

Recall

Search memories

POST/v1/memories/search

Hybrid retrieval over a scope

Search runs four retrievers in parallel — vector similarity, keyword match, a temporal stream of recent facts, and Personalized-PageRank diffusion over the memory graph — then fuses them with weighted reciprocal-rank fusion into a single ranked list. Typical p50 latency is ~400–600ms, so call it once per agent turn rather than per token.

Request body

query

Required · string

Natural-language query. Phrase it the way your agent would ask, e.g. "what does the user eat?".

user_id

Optional · string

Scope key to search within. At least one of user_id, agent_id, run_id is required.

agent_id

Optional · string

Scope key to search agent-owned memories.

run_id

Optional · string

Scope key to search a single session's memories.

top_k

Optional · number

Maximum number of results to return after fusion and re-ranking.

Values: 1–50, default 10

Search result (memory object)

id

Required · string

Memory id.

memory

Required · string

The memory text.

memory_type

Optional · string

Engine classification of the memory, e.g. semantic or episodic.

importance

Optional · number

Engine-assigned importance weight used during retrieval.

user_id / agent_id / run_id

Optional · string | null

Scope keys the memory was written under.

metadata

Optional · object | null

The metadata object supplied at write time, if any.

created_at / updated_at

Optional · ISO-8601 string

Server timestamps for creation and the latest change.

event_date

Optional · ISO-8601 string | null

When the fact happened (extracted by the LLM), as opposed to when it was stored.

valid_from / valid_to

Optional · ISO-8601 string | null

Validity interval. An INVALIDATE event sets valid_to on the superseded fact so history stays queryable.

score

Optional · number

Fused relevance score. Present on search results only; higher is more relevant.

curl --request POST "https://fishmem.com/v1/memories/search" \
  --header "Authorization: Bearer $FISHMEM_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "query": "where does the user live?",
    "user_id": "alice",
    "top_k": 5
  }'
{
  "results": [
    {
      "id": "mem_8f2c1a",
      "memory": "Moved to Lisbon in May 2026",
      "memory_type": "semantic",
      "importance": 0.8,
      "user_id": "alice",
      "agent_id": null,
      "run_id": null,
      "metadata": { "source": "support-chat" },
      "created_at": "2026-06-01T09:12:00.000Z",
      "updated_at": "2026-06-01T09:12:00.000Z",
      "event_date": "2026-05-15T00:00:00.000Z",
      "valid_from": "2026-05-15T00:00:00.000Z",
      "valid_to": null,
      "score": 0.91
    }
  ]
}
const response = await fetch("https://fishmem.com/v1/memories/search", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.FISHMEM_API_KEY}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    query: "dietary restrictions",
    user_id: "alice",
    top_k: 10
  })
});

const { results } = await response.json();
const context = results
  .map((r) => `- ${r.memory} (score ${r.score.toFixed(2)})`)
  .join("\n");

CRUD

Manage memories

GET/v1/memories

List every memory in a scope without ranking. Returns{ results: [...] }of memory objects (same shape as search results, withoutscore).

Query parameters

user_id / agent_id / run_id

Required · string

Scope to list. At least one scope key is required as a query parameter.

limit

Optional · number

Maximum number of memories to return.

Values: 1–500, default 100

offset

Optional · number

Number of memories to skip, for pagination.

Values: default 0

curl --request GET "https://fishmem.com/v1/memories?user_id=alice&limit=50&offset=0" \
  --header "Authorization: Bearer $FISHMEM_API_KEY"
GET/v1/memories/{id}

Fetch a single memory object by id. Returns404 MEMORY_NOT_FOUNDif the id does not exist in your workspace.

curl --request GET "https://fishmem.com/v1/memories/mem_8f2c1a" \
  --header "Authorization: Bearer $FISHMEM_API_KEY"
PUT/v1/memories/{id}

Overwrite a memory's text and/or metadata. The change is recorded in the memory's history. Returns{ id, memory, event: "UPDATE" }.

Request body

memory

Optional · string

New memory text. text is accepted as an alias. At least one updatable field is required.

metadata

Optional · object

Replacement metadata object stored with the memory.

curl --request PUT "https://fishmem.com/v1/memories/mem_8f2c1a" \
  --header "Authorization: Bearer $FISHMEM_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "memory": "Moved to Lisbon in May 2026; works remotely",
    "metadata": { "source": "support-chat", "verified": true }
  }'
DELETE/v1/memories/{id}

Permanently delete one memory. Returns{ id, deleted: true }.

curl --request DELETE "https://fishmem.com/v1/memories/mem_8f2c1a" \
  --header "Authorization: Bearer $FISHMEM_API_KEY"
DELETE/v1/memories?user_id=...

Delete every memory in a scope — for example when a user exercises a data-deletion request. At least one ofuser_id,agent_id,run_idis required. Returns{ deleted: <count> }.

curl --request DELETE "https://fishmem.com/v1/memories?user_id=alice" \
  --header "Authorization: Bearer $FISHMEM_API_KEY"
GET/v1/memories/{id}/history

Read the full change log of a memory: every ADD, UPDATE, INVALIDATE, and DELETE event with the value before and after.

History response

results

Required · array

Change-log entries for the memory, oldest first.

results[].id

Optional · string

History entry id.

results[].memory_id

Optional · string

The memory this entry belongs to.

results[].event

Optional · enum

What changed at this point in the memory's life.

Values: ADD, UPDATE, INVALIDATE, DELETE

results[].previous_value

Optional · string | null

Memory text before the change. Null for ADD events.

results[].new_value

Optional · string | null

Memory text after the change. Null for DELETE events.

results[].created_at

Optional · ISO-8601 string

When the change happened.

{
  "results": [
    {
      "id": "hist_01",
      "memory_id": "mem_8f2c1a",
      "event": "ADD",
      "previous_value": null,
      "new_value": "Moved to Lisbon in May 2026",
      "created_at": "2026-06-01T09:12:00.000Z"
    },
    {
      "id": "hist_02",
      "memory_id": "mem_8f2c1a",
      "event": "UPDATE",
      "previous_value": "Moved to Lisbon in May 2026",
      "new_value": "Moved to Lisbon in May 2026; works remotely",
      "created_at": "2026-06-03T14:40:00.000Z"
    }
  ]
}

Billing

Credits

Writes and searches consume workspace credits; reads are free. Inferred adds cost more than raw adds because they run LLM extraction and reconciliation. Track your balance, plan, and included monthly credits in the dashboard under Billing.

POST /v1/memories (infer: true)2 credits per request
POST /v1/memories (infer: false)1 credit per request
POST /v1/memories/search1 credit per request
GET /v1/memories, GET /v1/memories/{id}, historyFree

An inferred add is charged once per request regardless of how many facts the engine extracts. List, get, and history calls are never metered.

Errors

Errors

Use the HTTP status to decide whether to retry, correct the request, or surface an account action. Error responses include a machine-readableerrorcode and a sanitizedmessage.

401INVALID_API_KEYAPI key missing, malformed, or revoked
400SCOPE_REQUIREDNone of user_id, agent_id, run_id was provided
400INVALID_QUERYSearch query missing or empty
400INVALID_MESSAGESmessages array or content string missing or empty
400INVALID_BODYRequest body is not valid JSON or has nothing to apply
404MEMORY_NOT_FOUNDMemory id does not exist in your workspace
500MEMORY_ENGINE_ERRORThe memory engine failed; safe to retry

Error response

code

Required · number

HTTP status code repeated in the body for log pipelines.

message

Required · string

Safe human-readable explanation that can be shown to operators.

error

Required · string

Machine-readable error code for branching and retries.

{
  "code": 400,
  "message": "One of user_id, agent_id, run_id is required",
  "error": "SCOPE_REQUIRED"
}