# QM Screener documentation

Everything QM Screener knows is reachable over HTTP: three public endpoints,
a handful of authenticated ones, and an MCP server. This page is the human
version; `/openapi.json` is the machine version, and the two are generated from
the same registry of screens.

## API reference

Machine-readable: `https://qmscreener.com/openapi.json` (OpenAPI 3.1).

### Public endpoints

| Endpoint | Returns |
|---|---|
| `GET https://qmscreener.com/api/v1/health` | `{"ok": true}` whenever the service is up. |
| `GET https://qmscreener.com/api/v1/preview` | The leading few passing tickers per screen, plus the total that passed. |
| `GET https://qmscreener.com/api/v1/sector-strength` | 21 sector ETFs' trailing returns against SPY. |

### Authenticated endpoints

| Endpoint | Returns |
|---|---|
| `GET https://qmscreener.com/api/v1/screens` | Every screen's identifier, methodology, sort, and filter controls. |
| `GET https://qmscreener.com/<screen>_results.json` | One screen's full result set. |
| `GET https://qmscreener.com/api/v1/history?ticker=NVDA` | Ascending daily OHLCV bars for one symbol. |
| `GET https://qmscreener.com/api/v1/watchlist` | The account's saved tickers. |
| `POST https://qmscreener.com/api/v1/watchlist` | Add or remove one ticker. |

Authenticate with an OAuth 2.1 bearer token — see `https://qmscreener.com/auth.md`.

### Versioning and deprecation

The current major version is **v1**. Pin it. Every endpoint above is
canonical at `/api/v1/...`; the unversioned `/api/...` form is a
permanent alias for the same handler, kept because the dashboard's own
JavaScript has always used it. Both spellings answer with an `API-Version`
header, so a caller can always tell what served it.

A breaking change means a **new prefix** — `/api/v2/...` — not a changed one.
Adding a field, an endpoint, or an optional parameter is not breaking and ships
without a version bump, so do not treat an unfamiliar response field as an
error. When v1 is eventually retired, its responses will carry
RFC 8594 `Deprecation` and `Sunset` headers for at least
180 days before the endpoint stops answering, and the
sunset date will appear here and in `https://qmscreener.com/openapi.json`. There is no such date
today.

### Errors

Every failure is JSON, never an HTML page:

```json
{
  "error": "subscription_required",
  "code": "subscription_required",
  "message": "This endpoint needs an active subscription.",
  "hint": "Start a 7-day free trial at https://qmscreener.com/subscribe.",
  "docs": "https://qmscreener.com/docs#errors"
}
```

`code` is the stable identifier to branch on; `message` and `hint` are for
humans and for an agent explaining itself.

### Rate limits

Responses carry the RFC 9331 `RateLimit` header, and a 429 carries
`Retry-After` in seconds. Self-throttle on those rather than on a fixed sleep.

## MCP

`https://qmscreener.com/mcp` speaks MCP over Streamable HTTP. The server card at
`https://qmscreener.com/.well-known/mcp/server-card.json` lists the tools. Public tools work
without a credential; the full-result tools take the same bearer token as the
REST API.

## Agent skills

`https://qmscreener.com/.well-known/agent-skills/index.json` indexes task recipes:
`todays-momentum-setups`, `screen-methodologies`, `ticker-price-history`, `momentum-watchlist`.

## Data and limits

End-of-day US equities, refreshed once per trading day after the close. No
intraday, no fundamentals, no news. Every filter comparison is strictly
greater-than, and every row carries the values that decided it, so any result
can be audited without re-running the screen. Not financial advice.
