HaterPicks API docs

One versioned endpoint for Australian sports odds across every major bookmaker, deduplicated and freshness-filtered. Odds are decimal and times are ISO 8601 UTC.

Quickstart

Base URL https://www.haterpicks.app. Send your key as a bearer token (Authorization: Bearer hp_live_…) or in the x-api-key header. Keys start hp_live_. Keep your key on your server: anyone holding it spends your allowance.

Request

curl --compressed "https://www.haterpicks.app/api/v1/odds?sport=nba" \
  -H "Authorization: Bearer hp_live_your_key_here"

Response (trimmed)

{
  "version": "v1",
  "feed": "odds",
  "generated_at": "2026-10-09T04:15:00.000Z",
  "count": 214,
  "data": [
    {
      "id": "nba-nba-total-…",
      "sport": "NBA",
      "market": "NBA_TOTAL",
      "player_or_team": "Boston Celtics",
      "away_team": "New York Knicks",
      "matchup": "Boston Celtics v New York Knicks",
      "line": 221.5,
      "game_start_iso": "2026-10-21T23:30:00Z",
      "prices": {
        "sportsbet": { "over": 1.9, "under": 1.9, "over_link": "https://…", "under_link": "https://…" },
        "ladbrokes": { "over": 1.88, "under": 1.92, "over_link": "https://…", "under_link": "https://…" },
        "tab": { "over": 1.87, "under": 1.93, "over_link": "https://…", "under_link": "https://…" },
        "pointsbet": { "over": 1.91, "under": 1.89, "over_link": "https://…", "under_link": "https://…" }
      }
    }
  ]
}

The odds feed is sent gzipped to any client that accepts it. Browsers and most HTTP libraries do this for you; with curl, add --compressed.

Endpoints

GET/api/v1/odds

Live odds across every major Australian bookmaker, one row per market and line, deduplicated with stale prices dropped. Filter with ?sport=. Game lines on every plan; player props from Starter.

Free (game lines; props on Starter+)
GET/api/v1/props?sport=afl

Every player-prop market for one sport, every bookmaker, every line.

Starter+
GET/api/v1/ev

Player-prop prices with our model's fair probability, fair price and edge %, with the best bookmaker and price.

Pro+
GET/api/v1/arbs

Cross-bookmaker arbitrage opportunities with the price and bookmaker for each leg.

Pro+
POST/api/v1/sgm-quote

Price a same-game multi of two or more legs across bookmakers, with a fair quote.

Pro+

GET /api/v1 lists every endpoint without a key. The full contract is the OpenAPI 3.1 specification.

Filter by sport

GET /api/v1/odds takes an optional sport query parameter: the row's sport field, for example AFL, NRL, NBL, NBA, NFL, SOCCER or RACING (any case). Leave it off for every sport. A filtered call is still one call, and a much smaller response.

  • sport=AFL
  • sport=NRL
  • sport=NBL
  • sport=NBA
  • sport=NFL
  • sport=SOCCER
  • sport=RACING

Response envelope

Every response shares one versioned envelope. count is the number of rows in data. On the Free plan the odds feed adds filtered: "game-lines-only".

{
  "version": "v1",
  "feed": "odds",
  "generated_at": "2026-10-09T04:15:00.000Z",
  "count": 214,
  "data": [ … ]
}

Errors

Errors carry error in plain English and the matching HTTP status.

StatusWhenBody
400A parameter is missing or invalid.{ "error": "…" }
401The key is missing, invalid or revoked.{ "error": "…" }
403Your plan doesn't include this feed. required_tier names the cheapest plan that does.{ "error": "…", "upgrade_required": true, "required_tier": "pro" }
429Per-minute limit hit. Wait the number of seconds in the Retry-After header.{ "error": "…" }
429Monthly allowance used. Calls resume at resets_at (the 1st of next month, UTC), or upgrade from /developer.{ "error": "…", "quota_exceeded": true, "resets_at": "2026-11-01T00:00:00Z" }
502The upstream feed was unavailable. Safe to retry.{ "error": "…" }

Rate-limit and quota headers

Every keyed response tells you where you stand against your per-minute limit and your monthly allowance, so you can pace calls without waiting for a 429.

RateLimit-PolicyYour per-minute limit, as "minute";q=<requests>;w=60 (IETF RateLimit header fields draft).
RateLimitWhat is left of it, as "minute";r=<remaining>;t=<seconds until a request frees up>.
X-RateLimit-LimitRequests allowed per minute.
X-RateLimit-RemainingRequests left this minute.
X-RateLimit-ResetSeconds until a request frees up.
Retry-AfterOn a 429 only: seconds to wait before trying again.
X-Quota-LimitCalls your plan allows this month.
X-Quota-RemainingCalls left this month.
X-Quota-ResetWhen the allowance resets, as an ISO date (the 1st of next month, UTC).

Versioning and deprecation

The version is in the path: every endpoint lives under /api/v1, and every response says "version": "v1".

Within v1 we only add. New endpoints, new optional parameters and new response fields can appear at any time, so ignore fields you do not recognise. We never remove, rename or change the meaning of a field, parameter or endpoint inside v1.

A breaking change ships as a new version (/api/v2) beside the old one. Key holders are emailed when it is announced.

From that announcement, responses from the old version carry a Deprecation header (RFC 9745) and a Sunset header (RFC 8594) giving the date it stops answering, at least 6 months later, plus a Link header pointing here.

Plans and keys

Every plan, its allowance and its price is on the pricing page. Create a key, see your usage and change plan at /developer.

Get your key