Metadata-Version: 2.5
Name: supermemory
Version: 5.0.0rc2
Summary: The official Python library for the Supermemory API
Project-URL: Homepage, https://supermemory.ai
Project-URL: Documentation, https://docs.supermemory.ai
Project-URL: Repository, https://github.com/supermemoryai/python-sdk
Author-email: Supermemory <dhravya@supermemory.com>
License-Expression: Apache-2.0
License-File: LICENSE
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx>=0.21.2
Requires-Dist: pydantic-core>=2.18.2
Requires-Dist: pydantic>=1.9.2
Requires-Dist: typing-extensions>=4.0.0
Provides-Extra: aiohttp
Requires-Dist: httpx-aiohttp>=0.1.8; extra == 'aiohttp'
Description-Content-Type: text/markdown

# Supermemory Python SDK

[![PyPI version](https://img.shields.io/pypi/v/supermemory.svg)](https://pypi.org/project/supermemory/)

The official Python library for the [Supermemory](https://supermemory.ai) v5 API, with sync and async clients built on [httpx](https://github.com/encode/httpx).

> **Upgrading from 3.x?** v5 moves to the namespace-first v5 API. See [MIGRATION.md](MIGRATION.md).

## Installation

```sh
pip install --pre supermemory   # 5.0 release candidates
```

## Usage

```python
from supermemory import Supermemory

client = Supermemory()  # reads SUPERMEMORY_API_KEY; or Supermemory(api_key="...")

client.add("user_alex", content="Alex prefers morning meetings.", id="pref-1")

response = client.search("user_alex", query="when does alex like to meet?", limit=5)
for result in response.results:
    print(result)
```

Every content operation is scoped to a **namespace** (a user ID, project ID, or anything you want to isolate memories by), passed as the first argument.

### Async

```python
import asyncio
from supermemory import AsyncSupermemory

async_client = AsyncSupermemory()


async def main() -> None:
    profile, results = await asyncio.gather(
        async_client.profile("user_alex"),
        async_client.search("user_alex", query="upcoming meetings"),
    )


asyncio.run(main())
```

Install `supermemory[aiohttp]` to use aiohttp as the async transport.

## API surface

| Call | Endpoint |
|---|---|
| `client.add(namespace, content=...)` | `POST /ns/{namespace}/document` |
| `client.search(namespace, query=...)` | `POST /ns/{namespace}/search` |
| `client.profile(namespace)` | `POST /ns/{namespace}/profile` |
| `client.list(namespace, "documents" \| "chunks" \| "memories")` | `POST /ns/{namespace}/list/{type}` |
| `client.documents.{get, update, delete, batch_add, upload_file, replace_with_file, update_file}` | `/ns/{namespace}/document…` |
| `client.memories.{forget, forget_matching}` | `/ns/{namespace}/memories…` |
| `client.profiles.{get_buckets, set_buckets, delete_buckets}` | `/ns/{namespace}/profile/buckets` |
| `client.connectors.{list_all, list, create, get, update, delete, sync}` | `/connectors`, `/ns/{namespace}/connectors…` |
| `client.namespaces.{list, get, update, delete}` | `/ns`, `/ns/{namespace}` |
| `client.organization.{get, update}` | `/organization` |

Full parameter reference: [reference.md](reference.md).

## Filtering

Filters are plain dicts (typed as `FilterExpressionParams`), combinable with `and` / `or`:

```python
response = client.search(
    "user_alex",
    query="launch plans",
    filter={
        "operator": "and",
        "operands": [
            {"field": "source", "operator": "eq", "value": "slack"},
            {"field": "priority", "operator": "gte", "value": 3},
        ],
    },
)
```

The same filter shape works for `client.list(...)` and `client.profile(...)`.

## Listing

```python
page = client.list("user_alex", "documents", page=1, limit=50, sort="createdAt", order="desc")
for document in page.documents or []:
    print(document.id, document.title)
print(page.pagination)
```

## File uploads

```python
with open("notes.pdf", "rb") as f:
    client.documents.upload_file("user_alex", file=f)
```

## Errors

Same exception classes as 3.x. Non-2xx responses raise a subclass of `supermemory.APIStatusError` with `status_code`, `body`, `message`, `request`, and `response`. Network failures raise `APIConnectionError` (`APITimeoutError` for timeouts):

```python
import supermemory
from supermemory import Supermemory

client = Supermemory()

try:
    client.documents.get("user_alex", "missing-id")
except supermemory.NotFoundError:
    ...
except supermemory.RateLimitError as e:
    print(e.response.headers.get("retry-after"))
except supermemory.APIStatusError as e:
    print(e.status_code, e.body)
except supermemory.APIConnectionError:
    ...
```

| Status | Error |
|---|---|
| 400 | `BadRequestError` |
| 401 | `AuthenticationError` |
| 403 | `PermissionDeniedError` |
| 404 | `NotFoundError` |
| 409 | `ConflictError` |
| 422 | `UnprocessableEntityError` |
| 429 | `RateLimitError` |
| >=500 | `InternalServerError` |

## Retries, timeouts, and per-request options

Connection errors, 408, 429, and 5xx responses are retried twice with exponential backoff, and requests time out after 60 seconds (5 seconds to connect). All of these can be set on the client, or for one request with `with_options`:

```python
import httpx

client = Supermemory(max_retries=5, timeout=20.0, default_headers={"X-App": "my-app"})

client.with_options(max_retries=0, timeout=httpx.Timeout(5.0)).search("user_alex", query="...")
```

Every method also takes `extra_headers`, `extra_query`, `extra_body`, and `timeout`:

```python
client.search("user_alex", query="...", extra_headers={"X-Trace": "1"}, timeout=5)
```

The client reads `SUPERMEMORY_API_KEY`, `SUPERMEMORY_BASE_URL`, and `SUPERMEMORY_CUSTOM_HEADERS` from the environment.

## Raw responses

```python
response = client.with_raw_response.search("user_alex", query="...")
print(response.headers.get("x-request-id"))
results = response.parse()  # SearchResponse
```

Response models have `.to_dict()` and `.to_json()`.

## Development

The SDK is generated from the live v5 OpenAPI spec with [Fern](https://buildwithfern.com)'s open-source Python generator, run locally in Docker. No hosted codegen service is involved. Don't edit `src/supermemory` by hand; CI fails if it doesn't match a fresh generation.

| To change | Edit |
|---|---|
| Method names, grouping, parameter names, hidden endpoints | `fern/overlay.yaml` (`x-fern-sdk-group-name`, `x-fern-sdk-method-name`, `x-fern-parameter-name`, `x-fern-ignore`) |
| Type names (`Document`, `Memory`, `Connector`, …) | `fern/schemas.yaml` |
| Generator options | `fern/generators.yml` |
| Client conventions (options, errors, per-call options) | `custom/supermemory/_compat.py` |
| Small fixes to Fern's generated core | `custom/patches/*.patch` |

`scripts/generate` fetches the spec, names its schemas, runs Fern, then layers `custom/` on top.

```sh
scripts/generate              # fetch /v5/openapi and regenerate (needs Docker, bun, uv)
SKIP_FETCH=1 scripts/generate # regenerate from the committed fern/openapi.json
uv run --group dev pytest     # offline tests, including every snippet in README.md and MIGRATION.md
SUPERMEMORY_LIVE_API_KEY=... uv run --group dev pytest tests/live   # against the real API
```

Runnable examples are in [`examples/`](examples).

### Releases

- When the API deploy sends `openapi-updated` (or on the daily backstop run), **Generate SDK** regenerates. If the SDK changed, it bumps the version and opens a PR with the tests already run.
- Merging a PR that changes the version in `pyproject.toml` publishes it to PyPI and creates a GitHub release.
- To cut a specific version, run **Generate SDK** manually with `version` set.
