DocsGuidesView as Markdown

Example client

A complete client in cURL, Python and JavaScript: quote, call, read the token headers and poll with ETags.

Before you start

You need an API key. Sign up free: your first key is shown once when you finish. Save it as the environment variable ODDSRELAY_KEY, and keep it out of code, query strings and anything that runs in a browser.

The whole flow in three languages

Each sample makes one small call on the standard product: football 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}"
# Football 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 product 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.

From the blog

Every parameter: API reference.