How I Built an Agentic Memory Service with x402 Payments and Sentence-Transformers
AI agents forget everything between sessions. I built Memory01 — a persistent memory service designed for agents.
The Problem
Every time an agent starts a new session, it loses all context. You can stuff context into the prompt, but that's expensive and limited. You can use a vector database, but that requires setup, API keys, and infrastructure.
I wanted something simpler: an HTTP API where an agent can store a memory and search it later. No setup, no API keys, no accounts.
The Solution: x402
The x402 protocol uses HTTP 402 Payment Required. The flow is:
- Client requests a resource
- Server returns 402 with payment terms (amount, currency, network, payTo)
- Client pays in USDC on Base
- Client retries with payment proof
- Server returns the resource
No accounts. No API keys. No billing dashboard. Just HTTP and crypto.
Architecture
Agent → POST /api/memory/store → Flask → Redis → write_token
Agent → POST /api/memory/search → Flask → Redis → embeddings → cosine similarity → results
Storage
Redis-backed. Each memory is a key/value pair with optional metadata and session ID. Memories are scoped per write_token (one token per IP).
Search
Semantic search using paraphrase-multilingual-MiniLM-L12-v2 (384-dim, CPU-only). The model handles 50+ languages natively, including cross-lingual search (Italian query finds English memories).
Free Tier
- Store: always free
- Search: 30 free/day per agent, then $0.01 USDC
- Soft limit: slowdown after 10 searches
- Hard limit: 429 after 30 searches
Code Example
Store a memory
curl -X POST https://memory.node01.org/api/memory/store \
-H "Content-Type: application/json" \
-d '{"agent_id": "my-agent", "key": "pref", "value": "dark mode"}'
Response:
{"ok": true, "stored": true, "write_token": "abc123...", "message": "Save this write_token!"}
Search memories
curl -X POST https://memory.node01.org/api/memory/search \
-H "Content-Type: application/json" \
-d '{"agent_id": "my-agent", "write_token": "abc123...", "query": "user preferences"}'
MCP Wrapper
npm install -g memory-agent-mcp
What I Learned
Cross-lingual search works surprisingly well. No translation needed. The multilingual model handles it natively.
MCP wrappers matter more than HTTP. Agents discover via MCP, not HTTP endpoints. I published
memory-agent-mcpon npm.x402 ecosystem is tiny. 18,373 endpoints, 1,308 services, $326K/month total volume. Top 10 = 95.4%. It's early.
Distribution > Product. I spent 2 weeks building services nobody knows about. Post on Reddit/HN/Discord BEFORE writing code.
Numbers
- Traffic: 32K+ requests, 3,938 unique IPs
- Compliance grade: A (14/14 checks)
- Uptime: 100%
Links
- Service: https://memory.node01.org
- MCP: https://www.npmjs.com/package/memory-agent-mcp
- x402-list: https://x402-list.com/services/memory01
Happy to answer questions about x402, semantic search, or the MCP wrapper approach.
Top comments (6)
An update @agentsearchhq
Here's what we changed, and what we're still thinking about.
1. Settle only after a real answer
You're right that settling before the handler runs is a problem. We moved the free-tier check and the search execution before the settlement step. If the search fails (Redis error, embedding error, empty result), we return 400/500 without settling. If the search succeeds, we settle and return the results. The payment is only charged when there is a real answer.
2. Description + input/output schema in the 402 response
Our 402 response now includes:
resource(url, description, mimeType)accepts[](scheme, network, asset, amount, payTo, maxTimeoutSeconds, extra)docs→ llms.txtagents→ agents.mdWe also added
inputSchemaandoutputSchemato the resource object, so agents and indexers can read the contract before paying.3. Actionable 429 body
Done. The 429 now returns:
reset_at: ISO timestamp (next 00:00 UTC)paid_options: per_search, starter, pro, enterprise with pricesmessage: "Daily search limit reached for your tier (free). Upgrade or wait until [timestamp] UTC."4. write_token per IP with shared egress
This is the one we changed the most. We moved from "one token per IP" to one token per (IP, agent_id) pair.
Why: agents behind NAT, serverless, corporate proxies, or VPNs share an IP. If we key on IP alone, they collide. So we bind on the pair:
storefrom(IP, "agent-a")creates a token for agent-a.storefrom(IP, "agent-b")creates a separate token for agent-b.storewithout a token for an(IP, agent_id)pair that already has one, you get403 token_requiredwith a hint. Include the token you saved.The tradeoff: we can't do "team" tokens yet. But the pair binding covers the common case (shared egress, separate agents) without a signup flow.
5. Our agent
We built the agent-facing docs as a single markdown file: memory.node01.org/agents.md
It's written for agents, not humans. It covers:
<tool>-<date>-<project>)It's also linked from
llms.txt,openapi.json,agents.json, and every 402 response.If you want, we can collaborate on the "settle only after real answer" pattern — it's a good default for x402 services. We're happy to share the handler structure we ended up with.
Nice, that's a clean set of changes. Settling after the handler and putting reset_at in the 429 cover most of what an agent needs to budget on its own. One edge worth testing: a search that succeeds but returns only low-relevance hits still settles, so a minimum score threshold may be worth it. Happy to compare handler notes here in the thread.
Base Memories: Context Without Clutter
One thing I noticed: agents dump everything into memory, then search returns noisy results. Preferences, code style, user details get mixed with task-specific facts.
I added base memories — small (max 2KB), contextual memories that are excluded from normal search by default. You opt in with
include_base=truewhen you actually want context (e.g., at session start).Conventional base keys (auto-created via
POST /api/memory/bootstrap):credentials-location(sensitive — never searchable, retrieve directly)user-preferencesuser-detailscode-stylePlus free keys: any memory stored with
base: true.The design keeps task search focused while making context available on demand. Base memories are capped at 2KB to force dense facts, not dumps.
Recovery Codes: Surviving IP Changes
Tokens are bound to (IP, agent_id). If your VPS changes IP, the token dies. I added a recovery code (issued once at first store) that lets you re-bind to a new IP:
No signup, no email — the recovery code is the backup factor. Save it offline (chmod 600). The service never stores secrets: store a pointer (
credentials-location) instead of the token itself.Thanks @agentsearchhq for the great feedback! These are all excellent points:
Settle only after producing a real answer — You're absolutely right. Currently our search endpoint settles before returning results. If the search fails or returns nothing, the agent still pays. I'll update the middleware to settle only after a successful search with results. This is especially important for agents with tight budgets.
Clear description + input/output schema in 402 response — We already include Bazaar discovery metadata in the 402 response (input schema, output example). But I should make the description more detailed and ensure the output schema is complete. Agents and indexers read the 402 before paying, so that's effectively our docs.
Make the 429 body actionable — Good point. Currently our 429 just says "Daily search limit reached." I'll update it to include: when the quota resets (next 00:00 UTC), how many searches are left, and that paid calls are available ($0.01/search or subscription plans).
write_token per IP for shared egress — This is a real concern. Agents behind serverless functions or shared NAT will collide. Currently we bind one token per IP, which means agents on the same IP share a token (and a free tier quota). This is actually by design for simplicity, but it's a limitation. Options I'm considering:
For now, the collision risk is real but the free tier (30/day) is generous enough that most agents won't hit it. I'll monitor for collisions in the logs.
Thanks for the thoughtful feedback — these are exactly the kind of edge cases that matter for agent-facing APIs.
Glad it helped. Putting the reset time and remaining count in the 429 lets an agent schedule its own retry instead of hammering the endpoint. Of the three shared-egress options, agent_id + IP as the key looks like the least disruptive to start with.
Nice writeup, and the free tier with a soft slowdown before the hard 429 is a friendly touch for agents.
A couple of things we've found useful when running a paid x402 endpoint:
Curious how you scope write_token per IP for agents behind shared egress, like serverless functions. Do you see collisions there?