# GET /v1/ev

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

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

GET /v1/ev lists sportsbook prices that pay more than Novig's fair probability on the same line, best edge first: EV% = fair probability x book decimal odds - 1. Rows are recomputed every 60 seconds from FanDuel and DK Sportsbook prices; on 13 Nov 2026 a FanDuel NHL shots-on-goal under at +132 against a Novig fair of 44% read +2.08%. (Source: [Novig API docs: Trading fees](https://docs.novig.com/api/concepts/fees.md))

## What it returns

Novig is an exchange: its two-sided mid is a market price with no bookmaker margin in it. We use that mid as the fair probability and measure every FanDuel and DK Sportsbook price on exactly the same line against it.

```
ev_pct = (fair_probability x book_decimal - 1) x 100
```
fair_probability = (bid + (1 - other side's bid)) / 2, before fees. A sportsbook bet pays no exchange fee, so none is taken off.

```
kelly_full = (b x p - (1 - p)) / b
```
b = book_decimal - 1, p = fair_probability. Most bettors stake a quarter of it (kelly_quarter).

Free keys read the top 5 rows as they stood 10 minutes ago (`meta.delayed` is true). Developer and up read every live row with all filters. The same rows are on [today's +EV bets](https://novigapi.com/positive-ev-bets).

## Parameters

Query and path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| league | string | No | League code, such as `NBA`, `NHL` or `NCAAF`. |
| market_type | string | No | `moneyline`, `spread`, `total` or `player_prop`. |
| min_edge | number | No | Smallest EV% to return, -3 to 100. Default 0, so only +EV rows. |
| books | string | No | Comma list of book codes: `fd` (FanDuel), `dk` (DK Sportsbook). |
| player | string | No | Player slug, such as `gabriel-perreault`. |
| market_id | string | No | Only rows on this Novig market. |
| limit | integer | No | Rows per page, 1 to 1,000. Default 50. Free returns at most 100. |
| cursor | string | No | `meta.next_cursor` from the previous page. |


## Request

```bash
curl -s "https://novigapi.com/api/v1/ev?league=NHL&market_type=player_prop&min_edge=1" \
  -H "Authorization: Bearer nv_..."
```

## Response

Gabriel Perreault under 1.5 shots on goal, Rangers at Canucks, read 13 Nov 2026 02:57 UTC: FanDuel +132 (decimal 2.32) against a Novig fair of 0.44 from bids 0.43 and 0.55 (spread 2 points), so EV = 0.44 x 2.32 - 1 = +2.08%.

```json
{
  "data": [
    {
      "id": "01a1d1b9-25e9-7a90-bc19-a23602c58aac:fd:under",
      "league": "NHL",
      "event_id": "01a1ce81-c543-79b2-964e-e530fc5d03b1",
      "starts_at": "2026-11-14T02:00:00.000Z",
      "home": "Vancouver Canucks",
      "away": "New York Rangers",
      "market_type": "player_prop",
      "stat": "shots_on_goal",
      "player": "Gabriel Perreault",
      "player_slug": "gabriel-perreault",
      "line": 1.5,
      "side": "under",
      "book": "fd",
      "book_name": "FanDuel",
      "book_american": 132,
      "book_american_text": "+132",
      "book_decimal": 2.32,
      "fair_probability": 0.44,
      "fair_american": 127,
      "ev_pct": 2.08,
      "kelly_full": 0.0158,
      "kelly_quarter": 0.004,
      "exchange_spread": 0.02,
      "exchange_depth_usd": 40.79,
      "exchange_market_id": "01a1d1b9-25e9-7a90-bc19-a23602c58aac",
      "book_age_seconds": 40,
      "fair_age_seconds": 21,
      "confidence": "high",
      "flags": [],
      "first_seen": "2026-11-13T02:55:42.000Z",
      "observed_at": "2026-11-13T02:57:00.000Z"
    }
  ],
  "meta": {
    "observed_at": "2026-11-13T02:57:00.000Z",
    "source_age_seconds": 0,
    "edge_cached": false,
    "next_cursor": null,
    "limit": 50,
    "count": 1,
    "formula": "ev_pct = (fair_probability x book_decimal - 1) x 100",
    "delayed": false,
    "total": 1,
    "plan": "developer",
    "credits_charged": 2,
    "credits_remaining": 9874,
    "credit_policy": "credits_v1"
  }
}
```

## Fields

Response fields

| Field | Type | Description |
| --- | --- | --- |
| id | string | Stable row id: Novig market, book and side. |
| league, event_id, starts_at, home, away | string | The game, from Novig's event. |
| market_type, stat, player, player_slug, line, side | string, number | The market: `moneyline`, `spread` (line is the home team's handicap), `total` or `player_prop` with its stat; side is `home`, `away`, `over` or `under`. |
| book, book_name | string | `fd` FanDuel or `dk` DK Sportsbook. |
| book_american, book_american_text, book_decimal | integer, string, number | The book's price. |
| fair_probability, fair_american | number, integer | Novig's fair price for the same side. |
| ev_pct | number | Expected value of a 1-unit bet at the book's price, percent. |
| kelly_full, kelly_quarter | number | Kelly stake as a fraction of bankroll, full and quarter. |
| exchange_spread, exchange_depth_usd, exchange_market_id | number, string | Novig's spread on the side (ask minus bid), the dollars its best opposite bid absorbs, and the Novig market id. |
| book_age_seconds, fair_age_seconds | integer | Seconds since each price was last read. |
| confidence, flags | string, string[] | `low` when any flag is set: `stale_book_price` (over 360 s), `stale_fair_price` (over 180 s), `wide_exchange_spread` (over 6 points), `thin_exchange_depth` (under $25). |
| first_seen | string (ISO 8601) | When this edge first appeared. |
| observed_at | string (ISO 8601) | The older of the two prices' read times. |
| meta.delayed, meta.delay_seconds, meta.as_of | boolean, integer, string | Set on Free keys: the rows are the delayed view. |
| 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 2x: 2 credits per call per 100 rows requested (the `limit` you send, default 50, maximum 1,000; Free pages hold at most 100 rows). A default page costs 2 credits. 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/ev", params={"league": "NBA", "min_edge": 2, "limit": 50}, headers=HEADERS, timeout=10)
for row in r.json()["data"]:
    if row["confidence"] == "high":
        print(row["ev_pct"], row["book_name"], row["book_american_text"], row["player"] or row["home"], row["side"], row["line"])
```

## JavaScript

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

const res = await fetch("https://novigapi.com/api/v1/ev?league=NBA&min_edge=2", { headers });
const { data, meta } = await res.json();
console.log(meta.delayed ? "delayed view" : "live", data.map((r) => [r.ev_pct, r.book_name, r.book_american_text]));
```

## Notes and edge cases

- Only games that have not started. In-play prices move faster than either venue is read, so they are left out.
- Novig markets whose spread is wider than 15 points are left out entirely: a wide mid is not a fair price. Between 6 and 15 points the row is kept with `wide_exchange_spread`.
- Lines must match exactly. A book at 6.5 and Novig at 7.5 is not a comparison; it can be a [middle](https://novigapi.com/docs/endpoints/arbitrage).
- Protected props (participation protection) settle differently and are never compared.
- Use a [Kelly calculator](https://novigapi.com/kelly-criterion-calculator) or the [EV calculator](https://novigapi.com/ev-calculator) to check one row by hand.

## Sources

- [Novig API docs: Trading fees](https://docs.novig.com/api/concepts/fees.md)
- [Novig API docs](https://docs.novig.com)

---

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.
