# Quickstart

> Sign up, write documents, then search, filter, count, query with SQL and search by vector.

This takes a few minutes. You need `curl` (and `jq` for the first step), or Python 3 or Node.

## 1. Sign up

Signing up makes your account and its first tenant, and answers with your first API key. That
key has full access (scope `admin`) and is shown only once: keep it somewhere safe. You can also
sign up on the [sign-up page](https://skimmerdb.com/signup) and copy a key from the
[dashboard](https://skimmerdb.com/dashboard).

```sh title="curl"
B=https://api.skimmerdb.com/v1
KEY=$(curl -s $B/signup -d '{"email": "ada@example.com", "name": "Ada", "password": "correct horse"}' | jq -r .key)
echo $KEY    # tb_...
```

```python title="Python"
# pip install skimmerdb
from skimmerdb import SkimmerDB

db = SkimmerDB.signup("ada@example.com", "correct horse")
print(db.key)   # tb_... : keep it; next time SkimmerDB("tb_...") or SKIMMERDB_KEY
```

```ts title="TypeScript"
// npm install skimmerdb
import { SkimmerDB } from "skimmerdb";

const db = await SkimmerDB.signup("ada@example.com", "correct horse");
console.log(db.key); // tb_... : keep it; next time new SkimmerDB("tb_...") or SKIMMERDB_KEY
```

> **Tip** Give apps and agents narrower keys than this one: `POST /v1/keys` makes a `write` key by default, and a key can be `read`-only or limited to some namespaces. See [Keys, scopes and tenants](/guides/keys).

## 2. Write documents

A document is an `id`, the `text` you search, and `meta` you filter and group by. Writing to a
namespace that doesn't exist makes it, and the answer says `"created": true`.

```sh title="curl"
curl -s $B/namespaces/support/docs -H "Authorization: Bearer $KEY" -d '{
  "wait": true,
  "docs": [
    {"id": "t-1", "text": "Refund requested for order ACME-1042", "meta": {"status": "open", "priority": 3}},
    {"id": "t-2", "text": "Login fails with error E401 on mobile", "meta": {"status": "closed", "priority": 1}}
  ]}'
```

```python title="Python"
ns = db.namespace("support")
ns.write([
    {"id": "t-1", "text": "Refund requested for order ACME-1042", "meta": {"status": "open", "priority": 3}},
    {"id": "t-2", "text": "Login fails with error E401 on mobile", "meta": {"status": "closed", "priority": 1}},
], wait=True)
```

```ts title="TypeScript"
const ns = db.namespace("support");
await ns.write([
  { id: "t-1", text: "Refund requested for order ACME-1042", meta: { status: "open", priority: 3 } },
  { id: "t-2", text: "Login fails with error E401 on mobile", meta: { status: "closed", priority: 1 } },
], { wait: true });
```

The answer: `{"written": 2, "deleted": 0, "version": 1, "stored_bytes": ..., "created": true}`.
A write is durable when it answers. `wait` makes it answer only once the documents are
searchable too; without it they're searchable within about a second.

## 3. Search

Plain text is matched literally, ignoring case. Hits come newest first, with the matched text
and the line around it.

```sh title="curl"
curl -s $B/namespaces/support/search -H "Authorization: Bearer $KEY" -d '{"q": "acme-1042"}'
```

```python title="Python"
for hit in ns.search("acme-1042").hits:
    print(hit.id, hit.match, hit.line)
```

```ts title="TypeScript"
const { hits } = await ns.search("acme-1042");
hits.forEach((h) => console.log(h.id, h.match, h.line));
```

```json title="answer"
{"namespace": "support", "hits": [{"id": "t-1", "match": "ACME-1042", "line": "Refund requested for order ACME-1042",
  "meta": {"priority": 3, "status": "open"}, "written_at": "2026-10-05T01:30:12Z"}], "next": "", "docs": 2,
  "took_ms": 0.41, "partial": false, "temperature": "warm", "store_gets": 0, "store_bytes": 0}
```

## 4. Filter, regex and count

Filters match `meta`: equal, one of, a numeric range, not equal. Regex is opt-in (RE2). Counts
are exact.

```sh title="curl"
curl -s $B/namespaces/support/search -H "Authorization: Bearer $KEY" \
  -d '{"q": "E[0-9]{3}", "regex": true, "filter": {"priority": {"gte": 1, "lt": 4}}}'
curl -s $B/namespaces/support/count -H "Authorization: Bearer $KEY" -d '{"q": "", "filter": {"status": "open"}}'
```

```python title="Python"
ns.search("E[0-9]{3}", regex=True, filter={"priority": {"gte": 1, "lt": 4}})
ns.count(filter={"status": "open"})   # 1
```

```ts title="TypeScript"
await ns.search("E[0-9]{3}", { regex: true, filter: { priority: { gte: 1, lt: 4 } } });
await ns.count("", { filter: { status: "open" } }); // 1
```

More in [Search and filters](/guides/search) and the [filter reference](/reference/filters).

## 5. SQL

Every top-level `meta` key is a column, and `id` is one too.

```sh title="curl"
curl -s $B/namespaces/support/sql -H "Authorization: Bearer $KEY" \
  -d '{"sql": "SELECT status, COUNT(*) AS n, AVG(priority) AS p FROM docs GROUP BY status ORDER BY n DESC"}'
```

```python title="Python"
r = ns.sql("SELECT status, COUNT(*) AS n, AVG(priority) AS p FROM docs GROUP BY status ORDER BY n DESC")
print(r.columns, r.rows)
```

```ts title="TypeScript"
const r = await ns.sql("SELECT status, COUNT(*) AS n, AVG(priority) AS p FROM docs GROUP BY status ORDER BY n DESC");
console.log(r.columns, r.rows);
```

See [Analytics with SQL](/guides/analytics) and the [SQL reference](/reference/sql).

## 6. Vectors

Give documents a `vector` (your embeddings) and search with one. The first write with vectors
makes the namespace with their length and cosine similarity; `q` and `filter` narrow the
candidates (hybrid search).

```sh title="curl"
curl -s $B/namespaces/notes/docs -H "Authorization: Bearer $KEY" -d '{"wait": true, "docs": [
  {"id": "n-1", "text": "Webhook timeouts since Tuesday", "vector": [0.1, 0.0, 0.98], "meta": {"team": "api"}},
  {"id": "n-2", "text": "New logo for the landing page", "vector": [0.9, 0.4, 0.1], "meta": {"team": "design"}}]}'
curl -s $B/namespaces/notes/search -H "Authorization: Bearer $KEY" \
  -d '{"vector": [0.1, 0.0, 0.98], "limit": 10, "filter": {"team": "api"}}'
```

```python title="Python"
notes = db.namespace("notes")
notes.write([
    {"id": "n-1", "text": "Webhook timeouts since Tuesday", "vector": [0.1, 0.0, 0.98], "meta": {"team": "api"}},
    {"id": "n-2", "text": "New logo for the landing page", "vector": [0.9, 0.4, 0.1], "meta": {"team": "design"}},
], wait=True)
notes.search(vector=[0.1, 0.0, 0.98], limit=10, filter={"team": "api"})
```

```ts title="TypeScript"
const notes = db.namespace("notes");
await notes.write([
  { id: "n-1", text: "Webhook timeouts since Tuesday", vector: [0.1, 0.0, 0.98], meta: { team: "api" } },
  { id: "n-2", text: "New logo for the landing page", vector: [0.9, 0.4, 0.1], meta: { team: "design" } },
], { wait: true });
await notes.search("", { vector: [0.1, 0.0, 0.98], limit: 10, filter: { team: "api" } });
```

Hits come nearest first with a `score`. Real embeddings have hundreds of dimensions (up to
2,048). See [Vector and hybrid search](/guides/vectors).

## Next

- Shape your data well from the start: [Data model](/guides/data-model).
- Load files: [Importing files](/guides/import) (CSV, NDJSON, Parquet).
- Give an agent access: [Connect an agent](/mcp) and the [agent skill](/agent-skill).
