# Handling Odds - Parse Results and Grade Bets
URL: https://sockodds.com/docs/guides/handling-odds/

# Handling Odds - Parse Results and Grade Bets [#handling-odds---parse-results-and-grade-bets]

## Overview [#overview]

The SockOdds API carries odds and, where the source has them, results for every event. This guide shows how to fetch and parse the odds for a group of events and grade them.

## The three prices [#the-three-prices]

| Field | Meaning |
| --- | --- |
| `bookOdds` | Consensus book price as an American string ("-108", "+240"), present whenever any book prices the market |
| `fairOdds` | The de-vigged fair price from exchange/sharp consensus; `null` when it can't be computed and on Lite keys — `fairOddsAvailable` says which |
| `byBookmaker[book]` | That book's own price: `odds` (American), `decimal`, `available`, `lastUpdatedAt`, and `overUnder`/`spread` where relevant |

### American to decimal [#american-to-decimal]

Every bookmaker entry already carries `decimal`; for the consensus fields:

```python
def decimal(american: str) -> float:
    a = int(american)
    return 1 + (a / 100 if a > 0 else 100 / -a)
```

### Two-sided markets [#two-sided-markets]

`opposingOddID` names the other side (`…-over` ↔ `…-under`, `…-home` ↔ `…-away`). Alt lines live under `altLines` keyed by line, each with its own `byBookmaker`, when you ask with `includeAltLines=true`.

## Example: grading last week's NRL totals [#example-grading-last-weeks-nrl-totals]

```javascript
let cursor = null, events = [];
do {
  const url = new URL("https://api.sockodds.com/v2/events");
  url.search = new URLSearchParams({ leagueID: "NRL", startsAfter: "2026-08-31", startsBefore: "2026-09-07", finalized: "true", ...(cursor ? { cursor } : {}) });
  const body = await (await fetch(url, { headers: { "x-api-key": KEY } })).json();
  events = events.concat(body.data); cursor = body.nextCursor;
} while (cursor);

for (const event of events) {
  for (const odd of Object.values(event.odds)) {
    if (odd.betTypeID !== "ou" || !odd.scoringSupported) continue;
    // the stat that settles this market lives at results.{periodID}.{statEntityID}.{statID}
    const actual = event.results?.[odd.periodID]?.[odd.statEntityID]?.[odd.statID];
    const line = parseFloat(odd.bookOverUnder);
    if (actual == null || Number.isNaN(line)) continue;
    console.log(odd.oddID, actual > line ? "Over wins" : actual === line ? "Push" : "Under wins");
  }
}
```

> **Two flags you must read.** A book with `available: false` is listed but suspended. An event with `info.stale: true` has had no source write for 45 minutes — the prices are the last known, not current.

> Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/)
