arbplay

Odds HTTP API reference

The ArbPlay Odds API has two read-only JSON endpoints at https://api.arbplay.app: GET /v1/files lists the files your key can read, and GET /v1/data/{filename} returns one file with its updated_at time. Authenticate with X-API-Key, or with your marketplace key through RapidAPI, Zyla or JoJ API.

Base URL and authentication

Direct keys call https://api.arbplay.app with the header X-API-Key: YOUR_API_KEY. A key normally belongs to one bookmaker (its company key) and reads only that bookmaker's files; a direct key can also cover several bookmakers or a chosen set of files, and then company carries that bundle's name.

Marketplace subscribers call the marketplace's gateway instead, with the marketplace's own key. The endpoints and responses are the same:

MarketplaceYour key goes inBase URL and full path
RapidAPIX-RapidAPI-Key and X-RapidAPI-HostEndpoints tab of the listing
Zyla API HubAuthorization: Bearer <access key>Code sample on the listing
JoJ APIX-JoJAPI-Key (or Authorization: Bearer)Code sample on the listing

Keep keys server-side: the API sends no CORS headers, so browser calls fail by design.

GET /v1/files

Lists the files your key can read, sorted by name. Only files that exist right now are listed.

{
  "company": "stoiximan",
  "count": 5,
  "files": [
    "site_live_stoiximan.json",
    "site_pregame_stoiximan_basketball.json",
    "site_pregame_stoiximan_soccer.json",
    "site_pregame_stoiximan_soccer_leagues.json",
    "site_pregame_stoiximan_tennis.json"
  ]
}

GET /v1/data/{filename}

Returns one file inside a four-key envelope, always in this order:

KeyTypeMeaning
companystringCompany key of your API key (e.g. opapstore); a bundle key shows its own name
filestringThe filename you asked for
updated_atstring (ISO 8601, UTC)When the file was last written; also sent as Last-Modified
dataobject or arrayThe bookmaker's own JSON, byte-for-byte as scraped
{
  "company": "bwin",
  "file": "site_pregame_bwin_soccer.json",
  "updated_at": "2026-09-26T20:02:24Z",
  "data": {
    "fixtures": [
      "… Bwin's own JSON, unchanged …"
    ]
  }
}

The file is streamed from disk as-is — never parsed or re-serialized — so data is exactly what the scraper wrote. Files are replaced atomically, so a response is always one complete version of the file. Response headers: Content-Length, Last-Modified and Cache-Control: no-cache. See each bookmaker page for the structure inside data, or download a real sample.

Conditional requests (304)

Send the last Last-Modified value back as If-Modified-Since. Until the file is rewritten the API answers 304 Not Modified with no body. An invalid or future date is ignored. updated_at and Last-Modified have one-second resolution, and a 304 still counts toward your rate limit.

curl -sS -o /dev/null -w "%{http_code}\n" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "If-Modified-Since: Sat, 26 Sep 2026 20:02:34 GMT" \
  https://api.arbplay.app/v1/data/site_live_stoiximan.json
# 304 until the file is rewritten, then 200 with the new body

Compression

Send Accept-Encoding: gzip (curl --compressed; Python requests and Node fetch do it for you). Responses arrive about 6–7 times smaller (Fonbet's 8.6 MB file comes down as about 1.2 MB), which matters most for the largest files: Superbet pre-match soccer is around 200 MB uncompressed and Bwin pre-match soccer around 10 MB.

Errors

Every error body is {"detail": "<message>"}. Checks run in this order and the first failure wins — note that an unknown filename is 403, not 404.

StatusdetailCauseWhat to do
401Missing X-API-Key headerNo key sentSend X-API-Key (or the marketplace's header)
401Invalid API keyUnknown keyCheck the key; marketplace keys only work through that marketplace
429Rate limit exceededToo many requests on this pathWait Retry-After seconds
400Invalid filename.. or a path in the nameSend a bare filename
403Only site_*.json files are allowedName does not match site_*.jsonUse a name from /v1/files
403File is not mapped to any configured companyUnknown filenameUse a name from /v1/files
403Not allowed for this API keyFile is not among your key's filesSubscribe to that bookmaker's listing
404File not foundKnown file, temporarily missing on diskRetry later
500Data file is emptyZero-byte fileRetry later
500Invalid JSON fileFile is not a JSON documentRetry later

Rate limits

  • Limits are counted per key and per path: each filename is its own bucket, and /v1/files is another.
  • Marketplace traffic is limited to 10 requests per second per path at the origin; your plan's monthly quota is enforced by the marketplace.
  • A 429 carries Retry-After (seconds). Polling with If-Modified-Since at the file's refresh rate is the efficient pattern.
  • What counts as a request, and how to size a plan: Odds API FAQ.

Freshness

  • Live files are rewritten about every 1–3 s and pre-match files about every 4–7 s during active hours.

File catalogue

All 34 files. Each key reads the files of its own bookmaker, or of its bundle; GET /v1/files returns the current list.

FileBookmakerTypeSportRefresh
site_live_stoiximan.jsonStoiximanLiveAll sports≈1⁠–⁠3 s
site_pregame_stoiximan_soccer.jsonStoiximanPre-matchSoccer≈4⁠–⁠7 s
site_pregame_stoiximan_soccer_leagues.jsonStoiximanPre-matchSoccer (15 major leagues)≈4⁠–⁠7 s
site_pregame_stoiximan_basketball.jsonStoiximanPre-matchBasketball≈4⁠–⁠7 s
site_pregame_stoiximan_tennis.jsonStoiximanPre-matchTennis≈4⁠–⁠7 s
site_live_novibet.jsonNovibetLiveAll sports≈1⁠–⁠3 s
site_pregame_novibet_soccer.jsonNovibetPre-matchSoccer≈4⁠–⁠7 s
site_pregame_novibet_basketball.jsonNovibetPre-matchBasketball≈4⁠–⁠7 s
site_pregame_novibet_tennis.jsonNovibetPre-matchTennis≈4⁠–⁠7 s
site_livepre_fonbet.jsonFonbetLive + pre-matchAll sports≈1⁠–⁠3 s
site_live_superbet_soccer.jsonSuperbetLiveSoccer≈1⁠–⁠3 s
site_live_superbet_basketball.jsonSuperbetLiveBasketball≈1⁠–⁠3 s
site_live_superbet_tennis.jsonSuperbetLiveTennis≈1⁠–⁠3 s
site_pregame_superbet_soccer.jsonSuperbetPre-matchSoccer≈4⁠–⁠7 s
site_pregame_superbet_basketball.jsonSuperbetPre-matchBasketball≈4⁠–⁠7 s
site_pregame_superbet_tennis.jsonSuperbetPre-matchTennis≈4⁠–⁠7 s
site_live_elabet_soccer.jsonElabetLiveSoccer≈1⁠–⁠3 s
site_live_elabet_basketball.jsonElabetLiveBasketball≈1⁠–⁠3 s
site_live_elabet_tennis.jsonElabetLiveTennis≈1⁠–⁠3 s
site_pregame_elabet_soccer.jsonElabetPre-matchSoccer≈4⁠–⁠7 s
site_pregame_elabet_basketball.jsonElabetPre-matchBasketball≈4⁠–⁠7 s
site_pregame_elabet_tennis.jsonElabetPre-matchTennis≈4⁠–⁠7 s
site_live_opapstore_soccer.jsonOPAP (Allwyn)LiveSoccer≈1⁠–⁠3 s
site_live_opapstore_basketball.jsonOPAP (Allwyn)LiveBasketball≈1⁠–⁠3 s
site_live_opapstore_tennis.jsonOPAP (Allwyn)LiveTennis≈1⁠–⁠3 s
site_pregame_opapstore_soccer.jsonOPAP (Allwyn)Pre-matchSoccer≈4⁠–⁠7 s
site_pregame_opapstore_basketball.jsonOPAP (Allwyn)Pre-matchBasketball≈4⁠–⁠7 s
site_pregame_opapstore_tennis.jsonOPAP (Allwyn)Pre-matchTennis≈4⁠–⁠7 s
site_live_bwin_soccer.jsonBwinLiveSoccer≈1⁠–⁠3 s
site_live_bwin_basketball.jsonBwinLiveBasketball≈1⁠–⁠3 s
site_live_bwin_tennis.jsonBwinLiveTennis≈1⁠–⁠3 s
site_pregame_bwin_soccer.jsonBwinPre-matchSoccer≈4⁠–⁠7 s
site_pregame_bwin_basketball.jsonBwinPre-matchBasketball≈4⁠–⁠7 s
site_pregame_bwin_tennis.jsonBwinPre-matchTennis≈4⁠–⁠7 s

Code examples

Poll one file with If-Modified-Since (Python)

import time
import requests

URL = "https://api.arbplay.app/v1/data/site_live_novibet.json"
session = requests.Session()
session.headers["X-API-Key"] = "YOUR_API_KEY"
last_modified = None

while True:
    headers = {"If-Modified-Since": last_modified} if last_modified else {}
    resp = session.get(URL, headers=headers, timeout=60)
    if resp.status_code == 200:
        last_modified = resp.headers["Last-Modified"]
        payload = resp.json()  # {company, file, updated_at, data}
        print("file updated", payload["updated_at"])
    elif resp.status_code == 429:
        time.sleep(int(resp.headers.get("Retry-After", "1")))
        continue
    elif resp.status_code != 304:
        resp.raise_for_status()
    time.sleep(2)

List and download every file (Node 18+)

// Node 18+, server-side (no CORS)
const BASE = "https://api.arbplay.app";
const headers = { "X-API-Key": process.env.ARBPLAY_API_KEY };

const { files } = await (await fetch(`${BASE}/v1/files`, { headers })).json();
for (const file of files) {
  const res = await fetch(`${BASE}/v1/data/${file}`, { headers });
  if (!res.ok) { console.error(file, res.status, (await res.json()).detail); continue; }
  const { updated_at, data } = await res.json();
  console.log(file, updated_at);
}

Per-bookmaker parsers for match-result odds are on each bookmaker page.

Samples, OpenAPI and Postman

Frequently asked questions

Why do I get 403 instead of 404 for a filename?

The API only answers 404 for a known file that is missing on disk. A filename that does not match site_*.json, is not in any bookmaker's list, or is not among your key's files returns 403 with a detail message. Call GET /v1/files to see the exact names your key can read.

What does updated_at mean?

The time the file was last written, in UTC with one-second resolution. It is the freshness of the whole file, not of each price inside it. The same time is sent as the Last-Modified header.

How should I poll?

Send the last Last-Modified value as If-Modified-Since. You get 304 with no body until the file changes. Polling a live file every 1 to 3 seconds or a pre-match file every 4 to 7 seconds is enough; a 304 still counts toward your rate limit.

Can I call the API from a browser?

No. The API sends no CORS headers and your key must stay secret, so call it from a server, script or backend function and pass the results to your front end.

Are responses compressed?

Send Accept-Encoding: gzip (curl --compressed; requests and most HTTP clients do this by default). JSON arrives about 6-7 times smaller (Fonbet's 8.6 MB file as about 1.2 MB), which matters for the multi-megabyte pre-match files.

Bookmaker pages: Stoiximan · Novibet · Fonbet · Superbet · Elabet · OPAP (Allwyn) · Bwin.
api.arbplay.app is the Odds HTTP API. It is separate from the ArbPlay app's own endpoints under arbplay.app/api/* (sign-in, the arbitrage feed), which are not a public API.

Ready to build?

Pick a bookmaker listing on RapidAPI, Zyla API Hub or JoJ API