# @oddshawk/oddshawk-sdk

> SDK for connecting to the Odds Hawk Data Feed

Latest version **1.1.1** (published 2026-09-24) · ISC license · 0 weekly downloads

## Install

```sh
npm install @oddshawk/oddshawk-sdk
pnpm add @oddshawk/oddshawk-sdk
yarn add @oddshawk/oddshawk-sdk
bun add @oddshawk/oddshawk-sdk
```

## Health

**Score 60/100 (C)** — status: active.

Positive: esm support; no vulnerabilities; recently updated; high maintenance score.

Warnings: low downloads; no types.

## Facts

| | |
|---|---|
| Version | 1.1.1 |
| Published | 2026-09-24 |
| First published | 2022-01-28 |
| Weekly downloads | 0 |
| License | ISC |
| TypeScript types | none |
| Module format | ESM + CommonJS |
| Node | >=26 |
| Dependencies | 3 |
| Unpacked size | 21.8 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 0 |
| Author | Nathan Edwards |
| Maintainers | oddshawk |

## Links

- npm: https://www.npmjs.com/package/@oddshawk/oddshawk-sdk
- Repository: https://github.com/oddshawk/node-js-sdk
- Homepage: https://github.com/oddshawk/node-js-sdk#readme
- Issues: https://github.com/oddshawk/node-js-sdk/issues
- npm.io page: https://npm.io/package/@oddshawk/oddshawk-sdk

## Dependencies (3)

- [ws](https://npm.io/package/ws.md) ^8.21.1
- [axios](https://npm.io/package/axios.md) ^1.18.1
- [js-sha256](https://npm.io/package/js-sha256.md) ^1.0.0

## Recent versions

- 1.1.1 (latest) — 2026-09-24
- 1.0.17 — 2025-12-19
- 1.0.16 — 2025-01-11
- 1.0.15 — 2023-12-29
- 1.0.14 — 2023-12-21
- 1.0.13 — 2023-10-07
- 1.0.12 — 2023-07-19
- 1.0.11 — 2023-04-03
- 1.0.10 — 2022-11-12
- 1.0.9 — 2022-11-12
- 1.0.8 — 2022-08-22
- 1.0.7 — 2022-08-20
- 1.0.6 — 2022-07-30
- 1.0.5 — 2022-03-11
- 1.0.4 — 2022-02-03
- … 3 more at https://npm.io/package/@oddshawk/oddshawk-sdk/versions

## README

# OddsHawk JavaScript SDK

Client for the OddsHawk public odds catalog (REST) and live WebSocket feed.

- **Package:** `@oddshawk/oddshawk-sdk`
- **API docs:** https://odds.software/docs/ (OpenAPI **0.3.0**)
- **Auth:** `X-OH-USER` + `X-OH-HASH` (HMAC), or session cookie `oh-id` after `/authenticate`

## Installation

```bash
npm install @oddshawk/oddshawk-sdk
```

## Authentication

| Mode | Constructor | Behaviour |
|------|-------------|-----------|
| **Node (password)** | `new OddsHawk(user, password, true)` | SDK derives a time-based SHA-256 hash on each REST call via `generateHash` |
| **Browser (pre-hash)** | `new OddsHawk(user, hash, false)` | Caller supplies a server-side hash; call `rest.authenticate()` once to set the `oh-id` cookie, then cookie + `X-OH-USER` |

Hash algorithm (same as OpenAPI): SHA-256 hex of `password + unixSeconds`, then append `unixSeconds` as 8 lowercase hex digits. Use exported `generateHash(password)` in Node when needed.

Optional 4th argument: `baseUrl` (default `https://www.odds.software`).

## REST — public catalog

Aligned with OpenAPI public paths:

| Method | Path |
|--------|------|
| `rest.authenticate()` | `GET /authenticate` |
| `rest.version()` | `GET /rest` |
| `rest.odds(filter)` | `GET /rest/odds` |
| `rest.events(fromNow, filter)` | `GET /rest/odds/events` |
| `rest.sports(fromNow)` | `GET /rest/odds/sports` |
| `rest.markets(fromNow, filter)` | `GET /rest/odds/markets` |
| `rest.providers(fromNow, filter)` | `GET /rest/odds/providers` |
| `rest.competitions(fromNow, filter)` | `GET /rest/odds/competitions` |

`odds` filter keys include: `fromNow`, `eventTime`, `eventName`, `sport`, `provider`, `selectionStatus`, `market`, `updatedBefore`, `competition`, `competitionName`, `sortField`, `sortDirection`, `limit`, `skip`. To pin a single event, pass `eventName` together with `eventTime`.

The `/rest/match/*` helpers are documented separately under [Matching](#matching) — a separate surface, authenticated like the rest of the API.

### Node.js

```js
import { OddsHawk, OddsHawkApiError } from '@oddshawk/oddshawk-sdk';

const oddshawk = new OddsHawk(username, password, true);

try {
  const { version } = await oddshawk.rest.version();
  const rows = await oddshawk.rest.odds({ sport: 'Horse Racing', limit: 50 });
  console.log(version, rows.length);
} catch (err) {
  if (err instanceof OddsHawkApiError) {
    console.error(err.status, err.message, err.data);
  }
  throw err;
}
```

### Browser

```js
import { OddsHawk } from '@oddshawk/oddshawk-sdk';

const oddshawk = new OddsHawk(username, hash, false); // hash generated server-side

await oddshawk.rest.authenticate();
const sports = await oddshawk.rest.sports();
const events = await oddshawk.rest.events(true, { sport: 'Horse Racing' });
```

## Errors

Catalog REST methods throw `OddsHawkApiError` on HTTP/transport failure:

- `status` — HTTP status when a response was received (`403`, `400`, `429`, `500`, …)
- `data` — response body when present
- `code` — the body's `code` field when present. The live failures below put their machine-readable code in `error` instead (e.g. `coverage_not_entitled`, `throttled`), so read `data.error` for it.

Live statuses:

| Status | When |
|--------|------|
| **403** | Missing/invalid hash or session (plain-text body); or the request is outside your account's coverage grant — `{"error":"coverage_not_entitled"}` |
| **400** | Invalid odds query (empty `eventTime` or `eventName`) |
| **429** | A capped account is already over this hour's data-point limit — `{"error":"throttled",…}`, including `retry_after_seconds` |
| **404** | No match on `/rest/match/*` |

Metering is live too: successful responses on the metered routes (`GET /rest/odds` and `/rest/match/*`) carry your current usage in the `X-Data-Points-This-Hour`, `X-Data-Points-Limit` and `X-Hour-Resets-At` headers, and `GET /rest/account` reports the same limit plus your coverage grant. This SDK returns response bodies only, so read those headers from a direct HTTP call if you need them.

The following codes are **planned and not emitted yet**: `feed_down`, `catalog_dropped`, `payment_required` — do not depend on them.

Guides: https://odds.software/guides/errors.md and https://odds.software/guides/coverage.md.

## Matching

`/rest/match/*` resolves provider-supplied names to OddsHawk canonical entities. It is documented
in the OpenAPI `Matching` section and, like every other `/rest` endpoint — `/rest/odds` included —
it is available to any **authenticated** account (the same auth headers as every other call).

| Method | Path |
|--------|------|
| `rest.matchEvent(provider, name, time, sport, init = false)` | `GET /rest/match/event` |
| `rest.matchSelection(provider, name, time, sport, eventName, init = false)` | `GET /rest/match/selection` |
| `rest.matchTeam(provider, name, time, sport, init = false)` | `GET /rest/match/team` |
| `rest.matchCompetition(provider, name, time, sport, init = false)` | `GET /rest/match/competition` |

- `name` is the name to resolve (a canonical name, provider spelling, or known alias); `time` is the
  event start in unix seconds; `sport` is required.
- `init: true` registers an unresolved name for curation — the call that registers it still resolves to `false`.
- Unlike catalog methods, these return `false` on no match / error instead of throwing.
- Some lookups resolve to just the canonical name (for example `{ event: { name } }`) instead of a
  dictionary record — treat those as successful resolutions.

Full guide: https://odds.software/guides/matching.md

## WebSocket

```js
oddshawk.ws.connect();
oddshawk.ws.onUpdate((data) => { console.log(data); });
oddshawk.ws.onInitial((data) => { console.log(data); });
oddshawk.ws.onClose(() => { console.log('closed'); });
oddshawk.ws.subscribe({ sport: 'Horse Racing' });
```

Default endpoint: `wss://ws.odds.software`.

## Exports

```js
import { OddsHawk, generateHash, OddsHawkApiError } from '@oddshawk/oddshawk-sdk';
```

---
_Source: https://npm.io/package/@oddshawk/oddshawk-sdk · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
