arbplay

OPAP Odds API (Allwyn / OPAP Store)

The ArbPlay OPAP odds API serves the Allwyn store offer's own JSON over HTTPS in six files: live and pre-match for soccer, basketball and tennis. Every event carries a Betradar id, and pre-match soccer reaches about 2 weeks ahead. Live odds refresh about every 1–3 seconds, pre-match about every 4–7.

OPAP, part of the Allwyn group, is Greece's national betting operator. The data is the Allwyn store offer (formerly OPAP Store): the odds you bet on in an Allwyn shop, with each pre-match event's store code. OPAP's separate online sportsbook is not part of this API. The marketplace listings are named Allwyn Odds and OPAP Store Odds API.

Key facts

OPAP (Allwyn) odds API at a glance.
BookmakerOPAP (Allwyn group), Greece
Company keyopapstore (listings: "Allwyn Odds", "OPAP Store Odds API")
Files6: site_{live,pregame}_opapstore_{soccer,basketball,tennis}.json
Pre-match coverageSoccer about 2 weeks ahead; basketball about 1 week; tennis 1–2 days
RefreshLive about every 1–3 s, pre-match about every 4–7 s
Odds formatDecimal (prices[].decimal) plus fractional (numerator/denominator)
Cross-bookmaker idBetradar: externalIds[] with provider: "betradar" (sr:match:<id>) — on every event
Schemadata.events[].markets[].outcomes[].prices[]
Market groupsMATCH_RESULT, TOTAL_GOALS_OVER/UNDER, DOUBLE_CHANCE, BOTH_TEAMS_TO_SCORE, NO_BET_DRAW, ASIAN_HANDICAP, CORNERS_OVER/UNDER…
AccessRapidAPI, Zyla API Hub or JoJ API listing, or a direct X-API-Key from ArbPlay

Files and what's inside

Every file is OPAP's own JSON, served unchanged inside the API envelope. Refresh: live files about every 1–3 s, pre-match files about every 4–7 s.

FileWhat's insideRefresh
site_live_opapstore_soccer.jsonLive soccer with match result, totals, BTTS, corners and draw no bet≈1⁠–⁠3 s
site_live_opapstore_basketball.jsonLive basketball≈1⁠–⁠3 s
site_live_opapstore_tennis.jsonLive tennis≈1⁠–⁠3 s
site_pregame_opapstore_soccer.jsonPre-match soccer — match result, totals, double chance, BTTS, half-time, Asian handicap, corners≈4⁠–⁠7 s
site_pregame_opapstore_basketball.jsonPre-match basketball≈4⁠–⁠7 s
site_pregame_opapstore_tennis.jsonPre-match tennis≈4⁠–⁠7 s

Pre-match markets by sport

SportMarkets
SoccerMatch result, half-time result, total goals lines, double chance, both teams to score, draw no bet, Asian handicap, corners
BasketballMoney line, point spread lines, total points lines
TennisMatch winner, set winner, game handicap

Every bookmaker compared: sports and markets per bookmaker.

Field map: where each value lives

Paths start at data.data.events[] (the file has its own data key).

ValuePath
Event idevents[].id
Nameevents[].name ("Home v Away")
Teamsevents[].teams[].name with side HOME / AWAY
Leagueevents[].type.name
Country / regionevents[].class.name
Sportevents[].category.name
Start timeevents[].startTime (ISO)
In playevents[].liveNow
Marketmarkets[].name (line included, e.g. "Total Goals Over / Under 2.5"), .groupCode
Selection, pricemarkets[].outcomes[].name, .subType (H/D/A in match result) → prices[0].decimal
Betradar idevents[].externalIds[] where provider == "betradar" → id
Statusstatus ACTIVE / SUSPENDED and active on events, markets and outcomes
Store code (pre-match)events[].retailCode: the code a shop uses to find the event; not on every event

Real sample

An abridged record from site_pregame_opapstore_soccer.json: real values, most fields left out so the structure is readable. The downloads below keep every field.

{
  "data": {
    "events": [
      {
        "id": "12562240",
        "name": "Wydad Casablanca v Wydad Temara",
        "startTime": "2026-09-26T20:00:00Z",
        "liveNow": false,
        "category": {
          "name": "Football"
        },
        "class": {
          "name": "Morocco"
        },
        "type": {
          "name": "Botola"
        },
        "teams": [
          {
            "id": null,
            "name": "Wydad Casablanca",
            "side": "HOME"
          },
          {
            "id": null,
            "name": "Wydad Temara",
            "side": "AWAY"
          }
        ],
        "externalIds": [
          {
            "id": "sr:match:74829634",
            "provider": "betradar"
          }
        ],
        "markets": [
          {
            "name": "Match Result",
            "groupCode": "MATCH_RESULT",
            "outcomes": [
              {
                "name": "Wydad Temara",
                "subType": "A",
                "prices": [
                  {
                    "decimal": 6.0,
                    "numerator": 5,
                    "denominator": 1
                  }
                ]
              },
              {
                "name": "Wydad Casablanca",
                "subType": "H",
                "prices": [
                  {
                    "decimal": 1.51,
                    "numerator": 51,
                    "denominator": 100
                  }
                ]
              },
              {
                "name": "Draw",
                "subType": "D",
                "prices": [
                  {
                    "decimal": 3.65,
                    "numerator": 53,
                    "denominator": 20
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
}

Each download is a complete GET /v1/data/{filename} response cut down to a few events, with every id reference still resolving. All samples are listed in samples/index.json.

Extract match-result (1X2) odds in Python

This function is tested against the samples on this page. Pass it the parsed JSON response.

def match_result(payload):
    """(event, 1, X, 2) rows from an OPAP Store (Allwyn) file."""
    rows = []
    for ev in payload["data"].get("data", {}).get("events", []):
        for market in ev.get("markets", []):
            if market.get("groupCode") == "MATCH_RESULT":
                # outcome subType: H = home, D = draw, A = away (order is not fixed)
                odds = {o["subType"]: o["prices"][0]["decimal"] for o in market["outcomes"] if o.get("prices")}
                rows.append((ev["name"], odds.get("H"), odds.get("D"), odds.get("A")))
    return rows

Fetch OPAP odds

With a direct key

# 1. which files can this key read?
curl -sS --compressed -H "X-API-Key: YOUR_API_KEY" \
  https://api.arbplay.app/v1/files

# 2. download one file (JSON envelope: company, file, updated_at, data)
curl -sS --compressed -H "X-API-Key: YOUR_API_KEY" \
  https://api.arbplay.app/v1/data/site_pregame_opapstore_soccer.json -o site_pregame_opapstore_soccer.json
import requests  # pip install requests

resp = requests.get(
    "https://api.arbplay.app/v1/data/site_pregame_opapstore_soccer.json",
    headers={"X-API-Key": "YOUR_API_KEY"},  # gzip is negotiated automatically
    timeout=60,
)
resp.raise_for_status()
payload = resp.json()
print(payload["updated_at"], payload["file"])
for event, home, draw, away in match_result(payload):  # the function above
    print(f"{event}: {home} / {draw} / {away}")
// Node 18+ (server-side: the API sends no CORS headers)
const res = await fetch("https://api.arbplay.app/v1/data/site_pregame_opapstore_soccer.json", {
  headers: { "X-API-Key": process.env.ARBPLAY_API_KEY },
});
if (!res.ok) throw new Error(`${res.status} ${(await res.json()).detail}`);
const { company, file, updated_at, data } = await res.json();
console.log(file, "updated", updated_at);

Through a marketplace

On RapidAPI, Zyla API Hub or JoJ API you call the marketplace's gateway with that marketplace's key. The two endpoints and the JSON they return are the same; copy the base URL and path from the listing.

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

Freshness, gaps and caveats

  • Refresh: live files about every 1–3 s, pre-match files about every 4–7 s. Every response carries updated_at and a Last-Modified header; send If-Modified-Since to get 304 Not Modified when nothing changed.
  • Not every event carries every market: many pre-match events list only totals and other markets without a MATCH_RESULT market — check groupCode.
  • The line is part of the market name ("Total Goals Over / Under 2.5"). handicapValue equals the line for totals, but Asian handicaps use quarter-goal units there (a −1.25 line is -5.0) — read the line from the name.
  • Outcome order inside a market is not fixed — read subType (H, D, A) instead of the position.

Get OPAP odds API access

Plans and quotas are set on each marketplace listing. More in the API reference and the API hub.

Frequently asked questions

Is this the official OPAP (Allwyn) API?

No. ArbPlay is independent and is not affiliated with or endorsed by OPAP (Allwyn) or its operator. It collects the odds OPAP (Allwyn) publishes on its public website and serves them unchanged as JSON for informational use. ArbPlay does not place bets.

Why is the OPAP odds API called Allwyn or OPAP Store?

OPAP is part of the Allwyn group, and the files carry the Allwyn store offer, formerly OPAP Store: the odds in Allwyn shops. The marketplace listings are named Allwyn Odds and OPAP Store Odds API; both serve the same files, whose company key is opapstore.

Which OPAP files can I download?

Six: site_live_opapstore_soccer.json, site_live_opapstore_basketball.json, site_live_opapstore_tennis.json and the matching site_pregame_opapstore_*.json files. Call GET /v1/files to list the files your key can read.

How often are OPAP odds updated?

During active hours live files are rewritten about every 1 to 3 seconds and pre-match files about every 4 to 7 seconds. Every response carries updated_at and a Last-Modified header, and If-Modified-Since returns 304 when nothing changed.

Can I match OPAP events with other bookmakers?

Yes. Every event lists a Betradar id in externalIds (provider betradar, value sr:match:<id>). Stoiximan, Novibet, Superbet and Bwin expose the same id, so you can join on it.

How do I get access to the OPAP (Allwyn) odds API?

Subscribe to the OPAP (Allwyn) listing on RapidAPI, Zyla API Hub or JoJ API and call it with that marketplace's key; plans and quotas are set on each listing. For a direct key to https://api.arbplay.app (header X-API-Key), contact ArbPlay.

ArbPlay is independent and not affiliated with or endorsed by OPAP (Allwyn) or its operator. Odds are collected from publicly available bookmaker odds and served unchanged for informational use; ArbPlay does not place bets. 21+.
Other bookmakers: Stoiximan · Novibet · Fonbet · Superbet · Elabet · Bwin. Machine-readable: /openapi.json · /llms.txt. Guide: Greek bookmaker odds API.

Build on OPAP odds

RapidAPI · Zyla API Hub · JoJ API · direct key