> ## Documentation index
> Fetch the complete documentation index at: https://api-docs.scanr.ai/llms.txt
> Use it to discover every page before exploring further.

# Quick Start

Run your first scan against the whole U.S. market in about two minutes.

Every scan takes two calls. First you **get a scan payload**, the JSON that describes the screen. Then you **run it**. You don't write the payload by hand: describe the screen in English and the API writes it, or export a scan you already built in the scanr.ai scanner.

## 1. Get a token

1. Pick an API plan on [API pricing](https://scanr.ai/api-pricing). Every plan includes the full scanner.
2. Create a token at **[Account → API Tokens](https://scanr.ai/account/api-tokens)** with the `read` scope, and copy it. It is shown once.
3. Put it in an environment variable, which every example in these docs reads:

```bash
export SCANR_API_TOKEN="scanr_..."
```

Check it works:

```bash
curl https://scanr.ai/api/v2/misc/market-status \
  -H "Authorization: Bearer $SCANR_API_TOKEN"
```

A JSON body with `"status": {"market": "open" | "closed" | "extended-hours", ...}` means you're in.

The cURL examples below use [`jq`](https://jqlang.org) to move JSON between calls.

## 2. Get a scan payload

A scan payload says which stocks are eligible and which filters they must pass. There are two ways to get one, and both produce the same JSON.

### Describe it in English

`POST /api/v2/scan/natural` turns a sentence into a payload. It returns the payload and an explanation of each filter. It does not run the scan.

```bash
curl -s https://scanr.ai/api/v2/scan/natural \
  -H "Authorization: Bearer $SCANR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"query": "stocks worth at least $2B, above their 50-day moving average, with relative strength of 80 or more"}' \
  > natural.json

jq '.reasoning.summary' natural.json                  # what will be screened, in one sentence
jq '.generated_payload' natural.json > payload.json   # the payload itself
```

Check `reasoning.summary` before you run anything: it is the quickest way to catch a misread. Generation takes a few seconds and spends [AI credits](https://api-docs.scanr.ai/rate-limits.md#ai-credits); running the payload afterwards doesn't. [Natural-Language Scans](https://api-docs.scanr.ai/natural-language-scans.md) covers the full response.

### Or export it from the scanner

If you already screen in the [scanr.ai scanner](https://scanr.ai/scan), use the scan you have:

1. Open the scan, or build one. Presets work too.
2. Click **Filters**, then **Export filters** (or press Ctrl+Shift+E).
3. Click **Download JSON**, or **Copy to clipboard**, and save it as `payload.json`.

The export is the same payload `/scan/natural` returns, and costs no AI credits. Filters you've switched off in the scanner stay in the export so it imports back intact; the API skips them when it runs the scan. Scans you've saved can also be read from code with [`POST /scan/myscans`](https://api-docs.scanr.ai/saved-scans.md#read-your-saved-scans).

**No credits to spare? Use this payload**

This is the screen from the sentence above, written out. Save it as `payload.json`:

```json
{
  "Universe": {
    "Exchange": ["nasdaq", "nyse", "amex", "arca", "bats", "bx"],
    "Type": ["cs", "os", "adrc"]
  },
  "All": [
    {
      "Stock": {
        "MarketCap": [
          { "State": { "Value": { "Lower": { "Operator": "gte", "Value": 2000000000 } } } }
        ]
      },
      "Technical": {
        "SMA": [
          {
            "Timeframe": "1d",
            "Inputs": ["bar_close"],
            "Params": { "Period": { "Value": 50 } },
            "Returns": { "Want": { "SMA": { "Price": { "Comparison": { "LessThan": true } } } } }
          }
        ]
      },
      "Indicator": {
        "RelativeStrengthMkt": [
          { "Returns": { "Want": { "Value": { "GreaterThanOrEqual": 80 } } } }
        ]
      }
    }
  ],
  "Sort": { "field": "market_cap", "direction": "desc" }
}
```

## 3. Run it

Send the payload to `POST /api/v2/scan` as `instructions`. The payload goes in unchanged; only the wrapper is new:

cURL:

```bash
jq '{instructions: .}' payload.json \
  | curl https://scanr.ai/api/v2/scan \
      -H "Authorization: Bearer $SCANR_API_TOKEN" \
      -H "Content-Type: application/json" \
      -d @-
```

Python:

```python
import json
import os

import requests

API = "https://scanr.ai/api/v2"
HEADERS = {"Authorization": f"Bearer {os.environ['SCANR_API_TOKEN']}"}

# 1. Get a payload: from a sentence here. For an exported scan instead:
#      payload = json.load(open("payload.json"))
natural = requests.post(
    f"{API}/scan/natural",
    headers=HEADERS,
    json={"query": "stocks worth at least $2B, above their 50-day moving average, with relative strength of 80 or more"},
    timeout=120,
).json()
if not natural["conversion_success"]:
    raise SystemExit(natural.get("validation_errors"))
print("Screening for:", natural["reasoning"]["summary"])
payload = natural["generated_payload"]

# 2. Run it.
response = requests.post(f"{API}/scan", headers=HEADERS, json={"instructions": payload}, timeout=60)
response.raise_for_status()
result = response.json()
print(f"{result['count']} matches:", ", ".join(result["tickers"] or []))
```

JavaScript:

```javascript
const API = "https://scanr.ai/api/v2";
const headers = {
  Authorization: `Bearer ${process.env.SCANR_API_TOKEN}`,
  "Content-Type": "application/json",
};

async function post(path, body) {
  const response = await fetch(API + path, { method: "POST", headers, body: JSON.stringify(body) });
  if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);
  return response.json();
}

// 1. Get a payload: from a sentence here. For an exported scan instead:
//      const payload = JSON.parse(await readFile("payload.json", "utf8"));
const natural = await post("/scan/natural", {
  query: "stocks worth at least $2B, above their 50-day moving average, with relative strength of 80 or more",
});
if (!natural.conversion_success) throw new Error(JSON.stringify(natural.validation_errors));
console.log("Screening for:", natural.reasoning?.summary);
const payload = natural.generated_payload;

// 2. Run it.
const result = await post("/scan", { instructions: payload });
console.log(`${result.count} matches:`, (result.tickers ?? []).join(", "));
```

The response lists every matching ticker, largest first:

```json
{
  "tickers": ["NVDA", "AVGO", "ORCL", "PLTR", "ANET"],
  "count": 5,
  "cached": false,
  "end_time": "2026-09-29T20:05:12.418Z",
  "scan_id": 918273
}
```

(The tickers and count depend on the market when you run it.)

> **Keep the payload:** `payload.json` is now a reusable screen. Run it again tomorrow, from a cron job or from your agent, for one request and no AI credits. Generating it again from the sentence costs credits and can come back slightly different.

## 4. Change the question

To change the screen, **refine** the payload in English. `POST /api/v2/scan/refine` applies your change and keeps every filter you didn't mention:

```bash
jq '{existing_payload: ., refinement_query: "only healthcare stocks, and nothing under $10"}' payload.json \
  | curl -s https://scanr.ai/api/v2/scan/refine \
      -H "Authorization: Bearer $SCANR_API_TOKEN" \
      -H "Content-Type: application/json" \
      -d @- \
  | jq '.refined_payload' > refined.json
```

Run `refined.json` exactly as in step 3. Refinement spends AI credits too.

You can also edit the JSON yourself. Every filter is a small object inside the `All` group:

**Oversold instead of strong: RSI(14) below 30**

Replace the `Technical` block with:

```json
"Technical": {
  "RSI": [
    {
      "Timeframe": "1d",
      "Inputs": ["bar_close"],
      "Params": { "Period": { "Value": 14 } },
      "Returns": { "Want": { "RSI": { "Value": { "LessThan": 30 } } } }
    }
  ]
}
```

and drop the `Indicator` block.

**Only recent golden crosses**

Replace the `Technical` block with an `SMA` 50 that `CrossesAbove` the `SMA` 200 `Within` 5 bars. The full payload is in [Building Scan Payloads](https://api-docs.scanr.ai/scan-payloads.md#compare-with-another-study).

**Only one sector**

Add a sector filter next to `MarketCap`:

```json
"Sector": [{ "State": { "Value": ["Healthcare"] } }]
```

The allowed names come from `GET /api/v2/stocks/sectors`.

## 5. Next steps

- [How Scanning Works](https://api-docs.scanr.ai/scanning.md): The model behind the payload you just ran.
- [Natural-Language Scans](https://api-docs.scanr.ai/natural-language-scans.md): Generate and refine payloads, and write queries the model reads well.
- [Run an End-of-Day Screen](https://api-docs.scanr.ai/guides/end-of-day-screen.md): Run a saved payload every evening and export the matches.
- [MCP Server](https://api-docs.scanr.ai/mcp-server.md): Let Claude or Cursor build and run scans for you.
