{
  "openapi": "3.0.3",
  "info": {
    "title": "TipsAudit API",
    "version": "1.0",
    "description": "Free, read-only JSON API. Fair odds for upcoming football matches (the sharpest market's price with the bookmaker margin removed), bookmaker margins measured on real odds, and the live value-flag record. No key, CORS enabled, 60 requests per minute. Free with attribution (a link to tipsaudit.com). Not betting advice. Docs: https://tipsaudit.com/api",
    "contact": { "name": "TipsAudit", "url": "https://tipsaudit.com/contact", "email": "contact@tipsaudit.com" },
    "license": { "name": "Free with attribution (link to tipsaudit.com)", "url": "https://tipsaudit.com/api" }
  },
  "externalDocs": { "description": "Methodology", "url": "https://tipsaudit.com/methodology" },
  "servers": [{ "url": "https://tipsaudit.com/api/v1" }],
  "paths": {
    "/predictions": {
      "get": {
        "operationId": "getPredictions",
        "summary": "Upcoming matches with fair odds",
        "description": "Every upcoming match we price, with the fair 1X2 odds and probabilities (Pinnacle's current price with the margin removed, or the recorded price when no fresh read exists: see fair_source), the value status and, when a soft bookmaker pays above the fair price, the price gap. Cached for 5 minutes.",
        "responses": {
          "200": {
            "description": "Upcoming matches",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Predictions" } } }
          },
          "429": { "description": "Too many requests (60 per minute)" }
        }
      }
    },
    "/margins": {
      "get": {
        "operationId": "getMargins",
        "summary": "Bookmaker margins (1X2), 30-day window",
        "description": "The average 1X2 margin of each bookmaker, measured from odds snapshots over the last 30 days, and the average margin per league with the lowest-margin bookmaker. Same data as https://tipsaudit.com/bookmaker-margins.",
        "responses": {
          "200": {
            "description": "Margin index",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Margins" } } }
          },
          "429": { "description": "Too many requests (60 per minute)" }
        }
      }
    },
    "/badge": {
      "get": {
        "operationId": "getBadge",
        "summary": "Live value-flag record (headline numbers)",
        "description": "The headline numbers of the value flags recorded live: count, average closing line value (CLV) with its 95% interval from 30 results, and ROI from 100 settled. The reconstructed backtest is returned separately and labelled.",
        "responses": {
          "200": {
            "description": "Record summary",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Badge" } } }
          },
          "429": { "description": "Too many requests (120 per minute)" }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Odds3": {
        "type": "object",
        "properties": {
          "home": { "type": "number", "example": 2.43 },
          "draw": { "type": "number", "example": 3.67 },
          "away": { "type": "number", "example": 3.17 }
        }
      },
      "Prediction": {
        "type": "object",
        "properties": {
          "id": { "type": "integer", "example": 14109 },
          "league": { "type": "string", "example": "Serbia Super Liga" },
          "home": { "type": "string", "example": "Radnicki 1923" },
          "away": { "type": "string", "example": "Železničar Pančevo" },
          "kickoff_utc": { "type": "string", "format": "date-time" },
          "fair_odds": { "allOf": [{ "$ref": "#/components/schemas/Odds3" }], "nullable": true },
          "fair_probabilities": { "allOf": [{ "$ref": "#/components/schemas/Odds3" }], "nullable": true },
          "fair_source": { "type": "string", "description": "live = the current benchmark read; otherwise the price recorded when the match was logged", "example": "live" },
          "fair_read_at": { "type": "string", "format": "date-time", "nullable": true },
          "status": { "type": "string", "enum": ["actionable", "monitor", "expired", "unconfirmed", "none"] },
          "value": {
            "type": "object", "nullable": true,
            "description": "Present when a soft bookmaker currently pays above the fair price. price_gap = soft odds / fair odds - 1 (a price comparison, not an expected profit); edge = the same number under its old name.",
            "properties": {
              "selection": { "type": "string", "enum": ["1", "X", "2"] },
              "best_soft_odds": { "type": "number" },
              "bookmaker": { "type": "string" },
              "fair_odds": { "type": "number" },
              "price_gap": { "type": "number" },
              "edge": { "type": "number" }
            }
          },
          "detected": {
            "type": "object", "nullable": true,
            "description": "The value flag as recorded (stays visible after it expires). origin: live or replay.",
            "properties": {
              "selection": { "type": "string" },
              "odds": { "type": "number" },
              "price_gap": { "type": "number" },
              "edge": { "type": "number" },
              "at": { "type": "string", "format": "date-time" },
              "origin": { "type": "string", "enum": ["live", "replay"] }
            }
          },
          "value_available": { "type": "boolean", "description": "Only when the paid tier is on: whether a value flag exists (its details are then not public)." },
          "benchmark_read_at": { "type": "string", "format": "date-time", "nullable": true },
          "url": { "type": "string", "format": "uri" }
        }
      },
      "Predictions": {
        "type": "object",
        "properties": {
          "source": { "type": "string" },
          "license": { "type": "string" },
          "docs": { "type": "string", "format": "uri" },
          "generated_at": { "type": "string", "format": "date-time" },
          "methodology_version": { "type": "string", "example": "2.0" },
          "count": { "type": "integer" },
          "counts_by_status": {
            "type": "object",
            "properties": {
              "actionable": { "type": "integer" }, "monitor": { "type": "integer" }, "expired": { "type": "integer" },
              "unconfirmed": { "type": "integer" }, "none": { "type": "integer" }
            }
          },
          "predictions": { "type": "array", "items": { "$ref": "#/components/schemas/Prediction" } }
        }
      },
      "Margins": {
        "type": "object",
        "properties": {
          "source": { "type": "string" },
          "license": { "type": "string" },
          "docs": { "type": "string", "format": "uri" },
          "generated_at": { "type": "string" },
          "matches_sampled": { "type": "integer" },
          "bookmakers": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": { "type": "string" },
                "sharp": { "type": "boolean" },
                "type": { "type": "string", "enum": ["soft", "sharp", "exchange"] },
                "avg_margin": { "type": "number", "example": 0.0579 },
                "matches": { "type": "integer" },
                "url": { "type": "string", "format": "uri", "nullable": true }
              }
            }
          },
          "leagues": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "league": { "type": "string" },
                "avg_margin": { "type": "number" },
                "matches": { "type": "integer" },
                "lowest_margin_book": {
                  "type": "object",
                  "properties": { "name": { "type": "string" }, "avg_margin": { "type": "number" } }
                }
              }
            }
          }
        }
      },
      "Badge": {
        "type": "object",
        "properties": {
          "value_bets": { "type": "integer" },
          "avg_clv": { "type": "number", "nullable": true, "description": "Average CLV in %, live flags" },
          "avg_clv_ci95": { "type": "number", "nullable": true, "description": "Half-width of the 95% interval, from 30 results" },
          "avg_clv_significant": { "type": "boolean" },
          "roi": { "type": "number", "nullable": true, "description": "ROI in %, from 100 settled" },
          "live_since": { "type": "string", "format": "date" },
          "backtest": {
            "type": "object",
            "properties": {
              "value_bets": { "type": "integer" }, "avg_clv": { "type": "number", "nullable": true },
              "roi": { "type": "number", "nullable": true }, "note": { "type": "string" }
            }
          },
          "url": { "type": "string", "format": "uri" }
        }
      }
    }
  }
}
