# GET /v1/line-moves

> Independent. Not affiliated with, endorsed by or sponsored by Novig.

Canonical: https://novigapi.com/docs/endpoints/line-moves
Facts checked: 2026-11-12

GET /v1/line-moves lists recorded Novig line moves and steam, newest first: the fair price before and after, the size in percentage points and the market. A move is 2 or more points; steam is 4 or more inside 2 minutes with volume behind it. On 12 Nov 2026 our collector logged about 2,350 moves and 170 steam an hour. (Source: [Novig API docs](https://docs.novig.com))

## What it returns

Novig keeps no line-move history, and its daily CSVs hold one candle per market. Our collector watches every two-outcome market's fair price as it sweeps the catalog and writes a row each time the price crosses a threshold. This endpoint lists those rows, so you can find what moved without reading every market's [line history](https://novigapi.com/docs/endpoints/line-history).

What counts as a move

| Kind | Rule |
| --- | --- |
| move | The fair price is at least 2 percentage points away from the last level we reported for that outcome. `from_price` is that level, `to_price` the new fair price. |
| steam | The fair price moved at least 4 points within 2 minutes, and traded volume grew in that window. Reported at most once per window per outcome; the same change also appears as a `move`. |
| Quoted tightly | Both outcomes must have a bid and the spread must be at most 10 points; thin books flicker by tens of points as single orders come and go, and those are not reported. |


## Parameters

Query and path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| league | string | No | League code, such as `NBA`. |
| market_id | string | No | Only moves on this market. |
| kind | string | No | `move` or `steam`. Default: both. |
| since | string (ISO 8601) | No | Only moves at or after this time. Free reads only the last 7 days. |
| min_delta_pp | number | No | Smallest absolute move to return, in percentage points, such as `5`. |
| limit | integer | No | Rows per page, 1 to 1,000. Default 100. Free returns at most 100. |
| cursor | string | No | `meta.next_cursor` from the previous page. |


## Request

```bash
curl -s "https://novigapi.com/api/v1/line-moves?market_id=01a1ce51-7bb1-74c0-a133-1fa7cee2ef73&limit=3" \
  -H "Authorization: Bearer nv_..."
```

## Response

Real rows from our collector on 12 Nov 2026: the fair price of Joel Embiid Over 24.5 points went from 0.245 to 0.32 inside two minutes as it traded, which logged steam of 7.5 points and a move of 5.5 points from the last reported level (0.265) in the same sweep. The `meta` credit figures are an example Developer account.

```json
{
  "data": [
    {
      "id": 1505,
      "market_id": "01a1ce51-7bb1-74c0-a133-1fa7cee2ef73",
      "outcome_id": "01a1ce51-7bb1-74c0-a133-1fb9ae8b95e0",
      "league": "NBA",
      "description": "Joel Embiid 24.5 POINTS",
      "outcome_name": "Over 24.5",
      "at": "2026-11-12T18:04:30.095Z",
      "from_price": 0.32,
      "to_price": 0.34,
      "delta_pp": 2,
      "kind": "move",
      "observed_at": "2026-11-12T18:04:30.095Z",
      "source_age_seconds": 0,
      "edge_cached": true
    },
    {
      "id": 1417,
      "market_id": "01a1ce51-7bb1-74c0-a133-1fa7cee2ef73",
      "outcome_id": "01a1ce51-7bb1-74c0-a133-1fb9ae8b95e0",
      "league": "NBA",
      "description": "Joel Embiid 24.5 POINTS",
      "outcome_name": "Over 24.5",
      "at": "2026-11-12T18:02:57.817Z",
      "from_price": 0.265,
      "to_price": 0.32,
      "delta_pp": 5.5,
      "kind": "move",
      "observed_at": "2026-11-12T18:02:57.817Z",
      "source_age_seconds": 0,
      "edge_cached": true
    },
    {
      "id": 1416,
      "market_id": "01a1ce51-7bb1-74c0-a133-1fa7cee2ef73",
      "outcome_id": "01a1ce51-7bb1-74c0-a133-1fb9ae8b95e0",
      "league": "NBA",
      "description": "Joel Embiid 24.5 POINTS",
      "outcome_name": "Over 24.5",
      "at": "2026-11-12T18:02:57.817Z",
      "from_price": 0.245,
      "to_price": 0.32,
      "delta_pp": 7.5,
      "kind": "steam",
      "observed_at": "2026-11-12T18:02:57.817Z",
      "source_age_seconds": 0,
      "edge_cached": true
    }
  ],
  "meta": {
    "observed_at": "2026-11-12T18:02:57.817Z",
    "source_age_seconds": 0,
    "edge_cached": true,
    "next_cursor": "eyJpZCI6MTQxNn0",
    "limit": 3,
    "count": 3,
    "plan": "developer",
    "credits_charged": 3,
    "credits_remaining": 9874,
    "credit_policy": "credits_v1"
  }
}
```

## Fields

Response fields

| Field | Type | Description |
| --- | --- | --- |
| id | integer | Row id. Higher means newer; the cursor pages by it. |
| market_id, outcome_id | string | The market, and the outcome whose fair price moved (the first outcome of the market; the other side moved by the same amount the other way). |
| league, description, outcome_name | string or null | League code, Novig's market label and the outcome's label. |
| at | string (ISO 8601) | When the collector recorded the move, in UTC. |
| from_price, to_price | number | Fair price before and after, as probabilities. |
| delta_pp | number | to_price minus from_price, in percentage points. Negative when the price fell. |
| kind | string | `move` or `steam`. |
| observed_at | string (ISO 8601) or null | When our collector last read this row from Novig. |
| source_age_seconds | integer or null | Seconds between that read and our answer. |
| edge_cached | boolean | True when the value came through Novig's public CDN, which may serve a copy up to its cache time (5 to 10 seconds) older. |
| meta.next_cursor | string or null | Pass as `cursor` to get the next page. Null on the last page. |
| meta.limit, meta.count | integer | The page size used and the rows on this page. |
| meta.observed_at | string (ISO 8601) | When our collector read the oldest row in this answer from Novig's public catalog. Each row also carries its own `observed_at`. |
| meta.source_age_seconds | integer | Seconds between that read and our response. On paid plans this stays at about 10 or less. |
| meta.edge_cached | boolean | True when Novig's edge cache served any of the reads, so the source copy may itself be a few seconds old. |
| meta.plan, credits_charged, credits_remaining, credit_policy | string, integer | Your plan, what this call cost, the credits left this month and the credit rules version. |


## Credit cost

Weighted 3x: 3 credits per call per 100 rows requested (the `limit` you send, default 100, maximum 1,000; Free pages hold at most 100 rows). A default page costs 3 credits. A move row is history: Free reads only the last 7 days, and one call reads at most 14 days (the last 24 hours by default). Errors and 304 Not Modified cost 0; the `X-Credits-Cost` header and `meta.credits_charged` state the price of every call.

## Python

```python
import os
import requests

KEY = os.environ["NOVIGAPI_API_KEY"]  # nv_...
HEADERS = {"Authorization": f"Bearer {KEY}"}

r = requests.get("https://novigapi.com/api/v1/line-moves", params={"league": "NBA", "kind": "steam", "min_delta_pp": 5}, headers=HEADERS, timeout=10)
for m in r.json()["data"]:
    print(m["at"], m["description"], m["outcome_name"], m["from_price"], "->", m["to_price"], f"{m['delta_pp']:+} pp")
```

## JavaScript

```javascript
const KEY = process.env.NOVIGAPI_API_KEY; // nv_...
const headers = { Authorization: "Bearer " + KEY };

// Poll for new steam every 30 seconds, remembering the newest id seen.
let newest = 0;
async function poll() {
  const { data } = await (await fetch("https://novigapi.com/api/v1/line-moves?league=NBA&kind=steam&limit=100", { headers })).json();
  for (const m of data.filter((x) => x.id > newest).reverse()) console.log(m.at, m.description, m.delta_pp + " pp");
  newest = Math.max(newest, ...data.map((x) => x.id));
}
setInterval(poll, 30000);
```

## Notes and edge cases

- Rows come newest first by id. Two rows can share a time: one sweep can log a move and a steam on the same outcome.
- Moves are measured on the fair price, the midpoint of both sides' bids, not on the last trade, so a single stray fill never logs a move.
- Markets with more than two outcomes have no fair price and log no moves.
- For a push instead of a poll, subscribe to `line.move` and `steam` on the [stream](https://novigapi.com/docs/endpoints/stream), or to `line_move` and `steam` [webhooks](https://novigapi.com/docs/endpoints/webhooks) with your own threshold.
- The thresholds are the collector's defaults on 12 Nov 2026 (2 points, 4 points in 120 seconds). Use `min_delta_pp` to see only bigger moves.

## Sources

- [Novig API docs](https://docs.novig.com)
- [Novig docs: Exchange Data](https://docs.novig.com/public-data/overview.md)

---

Get a free API key: https://novigapi.com/sign-up . Docs: https://novigapi.com/docs . MCP: https://novigapi.com/api/mcp . Operated by Stunning Couches LLC. Independent. Not affiliated with, endorsed by or sponsored by Novig.
