clutchapi 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- clutchapi-0.1.0/PKG-INFO +71 -0
- clutchapi-0.1.0/README.md +56 -0
- clutchapi-0.1.0/clutchapi/__init__.py +184 -0
- clutchapi-0.1.0/clutchapi.egg-info/PKG-INFO +71 -0
- clutchapi-0.1.0/clutchapi.egg-info/SOURCES.txt +7 -0
- clutchapi-0.1.0/clutchapi.egg-info/dependency_links.txt +1 -0
- clutchapi-0.1.0/clutchapi.egg-info/top_level.txt +1 -0
- clutchapi-0.1.0/pyproject.toml +25 -0
- clutchapi-0.1.0/setup.cfg +4 -0
clutchapi-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: clutchapi
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official client for the CLUTCH sports odds API: live odds and player props from 30+ books, line movement, closing lines, CLV, arbitrage and webhooks.
|
|
5
|
+
Author: Cooper Tech Solutions LLC
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://api.clutchbets.online
|
|
8
|
+
Project-URL: Documentation, https://api.clutchbets.online/docs
|
|
9
|
+
Keywords: sports betting,odds api,player props,sportsbook,nfl odds,nba odds,line movement,closing line value,arbitrage
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Requires-Python: >=3.8
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# clutchapi
|
|
17
|
+
|
|
18
|
+
The official Python client for the [CLUTCH API](https://api.clutchbets.online): live odds and player props from 30+ sportsbooks, line movement, closing lines, CLV grading, arbitrage and webhooks. Standard library only.
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install clutchapi
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from clutchapi import Clutch
|
|
26
|
+
|
|
27
|
+
clutch = Clutch("ck_live_...") # or set CLUTCH_API_KEY
|
|
28
|
+
|
|
29
|
+
nfl = clutch.odds("nfl")
|
|
30
|
+
props = clutch.props("nba", market="points")
|
|
31
|
+
steam = clutch.steam(sport="nfl")
|
|
32
|
+
close = clutch.closing(sport="nfl", game_key="...", market="spread")
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Get a key at [api.clutchbets.online/console](https://api.clutchbets.online/console).
|
|
36
|
+
|
|
37
|
+
## Live stream
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
for event, data in clutch.stream(sport="nba"):
|
|
41
|
+
print(event, data)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Line-move webhooks
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
out = clutch.alerts.create(channel="webhook", url="https://your.app/hooks/clutch",
|
|
48
|
+
book="pinnacle", sport="nfl", min_move=1)
|
|
49
|
+
secret = out["secret"] # shown once, save it
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Verify each delivery (Flask example, raw body):
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from clutchapi import verify_webhook
|
|
56
|
+
|
|
57
|
+
@app.post("/hooks/clutch")
|
|
58
|
+
def hook():
|
|
59
|
+
if not verify_webhook(request.get_data(), request.headers.get("X-Clutch-Signature"), SECRET):
|
|
60
|
+
return "", 401
|
|
61
|
+
moves = request.get_json()["moves"]
|
|
62
|
+
return "", 200
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Errors and limits
|
|
66
|
+
|
|
67
|
+
Failed requests raise `ClutchError` with `.status` and `.body`. After every call, `clutch.last_limits` holds your rate-limit and monthly-quota headers.
|
|
68
|
+
|
|
69
|
+
Any endpoint is reachable directly with `clutch.get("/api/v1/...", {...})`. Full reference: [api.clutchbets.online/docs](https://api.clutchbets.online/docs).
|
|
70
|
+
|
|
71
|
+
MIT © Cooper Tech Solutions LLC
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# clutchapi
|
|
2
|
+
|
|
3
|
+
The official Python client for the [CLUTCH API](https://api.clutchbets.online): live odds and player props from 30+ sportsbooks, line movement, closing lines, CLV grading, arbitrage and webhooks. Standard library only.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install clutchapi
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from clutchapi import Clutch
|
|
11
|
+
|
|
12
|
+
clutch = Clutch("ck_live_...") # or set CLUTCH_API_KEY
|
|
13
|
+
|
|
14
|
+
nfl = clutch.odds("nfl")
|
|
15
|
+
props = clutch.props("nba", market="points")
|
|
16
|
+
steam = clutch.steam(sport="nfl")
|
|
17
|
+
close = clutch.closing(sport="nfl", game_key="...", market="spread")
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Get a key at [api.clutchbets.online/console](https://api.clutchbets.online/console).
|
|
21
|
+
|
|
22
|
+
## Live stream
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
for event, data in clutch.stream(sport="nba"):
|
|
26
|
+
print(event, data)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Line-move webhooks
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
out = clutch.alerts.create(channel="webhook", url="https://your.app/hooks/clutch",
|
|
33
|
+
book="pinnacle", sport="nfl", min_move=1)
|
|
34
|
+
secret = out["secret"] # shown once, save it
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Verify each delivery (Flask example, raw body):
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from clutchapi import verify_webhook
|
|
41
|
+
|
|
42
|
+
@app.post("/hooks/clutch")
|
|
43
|
+
def hook():
|
|
44
|
+
if not verify_webhook(request.get_data(), request.headers.get("X-Clutch-Signature"), SECRET):
|
|
45
|
+
return "", 401
|
|
46
|
+
moves = request.get_json()["moves"]
|
|
47
|
+
return "", 200
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Errors and limits
|
|
51
|
+
|
|
52
|
+
Failed requests raise `ClutchError` with `.status` and `.body`. After every call, `clutch.last_limits` holds your rate-limit and monthly-quota headers.
|
|
53
|
+
|
|
54
|
+
Any endpoint is reachable directly with `clutch.get("/api/v1/...", {...})`. Full reference: [api.clutchbets.online/docs](https://api.clutchbets.online/docs).
|
|
55
|
+
|
|
56
|
+
MIT © Cooper Tech Solutions LLC
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""Official Python client for the CLUTCH API (https://api.clutchbets.online).
|
|
2
|
+
|
|
3
|
+
Standard library only. Python 3.8+.
|
|
4
|
+
|
|
5
|
+
from clutchapi import Clutch
|
|
6
|
+
clutch = Clutch("ck_live_...") # or set CLUTCH_API_KEY
|
|
7
|
+
nfl = clutch.odds("nfl")
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import hashlib
|
|
11
|
+
import hmac
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import urllib.error
|
|
15
|
+
import urllib.parse
|
|
16
|
+
import urllib.request
|
|
17
|
+
|
|
18
|
+
__version__ = "0.1.0"
|
|
19
|
+
__all__ = ["Clutch", "ClutchError", "verify_webhook"]
|
|
20
|
+
|
|
21
|
+
DEFAULT_BASE = "https://api.clutchbets.online"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ClutchError(Exception):
|
|
25
|
+
def __init__(self, message, status=None, body=None):
|
|
26
|
+
super().__init__(message)
|
|
27
|
+
self.status = status
|
|
28
|
+
self.body = body
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class _Alerts:
|
|
32
|
+
def __init__(self, client):
|
|
33
|
+
self._c = client
|
|
34
|
+
|
|
35
|
+
def list(self):
|
|
36
|
+
return self._c.get("/api/v1/alerts")["alerts"]
|
|
37
|
+
|
|
38
|
+
def create(self, **rule):
|
|
39
|
+
"""channel='webhook'|'email', url=, book=, sport=, player=, market=,
|
|
40
|
+
min_move=1, min_odds_move=, event='line_move'|'sheet_drop', name=.
|
|
41
|
+
Returns {'rule': ..., 'secret': 'whsec_...'} — the secret is shown once."""
|
|
42
|
+
return self._c.request("POST", "/api/v1/alerts", body=rule)
|
|
43
|
+
|
|
44
|
+
def update(self, alert_id, **patch):
|
|
45
|
+
return self._c.request("PATCH", f"/api/v1/alerts/{alert_id}", body=patch)["alert"]
|
|
46
|
+
|
|
47
|
+
def delete(self, alert_id):
|
|
48
|
+
return self._c.request("DELETE", f"/api/v1/alerts/{alert_id}")
|
|
49
|
+
|
|
50
|
+
def test(self, alert_id):
|
|
51
|
+
return self._c.request("POST", f"/api/v1/alerts/{alert_id}/test")
|
|
52
|
+
|
|
53
|
+
def deliveries(self, alert_id):
|
|
54
|
+
return self._c.get(f"/api/v1/alerts/{alert_id}/deliveries")["deliveries"]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class Clutch:
|
|
58
|
+
def __init__(self, api_key=None, base_url=DEFAULT_BASE, timeout=30):
|
|
59
|
+
self.api_key = api_key or os.environ.get("CLUTCH_API_KEY")
|
|
60
|
+
self.base_url = base_url.rstrip("/")
|
|
61
|
+
self.timeout = timeout
|
|
62
|
+
#: rate-limit / quota headers from the most recent response
|
|
63
|
+
self.last_limits = None
|
|
64
|
+
self.alerts = _Alerts(self)
|
|
65
|
+
|
|
66
|
+
def request(self, method, path, params=None, body=None):
|
|
67
|
+
query = {}
|
|
68
|
+
for k, v in (params or {}).items():
|
|
69
|
+
if v is None or v == "":
|
|
70
|
+
continue
|
|
71
|
+
query[k] = ",".join(map(str, v)) if isinstance(v, (list, tuple)) else str(v)
|
|
72
|
+
url = self.base_url + path + ("?" + urllib.parse.urlencode(query) if query else "")
|
|
73
|
+
headers = {"Accept": "application/json", "User-Agent": f"clutchapi-python/{__version__}"}
|
|
74
|
+
if self.api_key:
|
|
75
|
+
headers["Authorization"] = f"Bearer {self.api_key}"
|
|
76
|
+
data = None
|
|
77
|
+
if body is not None:
|
|
78
|
+
data = json.dumps(body).encode()
|
|
79
|
+
headers["Content-Type"] = "application/json"
|
|
80
|
+
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
|
81
|
+
try:
|
|
82
|
+
with urllib.request.urlopen(req, timeout=self.timeout) as res:
|
|
83
|
+
self._limits(res.headers)
|
|
84
|
+
text = res.read().decode()
|
|
85
|
+
except urllib.error.HTTPError as e:
|
|
86
|
+
self._limits(e.headers)
|
|
87
|
+
raw = e.read().decode(errors="replace")
|
|
88
|
+
try:
|
|
89
|
+
parsed = json.loads(raw)
|
|
90
|
+
except ValueError:
|
|
91
|
+
parsed = {"raw": raw}
|
|
92
|
+
raise ClutchError(parsed.get("message") or parsed.get("error") or f"HTTP {e.code}", e.code, parsed) from None
|
|
93
|
+
return json.loads(text) if text else {}
|
|
94
|
+
|
|
95
|
+
def get(self, path, params=None):
|
|
96
|
+
return self.request("GET", path, params=params)
|
|
97
|
+
|
|
98
|
+
def _limits(self, h):
|
|
99
|
+
n = lambda k: int(h[k]) if h and h.get(k) not in (None, "") else None
|
|
100
|
+
self.last_limits = {
|
|
101
|
+
"limit": n("X-RateLimit-Limit"), "remaining": n("X-RateLimit-Remaining"),
|
|
102
|
+
"quota_limit": n("X-Quota-Limit"), "quota_remaining": n("X-Quota-Remaining"),
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
# ── catalog + health ──
|
|
106
|
+
def status(self): return self.get("/api/v1/status")
|
|
107
|
+
def book_status(self, days=90): return self.get("/api/v1/status/books", {"days": days})
|
|
108
|
+
def sports(self): return self.get("/api/v1/sports")
|
|
109
|
+
|
|
110
|
+
# ── odds ──
|
|
111
|
+
def odds(self, sport, **params):
|
|
112
|
+
"""Game lines (moneyline / spread / total) for a sport, every book."""
|
|
113
|
+
return self.get(f"/api/v1/{sport}/odds", params)
|
|
114
|
+
|
|
115
|
+
def props(self, sport, **params):
|
|
116
|
+
"""Player props for a sport, every book."""
|
|
117
|
+
return self.get(f"/api/v1/{sport}/props", params)
|
|
118
|
+
|
|
119
|
+
def board(self, **params): return self.get("/api/v1/board", params)
|
|
120
|
+
def ev(self, sport, **params): return self.get(f"/api/v1/{sport}/ev", params)
|
|
121
|
+
def arbitrage(self, sport=None, **params):
|
|
122
|
+
return self.get(f"/api/v1/{sport}/arbitrage" if sport else "/api/v1/arbitrage", params)
|
|
123
|
+
def signals(self, sport, **params): return self.get(f"/api/v1/{sport}/signals", params)
|
|
124
|
+
|
|
125
|
+
# ── movement + history ──
|
|
126
|
+
def movements(self, **params): return self.get("/api/v1/movements", params)
|
|
127
|
+
def steam(self, **params): return self.get("/api/v1/movements/steam", params)
|
|
128
|
+
def prop_movements(self, **params): return self.get("/api/v1/props/movements", params)
|
|
129
|
+
def history_games(self, **params): return self.get("/api/v1/history/games", params)
|
|
130
|
+
def history_odds(self, game_key, **params): return self.get("/api/v1/history/odds", {"game_key": game_key, **params})
|
|
131
|
+
def history_props(self, **params): return self.get("/api/v1/history/props", params)
|
|
132
|
+
|
|
133
|
+
def closing(self, **params):
|
|
134
|
+
"""The closing line for a game market."""
|
|
135
|
+
return self.get("/api/v1/closing", params)
|
|
136
|
+
|
|
137
|
+
def clv(self, **params):
|
|
138
|
+
"""Grade a bet's price against the close (closing-line value)."""
|
|
139
|
+
return self.get("/api/v1/clv", params)
|
|
140
|
+
|
|
141
|
+
# ── stats + context ──
|
|
142
|
+
def injuries(self, sport): return self.get(f"/api/v1/{sport}/injuries")
|
|
143
|
+
def results(self, sport, **params): return self.get(f"/api/v1/{sport}/results", params)
|
|
144
|
+
def averages(self, sport, **params): return self.get(f"/api/v1/{sport}/stats/averages", params)
|
|
145
|
+
def player_log(self, player_id, **params): return self.get(f"/api/v1/player/{player_id}/log", params)
|
|
146
|
+
|
|
147
|
+
# ── prediction markets ──
|
|
148
|
+
def kalshi(self, sport): return self.get(f"/api/v2/kalshi/{sport}/leagues")
|
|
149
|
+
def polymarket(self, sport="all"):
|
|
150
|
+
return self.get("/api/v2/polymarket/all/markets" if sport == "all" else f"/api/v2/polymarket/{sport}/leagues")
|
|
151
|
+
|
|
152
|
+
def stream(self, sport=None):
|
|
153
|
+
"""Live odds over Server-Sent Events. Yields (event_type, data) forever."""
|
|
154
|
+
q = "?" + urllib.parse.urlencode({"sport": sport}) if sport else ""
|
|
155
|
+
headers = {"Accept": "text/event-stream"}
|
|
156
|
+
if self.api_key:
|
|
157
|
+
headers["Authorization"] = f"Bearer {self.api_key}"
|
|
158
|
+
req = urllib.request.Request(self.base_url + "/api/v1/stream/odds" + q, headers=headers)
|
|
159
|
+
with urllib.request.urlopen(req) as res:
|
|
160
|
+
kind, data = "message", ""
|
|
161
|
+
for raw in res:
|
|
162
|
+
line = raw.decode().rstrip("\n").rstrip("\r")
|
|
163
|
+
if not line:
|
|
164
|
+
if data:
|
|
165
|
+
try:
|
|
166
|
+
yield kind, json.loads(data)
|
|
167
|
+
except ValueError:
|
|
168
|
+
yield kind, data
|
|
169
|
+
kind, data = "message", ""
|
|
170
|
+
elif line.startswith("event:"):
|
|
171
|
+
kind = line[6:].strip()
|
|
172
|
+
elif line.startswith("data:"):
|
|
173
|
+
data += line[5:].strip()
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def verify_webhook(raw_body, signature_header, secret):
|
|
177
|
+
"""Check a CLUTCH webhook. Pass the RAW body (bytes or str), the
|
|
178
|
+
X-Clutch-Signature header, and the whsec_... secret from the dashboard."""
|
|
179
|
+
if not signature_header or not secret:
|
|
180
|
+
return False
|
|
181
|
+
if isinstance(raw_body, str):
|
|
182
|
+
raw_body = raw_body.encode()
|
|
183
|
+
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
|
|
184
|
+
return hmac.compare_digest(expected, str(signature_header))
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: clutchapi
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official client for the CLUTCH sports odds API: live odds and player props from 30+ books, line movement, closing lines, CLV, arbitrage and webhooks.
|
|
5
|
+
Author: Cooper Tech Solutions LLC
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://api.clutchbets.online
|
|
8
|
+
Project-URL: Documentation, https://api.clutchbets.online/docs
|
|
9
|
+
Keywords: sports betting,odds api,player props,sportsbook,nfl odds,nba odds,line movement,closing line value,arbitrage
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Requires-Python: >=3.8
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# clutchapi
|
|
17
|
+
|
|
18
|
+
The official Python client for the [CLUTCH API](https://api.clutchbets.online): live odds and player props from 30+ sportsbooks, line movement, closing lines, CLV grading, arbitrage and webhooks. Standard library only.
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install clutchapi
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from clutchapi import Clutch
|
|
26
|
+
|
|
27
|
+
clutch = Clutch("ck_live_...") # or set CLUTCH_API_KEY
|
|
28
|
+
|
|
29
|
+
nfl = clutch.odds("nfl")
|
|
30
|
+
props = clutch.props("nba", market="points")
|
|
31
|
+
steam = clutch.steam(sport="nfl")
|
|
32
|
+
close = clutch.closing(sport="nfl", game_key="...", market="spread")
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Get a key at [api.clutchbets.online/console](https://api.clutchbets.online/console).
|
|
36
|
+
|
|
37
|
+
## Live stream
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
for event, data in clutch.stream(sport="nba"):
|
|
41
|
+
print(event, data)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Line-move webhooks
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
out = clutch.alerts.create(channel="webhook", url="https://your.app/hooks/clutch",
|
|
48
|
+
book="pinnacle", sport="nfl", min_move=1)
|
|
49
|
+
secret = out["secret"] # shown once, save it
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Verify each delivery (Flask example, raw body):
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from clutchapi import verify_webhook
|
|
56
|
+
|
|
57
|
+
@app.post("/hooks/clutch")
|
|
58
|
+
def hook():
|
|
59
|
+
if not verify_webhook(request.get_data(), request.headers.get("X-Clutch-Signature"), SECRET):
|
|
60
|
+
return "", 401
|
|
61
|
+
moves = request.get_json()["moves"]
|
|
62
|
+
return "", 200
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Errors and limits
|
|
66
|
+
|
|
67
|
+
Failed requests raise `ClutchError` with `.status` and `.body`. After every call, `clutch.last_limits` holds your rate-limit and monthly-quota headers.
|
|
68
|
+
|
|
69
|
+
Any endpoint is reachable directly with `clutch.get("/api/v1/...", {...})`. Full reference: [api.clutchbets.online/docs](https://api.clutchbets.online/docs).
|
|
70
|
+
|
|
71
|
+
MIT © Cooper Tech Solutions LLC
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
clutchapi
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "clutchapi"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Official client for the CLUTCH sports odds API: live odds and player props from 30+ books, line movement, closing lines, CLV, arbitrage and webhooks."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "Cooper Tech Solutions LLC" }]
|
|
13
|
+
keywords = ["sports betting", "odds api", "player props", "sportsbook", "nfl odds", "nba odds", "line movement", "closing line value", "arbitrage"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
Homepage = "https://api.clutchbets.online"
|
|
22
|
+
Documentation = "https://api.clutchbets.online/docs"
|
|
23
|
+
|
|
24
|
+
[tool.setuptools]
|
|
25
|
+
packages = ["clutchapi"]
|