Language-Agnostic Patterns
The following patterns hold no matter which client (or raw HTTP) you use. Header names, field names, status codes, and limits are the same across Python, Node, and direct cURL. Learn them once, apply them everywhere.
The headers every request carries
Both clients send the same headers. If you skip the SDKs and call the API directly, you must set these yourself.
| Header | Required | What it does |
|---|---|---|
X-API-Key | Yes | Your API key. Always sent, even by SDK methods that look read-only. |
Content-Type | On bodies | Always application/json. |
Accept | Recommended | application/json; the API never returns anything else. |
X-Project-ID | Optional | Scopes the call to a specific project. Both clients accept it on the constructor. |
X-End-User-ID | Required for API-key auth | Identifies which of your end users the request belongs to. The server returns 400 MISSING_END_USER_ID when an API-key caller omits it. (Ignored when authenticating via a dashboard session.) |
Reading the request id from a response
Every response carries a server-side request id in theX-Request-ID header. The clients copy that value onto every error they raise as request_id / requestId. When you file a support ticket, paste it.
curl -i -X POST "$API/memory/add" \-H "X-API-Key: $KEY" -H "Content-Type: application/json" \-d '{"text":"hello"}' | grep -i x-request-id
Idempotent retries
The memory routes are designed so that retrying a recently-failedadd with the exact same text does not create a duplicate — the deduplicator on the server collapses semantically identical inserts. bulkAdd takes an explicit deduplicate flag (default true) for the same reason.
skipped result instead of a duplicate row.A retry policy that works for both clients
Build retry logic against the typed errors. The same shape works in Python and TypeScript.
- Retry up to
3times onRateLimitErrorandServerError. - For
RateLimitError, sleep for the value the error reports in seconds, then retry. - For
ServerError, exponential backoff (e.g. 0.5s, 1s, 2s) with jitter. - Never retry on
AuthError,ValidationError, orNotFoundError— those are caller bugs.
Sizes, limits, and pagination
| Surface | Limit | What happens past it |
|---|---|---|
bulkAdd items | 50 per call | Server rejects with HTTP 400; SDKs raise ValidationError before the call. |
query k | 1–50 (default 5) | Values outside [1, 50] are rejected with HTTP 422 ValidationError. There is no silent clamping — send a number in range. |
compose max_tokens | Caller-defined | When the budget is exhausted the response sets truncated:true so you know to widen the window or trim memories. |
forget ids | Server-bounded | Pass them in batches; the response returns the ids that were actually deleted. |
Timeouts you should set yourself
Both clients default to a 30-second timeout per call. For interactive surfaces you want a shorter timeout; for background workers you may want longer. The constructors accept this as timeout (Python) and timeoutMs (Node).
MemorySyncClient(api_key="...", base_url="...", timeout=10.0) # seconds
new MemorySyncClient({ apiKey: "...", baseUrl: "...", timeoutMs: 10_000 });
Environment discipline
- Never inline an API key in source. Read it from
MEMORYSYNC_API_KEYor your secret manager. - Set
baseUrlper environment; use a staging key against a staging base URL and a live key against the production base URL. - If you scope by project on the dashboard, set
projectIdin the client constructor — do not pass it in every method call.