puntersedge 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.
@@ -0,0 +1,134 @@
1
+ Metadata-Version: 2.4
2
+ Name: puntersedge
3
+ Version: 0.1.0
4
+ Summary: Official Python SDK for the PuntersEdge Australian Sports Odds API
5
+ Home-page: https://puntersedge.online/developers
6
+ Author: PuntersEdge
7
+ Author-email: PuntersEdge <hello@puntersedge.online>
8
+ Project-URL: Homepage, https://puntersedge.online
9
+ Project-URL: Documentation, https://puntersedge.online/developers
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Requires-Python: >=3.8
13
+ Description-Content-Type: text/markdown
14
+ Requires-Dist: httpx>=0.27.0
15
+ Dynamic: author
16
+ Dynamic: home-page
17
+ Dynamic: requires-python
18
+
19
+ # PuntersEdge Python SDK
20
+
21
+ Official Python client for the PuntersEdge Australian Sports Odds API.
22
+
23
+ PuntersEdge gives developers simple REST access to Australian sports odds, best-price comparisons, next-to-go racing and arbitrage signals from local bookmakers.
24
+
25
+ ## Install
26
+
27
+ ```bash
28
+ pip install puntersedge
29
+ ```
30
+
31
+ For local development from this repository:
32
+
33
+ ```bash
34
+ pip install -e sdk/python
35
+ ```
36
+
37
+ ## Quick start
38
+
39
+ ```python
40
+ from puntersedge import PuntersEdgeClient
41
+ client = PuntersEdgeClient("YOUR_API_KEY")
42
+ sports = client.get_sports()
43
+ odds = client.get_odds("nrl", markets=["h2h"])
44
+ print(odds[:1])
45
+ client.close()
46
+ ```
47
+
48
+ ## Authentication
49
+
50
+ Pass your API key when creating the client. The SDK sends it as the `X-API-Key` header.
51
+
52
+ ```python
53
+ client = PuntersEdgeClient(api_key="YOUR_API_KEY")
54
+ ```
55
+
56
+ You can override the API base URL for testing:
57
+
58
+ ```python
59
+ client = PuntersEdgeClient("YOUR_API_KEY", base_url="http://localhost:8010")
60
+ ```
61
+
62
+ ## Methods
63
+
64
+ | Method | Description | Returns | Endpoint |
65
+ | --- | --- | --- | --- |
66
+ | `get_sports()` | List active sports and competitions | `list` | `GET /v1/sports` |
67
+ | `get_odds(sport_key, markets=None, bookmakers=None)` | Get bookmaker odds for a sport. Defaults to `h2h` market. | `list` | `GET /v1/sports/{sport_key}/odds` |
68
+ | `get_best_odds(sport_key)` | Get best available prices by outcome for a sport | `list` | `GET /v1/best-odds/{sport_key}` |
69
+ | `get_racing(categories=None, num_races=10)` | Get next-to-go racing markets | `list` | `GET /v1/racing/next-to-go` |
70
+ | `get_arb(sport_key=None, min_profit_pct=0.5)` | Scan for arbitrage opportunities | `dict` | `GET /v1/arb/sports` |
71
+ | `get_usage()` | View plan, credits, endpoint usage and recent activity | `dict` | `GET /v1/usage` |
72
+ | `get_health()` | Connector health and freshness | `dict` | `GET /v1/health` |
73
+ | `close()` | Close the underlying HTTP connection pool | `None` | - |
74
+
75
+ ## Async usage
76
+
77
+ ```python
78
+ import asyncio
79
+ from puntersedge import AsyncPuntersEdgeClient
80
+
81
+ async def main():
82
+ async with AsyncPuntersEdgeClient("YOUR_API_KEY") as client:
83
+ sports = await client.get_sports()
84
+ odds = await client.get_odds("afl", markets=["h2h"])
85
+ usage = await client.get_usage()
86
+ print(len(sports), len(odds), usage["credits_remaining"])
87
+
88
+ asyncio.run(main())
89
+ ```
90
+
91
+ ## Error handling
92
+
93
+ ```python
94
+ from puntersedge import PuntersEdgeClient
95
+ from puntersedge.exceptions import AuthError, RateLimitError, NotFoundError, ServerError
96
+
97
+ client = PuntersEdgeClient("YOUR_API_KEY")
98
+ try:
99
+ print(client.get_odds("nrl"))
100
+ except AuthError:
101
+ print("Invalid API key")
102
+ except RateLimitError as exc:
103
+ print(f"Credit limit reached. Resets at {exc.reset_at}")
104
+ except NotFoundError:
105
+ print("Unknown sport or endpoint")
106
+ except ServerError:
107
+ print("PuntersEdge API is temporarily unavailable")
108
+ finally:
109
+ client.close()
110
+ ```
111
+
112
+ All SDK exceptions inherit from `PuntersEdgeError`.
113
+
114
+ ## Examples
115
+
116
+ See the `examples/` directory:
117
+
118
+ - `basic_usage.py` — list sports, fetch NRL odds and check usage
119
+ - `arb_scanner.py` — scan all sports for arbitrage opportunities
120
+ - `racing_ntg.py` — print next-to-go racing runners and prices
121
+
122
+ Run an example with:
123
+
124
+ ```bash
125
+ PUNTERSEDGE_API_KEY=YOUR_API_KEY python sdk/python/examples/basic_usage.py
126
+ ```
127
+
128
+ ## Documentation
129
+
130
+ - API docs: https://puntersedge.online/api/docs
131
+ - Developer docs: https://puntersedge.online/developers
132
+ - Website: https://puntersedge.online
133
+
134
+ 18+ only. Please gamble responsibly.
@@ -0,0 +1,116 @@
1
+ # PuntersEdge Python SDK
2
+
3
+ Official Python client for the PuntersEdge Australian Sports Odds API.
4
+
5
+ PuntersEdge gives developers simple REST access to Australian sports odds, best-price comparisons, next-to-go racing and arbitrage signals from local bookmakers.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pip install puntersedge
11
+ ```
12
+
13
+ For local development from this repository:
14
+
15
+ ```bash
16
+ pip install -e sdk/python
17
+ ```
18
+
19
+ ## Quick start
20
+
21
+ ```python
22
+ from puntersedge import PuntersEdgeClient
23
+ client = PuntersEdgeClient("YOUR_API_KEY")
24
+ sports = client.get_sports()
25
+ odds = client.get_odds("nrl", markets=["h2h"])
26
+ print(odds[:1])
27
+ client.close()
28
+ ```
29
+
30
+ ## Authentication
31
+
32
+ Pass your API key when creating the client. The SDK sends it as the `X-API-Key` header.
33
+
34
+ ```python
35
+ client = PuntersEdgeClient(api_key="YOUR_API_KEY")
36
+ ```
37
+
38
+ You can override the API base URL for testing:
39
+
40
+ ```python
41
+ client = PuntersEdgeClient("YOUR_API_KEY", base_url="http://localhost:8010")
42
+ ```
43
+
44
+ ## Methods
45
+
46
+ | Method | Description | Returns | Endpoint |
47
+ | --- | --- | --- | --- |
48
+ | `get_sports()` | List active sports and competitions | `list` | `GET /v1/sports` |
49
+ | `get_odds(sport_key, markets=None, bookmakers=None)` | Get bookmaker odds for a sport. Defaults to `h2h` market. | `list` | `GET /v1/sports/{sport_key}/odds` |
50
+ | `get_best_odds(sport_key)` | Get best available prices by outcome for a sport | `list` | `GET /v1/best-odds/{sport_key}` |
51
+ | `get_racing(categories=None, num_races=10)` | Get next-to-go racing markets | `list` | `GET /v1/racing/next-to-go` |
52
+ | `get_arb(sport_key=None, min_profit_pct=0.5)` | Scan for arbitrage opportunities | `dict` | `GET /v1/arb/sports` |
53
+ | `get_usage()` | View plan, credits, endpoint usage and recent activity | `dict` | `GET /v1/usage` |
54
+ | `get_health()` | Connector health and freshness | `dict` | `GET /v1/health` |
55
+ | `close()` | Close the underlying HTTP connection pool | `None` | - |
56
+
57
+ ## Async usage
58
+
59
+ ```python
60
+ import asyncio
61
+ from puntersedge import AsyncPuntersEdgeClient
62
+
63
+ async def main():
64
+ async with AsyncPuntersEdgeClient("YOUR_API_KEY") as client:
65
+ sports = await client.get_sports()
66
+ odds = await client.get_odds("afl", markets=["h2h"])
67
+ usage = await client.get_usage()
68
+ print(len(sports), len(odds), usage["credits_remaining"])
69
+
70
+ asyncio.run(main())
71
+ ```
72
+
73
+ ## Error handling
74
+
75
+ ```python
76
+ from puntersedge import PuntersEdgeClient
77
+ from puntersedge.exceptions import AuthError, RateLimitError, NotFoundError, ServerError
78
+
79
+ client = PuntersEdgeClient("YOUR_API_KEY")
80
+ try:
81
+ print(client.get_odds("nrl"))
82
+ except AuthError:
83
+ print("Invalid API key")
84
+ except RateLimitError as exc:
85
+ print(f"Credit limit reached. Resets at {exc.reset_at}")
86
+ except NotFoundError:
87
+ print("Unknown sport or endpoint")
88
+ except ServerError:
89
+ print("PuntersEdge API is temporarily unavailable")
90
+ finally:
91
+ client.close()
92
+ ```
93
+
94
+ All SDK exceptions inherit from `PuntersEdgeError`.
95
+
96
+ ## Examples
97
+
98
+ See the `examples/` directory:
99
+
100
+ - `basic_usage.py` — list sports, fetch NRL odds and check usage
101
+ - `arb_scanner.py` — scan all sports for arbitrage opportunities
102
+ - `racing_ntg.py` — print next-to-go racing runners and prices
103
+
104
+ Run an example with:
105
+
106
+ ```bash
107
+ PUNTERSEDGE_API_KEY=YOUR_API_KEY python sdk/python/examples/basic_usage.py
108
+ ```
109
+
110
+ ## Documentation
111
+
112
+ - API docs: https://puntersedge.online/api/docs
113
+ - Developer docs: https://puntersedge.online/developers
114
+ - Website: https://puntersedge.online
115
+
116
+ 18+ only. Please gamble responsibly.
@@ -0,0 +1,3 @@
1
+ from .client import PuntersEdgeClient, AsyncPuntersEdgeClient
2
+ __version__ = "0.1.0"
3
+ __all__ = ["PuntersEdgeClient", "AsyncPuntersEdgeClient"]
@@ -0,0 +1,162 @@
1
+ import httpx
2
+ from .exceptions import AuthError, RateLimitError, NotFoundError, ServerError
3
+
4
+
5
+ class PuntersEdgeClient:
6
+ """Synchronous PuntersEdge API client."""
7
+ BASE_URL = "https://puntersedge.online/api"
8
+
9
+ def __init__(self, api_key: str, timeout: int = 30, base_url: str = None):
10
+ self.api_key = api_key
11
+ self.timeout = timeout
12
+ if base_url:
13
+ self.BASE_URL = base_url
14
+ self._client = httpx.Client(
15
+ headers={"X-API-Key": api_key, "User-Agent": "puntersedge-python/0.1.0"},
16
+ timeout=timeout,
17
+ )
18
+
19
+ def _handle_response(self, resp):
20
+ if resp.status_code == 401:
21
+ raise AuthError("Invalid API key", status_code=401, response=resp)
22
+ if resp.status_code == 402:
23
+ data = resp.json()
24
+ raise RateLimitError(
25
+ data.get("detail", "Credit limit reached"),
26
+ credits_remaining=data.get("credits_remaining", 0),
27
+ reset_at=data.get("reset_at"),
28
+ status_code=402,
29
+ response=resp,
30
+ )
31
+ if resp.status_code == 404:
32
+ raise NotFoundError(f"Not found: {resp.url}", status_code=404, response=resp)
33
+ if resp.status_code >= 500:
34
+ raise ServerError(f"Server error: {resp.status_code}", status_code=resp.status_code, response=resp)
35
+ resp.raise_for_status()
36
+ return resp.json()
37
+
38
+ def get_sports(self) -> list:
39
+ resp = self._client.get(f"{self.BASE_URL}/v1/sports")
40
+ return self._handle_response(resp)
41
+
42
+ def get_odds(self, sport_key: str, markets: list = None, bookmakers: list = None) -> list:
43
+ params = {"markets": ",".join(markets or ["h2h"])}
44
+ if bookmakers:
45
+ params["bookmakers"] = ",".join(bookmakers)
46
+ resp = self._client.get(f"{self.BASE_URL}/v1/sports/{sport_key}/odds", params=params)
47
+ return self._handle_response(resp)
48
+
49
+ def get_best_odds(self, sport_key: str) -> list:
50
+ resp = self._client.get(f"{self.BASE_URL}/v1/best-odds/{sport_key}")
51
+ return self._handle_response(resp)
52
+
53
+ def get_racing(self, categories: list = None, num_races: int = 10) -> list:
54
+ params = {"num_races": num_races}
55
+ if categories:
56
+ params["categories"] = ",".join(categories)
57
+ resp = self._client.get(f"{self.BASE_URL}/v1/racing/next-to-go", params=params)
58
+ return self._handle_response(resp)
59
+
60
+ def get_arb(self, sport_key: str = None, min_profit_pct: float = 0.5) -> dict:
61
+ params = {"min_profit_pct": min_profit_pct}
62
+ if sport_key:
63
+ params["sport_key"] = sport_key
64
+ resp = self._client.get(f"{self.BASE_URL}/v1/arb/sports", params=params)
65
+ return self._handle_response(resp)
66
+
67
+ def get_usage(self) -> dict:
68
+ resp = self._client.get(f"{self.BASE_URL}/v1/usage")
69
+ return self._handle_response(resp)
70
+
71
+ def get_health(self) -> dict:
72
+ resp = self._client.get(f"{self.BASE_URL}/v1/health")
73
+ return self._handle_response(resp)
74
+
75
+ def close(self):
76
+ self._client.close()
77
+
78
+ def __enter__(self):
79
+ return self
80
+
81
+ def __exit__(self, *args):
82
+ self.close()
83
+
84
+
85
+ class AsyncPuntersEdgeClient:
86
+ """Async PuntersEdge API client."""
87
+ BASE_URL = "https://puntersedge.online/api"
88
+
89
+ def __init__(self, api_key: str, timeout: int = 30, base_url: str = None):
90
+ self.api_key = api_key
91
+ self.timeout = timeout
92
+ if base_url:
93
+ self.BASE_URL = base_url
94
+ self._client = httpx.AsyncClient(
95
+ headers={"X-API-Key": api_key, "User-Agent": "puntersedge-python/0.1.0"},
96
+ timeout=timeout,
97
+ )
98
+
99
+ def _handle_response(self, resp):
100
+ if resp.status_code == 401:
101
+ raise AuthError("Invalid API key", status_code=401, response=resp)
102
+ if resp.status_code == 402:
103
+ data = resp.json()
104
+ raise RateLimitError(
105
+ data.get("detail", "Credit limit reached"),
106
+ credits_remaining=data.get("credits_remaining", 0),
107
+ reset_at=data.get("reset_at"),
108
+ status_code=402,
109
+ response=resp,
110
+ )
111
+ if resp.status_code == 404:
112
+ raise NotFoundError("Not found", status_code=404, response=resp)
113
+ if resp.status_code >= 500:
114
+ raise ServerError(f"Server error {resp.status_code}", status_code=resp.status_code, response=resp)
115
+ resp.raise_for_status()
116
+ return resp.json()
117
+
118
+ async def get_sports(self) -> list:
119
+ resp = await self._client.get(f"{self.BASE_URL}/v1/sports")
120
+ return self._handle_response(resp)
121
+
122
+ async def get_odds(self, sport_key: str, markets: list = None, bookmakers: list = None) -> list:
123
+ params = {"markets": ",".join(markets or ["h2h"])}
124
+ if bookmakers:
125
+ params["bookmakers"] = ",".join(bookmakers)
126
+ resp = await self._client.get(f"{self.BASE_URL}/v1/sports/{sport_key}/odds", params=params)
127
+ return self._handle_response(resp)
128
+
129
+ async def get_best_odds(self, sport_key: str) -> list:
130
+ resp = await self._client.get(f"{self.BASE_URL}/v1/best-odds/{sport_key}")
131
+ return self._handle_response(resp)
132
+
133
+ async def get_racing(self, categories: list = None, num_races: int = 10) -> list:
134
+ params = {"num_races": num_races}
135
+ if categories:
136
+ params["categories"] = ",".join(categories)
137
+ resp = await self._client.get(f"{self.BASE_URL}/v1/racing/next-to-go", params=params)
138
+ return self._handle_response(resp)
139
+
140
+ async def get_arb(self, sport_key: str = None, min_profit_pct: float = 0.5) -> dict:
141
+ params = {"min_profit_pct": min_profit_pct}
142
+ if sport_key:
143
+ params["sport_key"] = sport_key
144
+ resp = await self._client.get(f"{self.BASE_URL}/v1/arb/sports", params=params)
145
+ return self._handle_response(resp)
146
+
147
+ async def get_usage(self) -> dict:
148
+ resp = await self._client.get(f"{self.BASE_URL}/v1/usage")
149
+ return self._handle_response(resp)
150
+
151
+ async def get_health(self) -> dict:
152
+ resp = await self._client.get(f"{self.BASE_URL}/v1/health")
153
+ return self._handle_response(resp)
154
+
155
+ async def close(self):
156
+ await self._client.aclose()
157
+
158
+ async def __aenter__(self):
159
+ return self
160
+
161
+ async def __aexit__(self, *args):
162
+ await self.close()
@@ -0,0 +1,20 @@
1
+ class PuntersEdgeError(Exception):
2
+ def __init__(self, message, status_code=None, response=None):
3
+ super().__init__(message)
4
+ self.status_code = status_code
5
+ self.response = response
6
+
7
+ class AuthError(PuntersEdgeError):
8
+ pass
9
+
10
+ class RateLimitError(PuntersEdgeError):
11
+ def __init__(self, message, credits_remaining=0, reset_at=None, **kwargs):
12
+ super().__init__(message, **kwargs)
13
+ self.credits_remaining = credits_remaining
14
+ self.reset_at = reset_at
15
+
16
+ class NotFoundError(PuntersEdgeError):
17
+ pass
18
+
19
+ class ServerError(PuntersEdgeError):
20
+ pass
@@ -0,0 +1,68 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional, List
3
+
4
+
5
+ @dataclass
6
+ class Outcome:
7
+ name: str
8
+ price: float
9
+ point: Optional[float] = None
10
+
11
+ @classmethod
12
+ def from_dict(cls, d):
13
+ return cls(**{k: d.get(k) for k in ['name', 'price', 'point']})
14
+
15
+
16
+ @dataclass
17
+ class BookmakerOdds:
18
+ bookmaker_key: str
19
+ bookmaker_title: str
20
+ outcomes: List[Outcome]
21
+
22
+ @classmethod
23
+ def from_dict(cls, d):
24
+ return cls(
25
+ bookmaker_key=d.get('bookmaker_key', ''),
26
+ bookmaker_title=d.get('bookmaker_title', ''),
27
+ outcomes=[Outcome.from_dict(o) for o in d.get('outcomes', [])]
28
+ )
29
+
30
+
31
+ @dataclass
32
+ class Event:
33
+ id: str
34
+ sport_key: str
35
+ home_team: str
36
+ away_team: str
37
+ commence_time: str
38
+ bookmakers: List[BookmakerOdds]
39
+
40
+ @classmethod
41
+ def from_dict(cls, d):
42
+ return cls(
43
+ id=d.get('id', ''),
44
+ sport_key=d.get('sport_key', ''),
45
+ home_team=d.get('home_team', ''),
46
+ away_team=d.get('away_team', ''),
47
+ commence_time=d.get('commence_time', ''),
48
+ bookmakers=[BookmakerOdds.from_dict(b) for b in d.get('bookmakers', [])]
49
+ )
50
+
51
+
52
+ @dataclass
53
+ class UsageInfo:
54
+ plan: str
55
+ credits_used: int
56
+ credits_limit: int
57
+ credits_remaining: int
58
+ reset_at: Optional[str]
59
+
60
+ @classmethod
61
+ def from_dict(cls, d):
62
+ return cls(
63
+ plan=d['plan'],
64
+ credits_used=d['credits_used'],
65
+ credits_limit=d['credits_limit'],
66
+ credits_remaining=d['credits_remaining'],
67
+ reset_at=d.get('reset_at'),
68
+ )
@@ -0,0 +1,134 @@
1
+ Metadata-Version: 2.4
2
+ Name: puntersedge
3
+ Version: 0.1.0
4
+ Summary: Official Python SDK for the PuntersEdge Australian Sports Odds API
5
+ Home-page: https://puntersedge.online/developers
6
+ Author: PuntersEdge
7
+ Author-email: PuntersEdge <hello@puntersedge.online>
8
+ Project-URL: Homepage, https://puntersedge.online
9
+ Project-URL: Documentation, https://puntersedge.online/developers
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Requires-Python: >=3.8
13
+ Description-Content-Type: text/markdown
14
+ Requires-Dist: httpx>=0.27.0
15
+ Dynamic: author
16
+ Dynamic: home-page
17
+ Dynamic: requires-python
18
+
19
+ # PuntersEdge Python SDK
20
+
21
+ Official Python client for the PuntersEdge Australian Sports Odds API.
22
+
23
+ PuntersEdge gives developers simple REST access to Australian sports odds, best-price comparisons, next-to-go racing and arbitrage signals from local bookmakers.
24
+
25
+ ## Install
26
+
27
+ ```bash
28
+ pip install puntersedge
29
+ ```
30
+
31
+ For local development from this repository:
32
+
33
+ ```bash
34
+ pip install -e sdk/python
35
+ ```
36
+
37
+ ## Quick start
38
+
39
+ ```python
40
+ from puntersedge import PuntersEdgeClient
41
+ client = PuntersEdgeClient("YOUR_API_KEY")
42
+ sports = client.get_sports()
43
+ odds = client.get_odds("nrl", markets=["h2h"])
44
+ print(odds[:1])
45
+ client.close()
46
+ ```
47
+
48
+ ## Authentication
49
+
50
+ Pass your API key when creating the client. The SDK sends it as the `X-API-Key` header.
51
+
52
+ ```python
53
+ client = PuntersEdgeClient(api_key="YOUR_API_KEY")
54
+ ```
55
+
56
+ You can override the API base URL for testing:
57
+
58
+ ```python
59
+ client = PuntersEdgeClient("YOUR_API_KEY", base_url="http://localhost:8010")
60
+ ```
61
+
62
+ ## Methods
63
+
64
+ | Method | Description | Returns | Endpoint |
65
+ | --- | --- | --- | --- |
66
+ | `get_sports()` | List active sports and competitions | `list` | `GET /v1/sports` |
67
+ | `get_odds(sport_key, markets=None, bookmakers=None)` | Get bookmaker odds for a sport. Defaults to `h2h` market. | `list` | `GET /v1/sports/{sport_key}/odds` |
68
+ | `get_best_odds(sport_key)` | Get best available prices by outcome for a sport | `list` | `GET /v1/best-odds/{sport_key}` |
69
+ | `get_racing(categories=None, num_races=10)` | Get next-to-go racing markets | `list` | `GET /v1/racing/next-to-go` |
70
+ | `get_arb(sport_key=None, min_profit_pct=0.5)` | Scan for arbitrage opportunities | `dict` | `GET /v1/arb/sports` |
71
+ | `get_usage()` | View plan, credits, endpoint usage and recent activity | `dict` | `GET /v1/usage` |
72
+ | `get_health()` | Connector health and freshness | `dict` | `GET /v1/health` |
73
+ | `close()` | Close the underlying HTTP connection pool | `None` | - |
74
+
75
+ ## Async usage
76
+
77
+ ```python
78
+ import asyncio
79
+ from puntersedge import AsyncPuntersEdgeClient
80
+
81
+ async def main():
82
+ async with AsyncPuntersEdgeClient("YOUR_API_KEY") as client:
83
+ sports = await client.get_sports()
84
+ odds = await client.get_odds("afl", markets=["h2h"])
85
+ usage = await client.get_usage()
86
+ print(len(sports), len(odds), usage["credits_remaining"])
87
+
88
+ asyncio.run(main())
89
+ ```
90
+
91
+ ## Error handling
92
+
93
+ ```python
94
+ from puntersedge import PuntersEdgeClient
95
+ from puntersedge.exceptions import AuthError, RateLimitError, NotFoundError, ServerError
96
+
97
+ client = PuntersEdgeClient("YOUR_API_KEY")
98
+ try:
99
+ print(client.get_odds("nrl"))
100
+ except AuthError:
101
+ print("Invalid API key")
102
+ except RateLimitError as exc:
103
+ print(f"Credit limit reached. Resets at {exc.reset_at}")
104
+ except NotFoundError:
105
+ print("Unknown sport or endpoint")
106
+ except ServerError:
107
+ print("PuntersEdge API is temporarily unavailable")
108
+ finally:
109
+ client.close()
110
+ ```
111
+
112
+ All SDK exceptions inherit from `PuntersEdgeError`.
113
+
114
+ ## Examples
115
+
116
+ See the `examples/` directory:
117
+
118
+ - `basic_usage.py` — list sports, fetch NRL odds and check usage
119
+ - `arb_scanner.py` — scan all sports for arbitrage opportunities
120
+ - `racing_ntg.py` — print next-to-go racing runners and prices
121
+
122
+ Run an example with:
123
+
124
+ ```bash
125
+ PUNTERSEDGE_API_KEY=YOUR_API_KEY python sdk/python/examples/basic_usage.py
126
+ ```
127
+
128
+ ## Documentation
129
+
130
+ - API docs: https://puntersedge.online/api/docs
131
+ - Developer docs: https://puntersedge.online/developers
132
+ - Website: https://puntersedge.online
133
+
134
+ 18+ only. Please gamble responsibly.
@@ -0,0 +1,12 @@
1
+ README.md
2
+ pyproject.toml
3
+ setup.py
4
+ puntersedge/__init__.py
5
+ puntersedge/client.py
6
+ puntersedge/exceptions.py
7
+ puntersedge/models.py
8
+ puntersedge.egg-info/PKG-INFO
9
+ puntersedge.egg-info/SOURCES.txt
10
+ puntersedge.egg-info/dependency_links.txt
11
+ puntersedge.egg-info/requires.txt
12
+ puntersedge.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ httpx>=0.27.0
@@ -0,0 +1 @@
1
+ puntersedge
@@ -0,0 +1,20 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "puntersedge"
7
+ version = "0.1.0"
8
+ description = "Official Python SDK for the PuntersEdge Australian Sports Odds API"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ dependencies = ["httpx>=0.27.0"]
12
+ authors = [{name = "PuntersEdge", email = "hello@puntersedge.online"}]
13
+ classifiers = [
14
+ "Programming Language :: Python :: 3",
15
+ "License :: OSI Approved :: MIT License",
16
+ ]
17
+
18
+ [project.urls]
19
+ Homepage = "https://puntersedge.online"
20
+ Documentation = "https://puntersedge.online/developers"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,19 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="puntersedge",
5
+ version="0.1.0",
6
+ description="Official Python SDK for the PuntersEdge Australian Sports Odds API",
7
+ long_description=open("README.md").read(),
8
+ long_description_content_type="text/markdown",
9
+ author="PuntersEdge",
10
+ author_email="hello@puntersedge.online",
11
+ url="https://puntersedge.online/developers",
12
+ packages=find_packages(),
13
+ install_requires=["httpx>=0.27.0"],
14
+ python_requires=">=3.8",
15
+ classifiers=[
16
+ "Programming Language :: Python :: 3",
17
+ "License :: OSI Approved :: MIT License",
18
+ ],
19
+ )