---
name: neuroencoder
description: Use when working with EEG data to generate embeddings with the neuroencoder Python package, run similarity search or classification on brain states, or visualize EEG recordings. Reach for this when a user needs to turn raw EEG into fixed-dimensional vectors for downstream ML.
metadata:
  version: "2.2"
---

# Neuroencoder

`pip install neuroencoder` (2.2). One 768-d L2-normalised embedding per 30 s window of EEG, any montage, any sampling rate.

```python
from neuroencoder import embeddings                   # hosted API; needs a key
r = embeddings("night.edf")                           # EDF, BDF, single-file FIF or .npy path, open file, or mne Raw
r = embeddings(eeg, sfreq=256, channel_names=ch_names)   # array [channels, samples]
emb, t0 = r.to_numpy(), r.t_start                     # [n_windows, 768], window starts in seconds
r.channels_used                                       # per input channel: label, region (None = ignored), bipolar
r.usage                                               # windows, seconds_of_eeg (billed), month_seconds_of_eeg
```

- Trial key: `neuroencoder login --email you@lab.org` prints a code, emails a link, and saves the key to `~/.config/neuroencoder/api_key` once a human approves the link (the page must show the same code). HTTP device flow: `POST /v1/signup {"email", "device": true}` -> `device_code`, `user_code`, `interval` 5; poll `POST /v1/signup/token {"device_code"}` until the key arrives once. Plain `POST /v1/signup {"email"}` mails a link that shows the key once (or `POST /v1/verify {"t": token}`). 100 h of EEG per month, 30 requests/min, 2,000 windows/min, 2 requests in flight, 1 job at a time, 30 days; signing up again replaces the key (same limits, usage carried over). Key lookup: `api_key=`, `NEUROENCODER_API_KEY`, then the saved file.
- CLI: `neuroencoder inspect night.edf` (free, no upload: channel regions, windows, billed seconds), `neuroencoder embed night.edf -o night.npz` (`embeddings` [n, 768], `t_start`, `meta` JSON), `neuroencoder whoami`; `--json` prints one JSON object, errors as `{"error": {"code", "message", "fix"}}`.
- MCP: `pip install 'neuroencoder[mcp]'`, config `{"mcpServers": {"neuroencoder": {"command": "neuroencoder", "args": ["mcp"]}}}`. Tools `inspect_recording` (free), `embed_recording` (bills, writes `<stem>.npz`), `find_similar`, `plot_embeddings` (free, local), `account`. Vectors stay in `.npz` files and are never returned to the model.
- `channel_names` are 10-20 labels, matched case-insensitively (`EEG Fpz-Cz`, `C3-M2`, `FP1-REF` parse). Bipolar pairs map to the first electrode's region. Unmatched channels are ignored and shown with region None. Names may be omitted only for exactly the 8 regional channels.
- Default `stride_seconds=30` (non-overlapping); 0.5 to 3600. Billing is per window, 30 s of EEG each, so stride 1 s bills about 30x the recording length.
- Hosted limits: 1024 windows per request (8.5 h at stride 30; check with `neuroencoder.n_windows(n_samples, sfreq, stride_seconds)`), sfreq 50-20000 Hz, 256 channels, 512 MB per upload. The Python client splits longer recordings into several requests and merges them; with plain REST send a long recording as one job instead (see below).
- Input from 0.5 s to 30 s gives one window. Resampling is linear to 250 Hz.
- Errors carry `code`, `param` and `fix`; follow `fix`. The client retries 503, 429 `rate_limit_exceeded`, timeouts and 500/502/504 itself with the same `Idempotency-Key` (a repeated request is not billed twice); other 429s (`concurrency_limit_exceeded`, `windows_rate_limit_exceeded`, or one without a JSON code from the gateway) raise `RateLimitError`: wait `Retry-After`, or back off with jitter. Do not retry `quota_exceeded` or `too_many_queued_jobs`. With REST send `Idempotency-Key: <uuid4>` per logical request and reuse it on retries; the same key with a different body is a 409.
- REST: `POST https://api.neuroencoder.com/v1/embeddings`, Bearer auth, multipart `file` + `metadata` JSON, or JSON `input`/`sfreq`/`channel_names`.
- Long recordings over REST: one job per file of up to 24 h (.edf, .bdf, .npy; 2 GB, 8192 windows), so a full night is one job (a 23 h Sleep-EDF night, 2,802 windows: 65 s from upload to result). `POST /v1/files {"filename", "bytes"}` -> `upload_url`; PUT the file there (no API key); `POST /v1/jobs {"file", "options": {"stride_seconds", "picks", "sfreq", "channel_names"}}` -> 202; poll `GET /v1/jobs/{id}` every `Retry-After` s until `succeeded`, `failed`, `cancelled` or `expired`; download `result.url` (`.npy` float32 `[windows, 768]`, link valid 10 min). Results kept 7 days; the upload is deleted when the job ends; failed or cancelled jobs are not billed. The Python client 2.2.0 does not use jobs. Details: https://docs.neuroencoder.com/api#jobs-for-long-recordings
- Plots and atlas: `pip install 'neuroencoder[viz]'`; `ne.explore(r)`, `ne.export(r, "atlas")`, `ne.plot(r, save="umap.png")`.
- Tested recipe with results: https://docs.neuroencoder.com/similarity-search . Agents: https://docs.neuroencoder.com/agents . Reference: https://docs.neuroencoder.com/api . Research use only, not a medical device.
