mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-09-07 08:26:15 +00:00
71 '<!-- CONFIRM -->' review markers across 19 files used HTML comment
syntax, which MDX cannot parse. One parse error breaks the whole
production build — this is why the deployed site 404'd on every page
while local dev limped along. All converted to {/* */} (code-fence
contents untouched). Also: remove the legacy source-'/' redirect,
replace the phantom architecture-diagram image with an ASCII diagram
until the real one lands.
Verified locally: mintlify broken-links parses all pages clean (one
known-good /api-reference tab link that 307s at runtime), and /,
/overview, /concepts/architecture, /quickstart, /patterns/*,
/versioning all render 200 with content.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
233 lines
10 KiB
Text
233 lines
10 KiB
Text
---
|
|
title: "Usage & billing"
|
|
description: "What counts against your quota, what's free, and how to track spend per API key."
|
|
---
|
|
|
|
You're billed for what supermemory processes, not what it stores. Tokens are counted once, at ingestion — searches, profile reads, and memory injection don't draw them down.
|
|
|
|
Start by checking where you stand:
|
|
|
|
<CodeGroup>
|
|
|
|
```typescript TypeScript
|
|
const res = await fetch("https://api.supermemory.ai/v3/auth/billing/usage", {
|
|
headers: { Authorization: `Bearer ${process.env.SUPERMEMORY_API_KEY}` },
|
|
});
|
|
|
|
const { items, periodStart, periodEnd } = await res.json();
|
|
```
|
|
|
|
```python Python
|
|
import requests
|
|
|
|
res = requests.get(
|
|
"https://api.supermemory.ai/v3/auth/billing/usage",
|
|
headers={"Authorization": f"Bearer {SUPERMEMORY_API_KEY}"},
|
|
)
|
|
|
|
usage = res.json()
|
|
```
|
|
|
|
```bash cURL
|
|
curl "https://api.supermemory.ai/v3/auth/billing/usage" \
|
|
-H "Authorization: Bearer $SUPERMEMORY_API_KEY"
|
|
```
|
|
|
|
</CodeGroup>
|
|
|
|
You get back each meter with its usage and limit, plus your current billing period:
|
|
|
|
```json
|
|
{
|
|
"items": [
|
|
{ "name": "sm tokens text", "used": 184203, "limit": 1000000, "unit": "tokens" },
|
|
{ "name": "sm search queries", "used": 4210, "limit": 100000, "unit": "queries" }
|
|
],
|
|
"periodStart": "2026-07-01T00:00:00.000Z",
|
|
"periodEnd": "2026-08-01T00:00:00.000Z"
|
|
}
|
|
```
|
|
|
|
{/* CONFIRM: billing usage response shape and exact meter names */}
|
|
|
|
One thing to know up front: scoped API keys can **not** read billing endpoints — they return a 403. Use an unscoped key, or check the billing page in the console instead. {/* CONFIRM: scoped-key 403 on billing endpoints */}
|
|
|
|
## What "tokens processed" counts
|
|
|
|
Every document you add — via the API, a connector, MCP, or any other door — goes through the ingestion pipeline. The tokens of the **extracted content** are what's metered. Not your raw upload size, not the embeddings, not the memories derived from it: the token count of the text supermemory pulled out of your document.
|
|
|
|
Two meters exist:
|
|
|
|
- **Text tokens** — plain text, tweets, markdown.
|
|
- **Rich content tokens** — PDFs, images, files, web pages. Anything that needs extraction before it's text.
|
|
|
|
Rich content costs more per token than plain text, because extraction does more work. {/* CONFIRM: text vs rich meter split and relative pricing */} If you can send markdown instead of a PDF of the same content, send markdown — it's also what the [ingestion guide](/patterns/ingestion) recommends for quality reasons.
|
|
|
|
Updates are billed on the **delta**. When you update a document (or re-add one with the same `customId`), you're charged only for the token count *increase* over what that document already cost. Re-processing unchanged content bills zero. So appending a session to an existing conversation document charges you for the new turns, not the whole history again — this is why [ingesting full conversations under one `customId`](/patterns/ingestion) is cheaper than adding every turn as its own document. {/* CONFIRM: delta billing on updates and customId re-adds */}
|
|
|
|
And the reads are not on this meter at all:
|
|
|
|
- **Search** is metered per query, not per token — and priced low enough that it's effectively free at any realistic volume. Ingestion is where your money goes.
|
|
- **Profile reads** don't consume tokens. A `client.profile({ containerTag })` call isn't metered; add a `q` and it counts as one search query.
|
|
- **Memory injection** — the AI SDK wrapper putting a profile or search results into your prompt — is a profile/search read under the hood, so it follows the same rules. It never consumes ingestion tokens. {/* CONFIRM: injection billing — verified in code (read paths only hit the search-query meter), confirm this is the publishable statement */}
|
|
- **Storage is free.** A document you ingested in January costs nothing to keep in July.
|
|
|
|
## Deleting documents does not restore quota
|
|
|
|
The meter counts processing, and the processing already happened. Deleting a document removes its content, chunks, and derived memories — but the tokens it consumed stay consumed. Your quota is a record of work done, not a measure of what's currently stored.
|
|
|
|
Your quota refreshes at the start of each billing period. Unused quota doesn't roll over. {/* CONFIRM: monthly refresh cadence + no-rollover on all plans */}
|
|
|
|
If you're near the limit, deleting old documents won't buy you headroom — upgrading or waiting for the reset will.
|
|
|
|
## Model your costs
|
|
|
|
The mental math is short:
|
|
|
|
1. **Ingestion is the cost driver.** Estimate the tokens in what you'll send — that's most of your bill.
|
|
2. **Search is ~free.** Query as much as you want; per-query pricing is negligible next to ingestion.
|
|
3. **Profiles are cheap to use.** A profile fits in roughly a 1k-token budget and its shape is stable between updates, so it's prompt-cache-friendly on the LLM side too.
|
|
4. **Send conversations, not turns.** One document per session with a `customId` beats one document per message — better memories *and* delta billing.
|
|
|
|
## Cut costs with `taskType: "superrag"`
|
|
|
|
If a set of documents only needs to be searchable — you'll never want derived memories, a graph, or profile contributions from it — ingest it with `taskType: "superrag"`. That runs the retrieval-only pipeline (extract, chunk, embed) and skips memory derivation, at about 5x cheaper per token than the default `"memory"` task type. {/* CONFIRM: taskType param name and "superrag"/"memory" values */}
|
|
|
|
A support-docs corpus is the typical case:
|
|
|
|
<CodeGroup>
|
|
|
|
```typescript TypeScript
|
|
const res = await fetch("https://api.supermemory.ai/v3/documents", {
|
|
method: "POST",
|
|
headers: {
|
|
Authorization: `Bearer ${process.env.SUPERMEMORY_API_KEY}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
content: "https://help.acme.dev/articles/refund-policy",
|
|
containerTag: "acme_help_center",
|
|
taskType: "superrag",
|
|
}),
|
|
});
|
|
```
|
|
|
|
```python Python
|
|
import requests
|
|
|
|
requests.post(
|
|
"https://api.supermemory.ai/v3/documents",
|
|
headers={"Authorization": f"Bearer {SUPERMEMORY_API_KEY}"},
|
|
json={
|
|
"content": "https://help.acme.dev/articles/refund-policy",
|
|
"containerTag": "acme_help_center",
|
|
"taskType": "superrag",
|
|
},
|
|
)
|
|
```
|
|
|
|
```bash cURL
|
|
curl -X POST "https://api.supermemory.ai/v3/documents" \
|
|
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"content": "https://help.acme.dev/articles/refund-policy",
|
|
"containerTag": "acme_help_center",
|
|
"taskType": "superrag"
|
|
}'
|
|
```
|
|
|
|
</CodeGroup>
|
|
|
|
Use `"memory"` (the default) for anything about your users — conversations, preferences, facts you want the engine to reason over. Use `"superrag"` for reference material you only retrieve. The examples above use raw `POST /v3/documents` because the TS SDK typings don't include `taskType` yet.
|
|
|
|
<Note>
|
|
Documents ingested with `"superrag"` show up in [document search](/search) but don't produce memories, so they won't appear in memory search results or [profiles](/concepts/user-profiles).
|
|
</Note>
|
|
|
|
## Track usage per API key
|
|
|
|
If you run one key per environment — or per tenant — you can see exactly which key spent what. `GET /v3/analytics/usage` breaks usage down by key, including tokens:
|
|
|
|
<CodeGroup>
|
|
|
|
```typescript TypeScript
|
|
const res = await fetch(
|
|
"https://api.supermemory.ai/v3/analytics/usage?period=30d",
|
|
{ headers: { Authorization: `Bearer ${process.env.SUPERMEMORY_API_KEY}` } },
|
|
);
|
|
|
|
const { byKey } = await res.json();
|
|
```
|
|
|
|
```python Python
|
|
import requests
|
|
|
|
res = requests.get(
|
|
"https://api.supermemory.ai/v3/analytics/usage",
|
|
params={"period": "30d"},
|
|
headers={"Authorization": f"Bearer {SUPERMEMORY_API_KEY}"},
|
|
)
|
|
|
|
by_key = res.json()["byKey"]
|
|
```
|
|
|
|
```bash cURL
|
|
curl "https://api.supermemory.ai/v3/analytics/usage?period=30d" \
|
|
-H "Authorization: Bearer $SUPERMEMORY_API_KEY"
|
|
```
|
|
|
|
</CodeGroup>
|
|
|
|
Each entry in `byKey` carries the token total for that key:
|
|
|
|
```json
|
|
{
|
|
"byKey": [
|
|
{
|
|
"keyId": "key_prod_4f8a",
|
|
"keyName": "Production API",
|
|
"count": 23410,
|
|
"tokensUsed": 1284203,
|
|
"avgDuration": 98.7,
|
|
"lastUsed": "2026-07-15T14:35:00Z"
|
|
}
|
|
],
|
|
"usage": [
|
|
{ "type": "add", "count": 1523, "avgDuration": 245.5 },
|
|
{ "type": "search", "count": 3421, "avgDuration": 89.2 }
|
|
]
|
|
}
|
|
```
|
|
|
|
The endpoint also accepts `from`/`to` (ISO 8601) instead of `period`, and paginates with `page` and `limit`. {/* CONFIRM: analytics usage response fields and query params */} The same family has `/v3/analytics/errors` and `/v3/analytics/logs` for error breakdowns and request-level logs.
|
|
|
|
## Handle running out
|
|
|
|
When a meter is exhausted, writes start returning `402` with a body that names the meter:
|
|
|
|
```json
|
|
{
|
|
"error": "Text tokens limit reached",
|
|
"details": "You've run out of credits. Top up to continue."
|
|
}
|
|
```
|
|
|
|
{/* CONFIRM: 402 status and exact body shape */}
|
|
|
|
Catch the `402` in your ingestion path and queue the writes — reads keep working, so your app degrades to "remembers everything up to now" rather than breaking. Whether usage past the included quota bills as overage or hard-blocks depends on your plan and its overage setting. {/* CONFIRM: overage defaults and availability by plan */}
|
|
|
|
## Manage invoices, downgrades, and cancellation
|
|
|
|
Invoices, payment methods, plan changes, and cancellation all live in the console's billing settings. {/* CONFIRM: exact console navigation path */} Only org admins can manage billing.
|
|
|
|
When you downgrade or cancel, your data is not deleted — you keep read access, and ingestion is governed by the lower plan's quota from the next billing period. {/* CONFIRM: plan-downgrade data behavior — verify data retention and any limits enforcement on existing over-quota data */}
|
|
|
|
That's the whole meter: pay when supermemory processes, read for ~free, and delete for hygiene — not refunds.
|
|
|
|
## Where next
|
|
|
|
- [Ingestion best practices](/patterns/ingestion) — the patterns that make ingestion cheaper *and* produce better memories
|
|
- [Errors and limits](/errors-and-limits) — rate limits, the 429 shape, and backoff
|
|
- [User profiles](/concepts/user-profiles) — what that ~1k-token budget buys you
|
|
- [Security](/trust/security) — scoped keys, deletion mechanics, and compliance
|