Skip to content

DocsGuides

Quickstart

Make your first small call, price the big ones first and poll without paying twice.

Also as Markdown.

Before you start

You need a key. Access is by request today, and the key arrives by email. Put it in an environment variable called ODDSRELAY_KEY and never in code, a query string or a browser bundle.

The whole flow in three languages

Each sample makes one small call on the standard board: soccer match odds at one bookmaker in the UK & Ireland. It prices the call first with a free quote, makes it with gzip, reads the token headers, then polls again with the ETag. It backs off on a 429 and stops on a 402. Each is tested against the live API.

#!/usr/bin/env bash
# OddsRelay API sample, cURL. Run: ODDSRELAY_KEY=or_live_… bash oddsrelay.sh
#
# One small filtered call on the standard board, the way every client should make it:
#   1. price it first with a free quote=true;
#   2. make it with --compressed (gzip; standard refuses a call without it) and read the token headers;
#   3. poll it again with If-None-Match, where an unchanged board answers 304, free;
#   4. back off on 429 (Retry-After) and stop on 402 (out of tokens).
set -euo pipefail

BASE="https://api.oddsrelay.io"
: "${ODDSRELAY_KEY:?set ODDSRELAY_KEY}"
# Soccer match odds at one bookmaker, UK & Ireland: a small call.
URL="$BASE/v2/odds/standard?region=uk&sports=soccer&markets=h2h&bookmakers=ladbrokes"
MAX_TOKENS=1000

tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT

# call URL [extra header]: writes headers to $tmp/h, body to $tmp/b, prints the status. Retries 429s.
call() {
  local status extra=()
  [ -n "${2:-}" ] && extra=(-H "$2")
  for _ in 1 2 3; do
    status=$(curl --compressed -sS -o "$tmp/b" -D "$tmp/h" -w '%{http_code}' \
      -H "Authorization: Bearer $ODDSRELAY_KEY" ${extra[@]+"${extra[@]}"} "$1")
    if [ "$status" = 429 ]; then
      # The front door's own 429 has an empty body; both carry Retry-After.
      wait=$(awk -F': ' 'tolower($1)=="retry-after"{print $2+0}' "$tmp/h")
      echo "429: waiting ${wait:-1} s" >&2
      sleep "${wait:-1}"
      continue
    fi
    if [ "$status" = 402 ]; then
      echo "402: out of tokens: $(cat "$tmp/b")" >&2
      exit 1
    fi
    echo "$status"
    return
  done
  echo "still rate-limited after 3 tries" >&2
  exit 1
}

header() { awk -F': ' -v k="$(echo "$1" | tr 'A-Z' 'a-z')" 'tolower($1)==k{sub(/\r$/,"",$2); print $2}' "$tmp/h"; }
tokens() { echo "cost=$(header X-Tokens-Cost) remaining=$(header X-Tokens-Remaining) reset=$(header X-Tokens-Reset)"; }

# 1. Quote: free.
[ "$(call "$URL&quote=true")" = 200 ] || { echo "quote failed: $(cat "$tmp/b")" >&2; exit 1; }
cost=$(sed -E 's/.*"cost": *([0-9]+).*/\1/' "$tmp/b")
echo "quote: $cost tokens"
[ "$cost" -le "$MAX_TOKENS" ] || { echo "quote $cost is above $MAX_TOKENS: narrow the filters" >&2; exit 1; }

# 2. The call.
[ "$(call "$URL")" = 200 ] || { echo "call failed: $(head -c 200 "$tmp/b")" >&2; exit 1; }
etag=$(header ETag)
echo "200: $(tokens)"

# 3. Poll with the ETag: 304 if nothing changed (free), else a fresh 200.
status=$(call "$URL" "If-None-Match: $etag")
case "$status" in 200|304) ;; *) echo "poll answered $status" >&2; exit 1 ;; esac
echo "$status on poll: $(tokens)"
echo "ok"

What to change first

Swap the board in the path, and the filters in the query, for the data you need. Keep region on every call, keep gzip on, and keep the quote step for any call you haven't priced before.

Every statement here follows the published contract. The API reference has the full detail.