puntersedge 0.1.0
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.
- package/README.md +67 -0
- package/dist/index.d.ts +117 -0
- package/dist/index.js +99 -0
- package/package.json +35 -0
- package/src/index.ts +209 -0
package/README.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# puntersedge
|
|
2
|
+
|
|
3
|
+
Official Node.js / TypeScript client for the [PuntersEdge Australian Sports Odds API](https://puntersedge.online/api-platform).
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install puntersedge
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import PuntersEdgeClient from "puntersedge";
|
|
15
|
+
|
|
16
|
+
const client = new PuntersEdgeClient({ apiKey: "YOUR_API_KEY" });
|
|
17
|
+
|
|
18
|
+
// List sports
|
|
19
|
+
const sports = await client.sports();
|
|
20
|
+
|
|
21
|
+
// NRL head-to-head odds
|
|
22
|
+
const nrl = await client.odds("nrl", "h2h");
|
|
23
|
+
|
|
24
|
+
// Best odds across bookmakers (with arb detection)
|
|
25
|
+
const best = await client.bestOdds("afl");
|
|
26
|
+
|
|
27
|
+
// Next 5 horse races
|
|
28
|
+
const races = await client.nextToGo(5, "horse");
|
|
29
|
+
|
|
30
|
+
// Check your credit usage
|
|
31
|
+
const usage = await client.usage();
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## API key
|
|
35
|
+
|
|
36
|
+
[Get a free API key](https://puntersedge.online/api-platform) — no credit card required.
|
|
37
|
+
|
|
38
|
+
## Errors
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { AuthError, RateLimitError, NotFoundError, ServerError } from "puntersedge";
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const data = await client.odds("afl");
|
|
45
|
+
} catch (e) {
|
|
46
|
+
if (e instanceof AuthError) { /* invalid key */ }
|
|
47
|
+
if (e instanceof RateLimitError) { /* out of credits */ }
|
|
48
|
+
if (e instanceof NotFoundError) { /* sport not found */ }
|
|
49
|
+
if (e instanceof ServerError) { /* 5xx */ }
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Supported sports
|
|
54
|
+
|
|
55
|
+
AFL, NRL, NBA, Tennis ATP/WTA, EPL, A-League, Super Rugby, Big Bash, Test Cricket
|
|
56
|
+
|
|
57
|
+
## Links
|
|
58
|
+
|
|
59
|
+
- [API documentation](https://puntersedge.online/api/examples)
|
|
60
|
+
- [Coverage](https://puntersedge.online/api/coverage)
|
|
61
|
+
- [Pricing](https://puntersedge.online/api/pricing)
|
|
62
|
+
- [Status](https://puntersedge.online/status)
|
|
63
|
+
- [Python SDK](https://pypi.org/project/puntersedge/)
|
|
64
|
+
|
|
65
|
+
## License
|
|
66
|
+
|
|
67
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PuntersEdge Node.js SDK
|
|
3
|
+
* Official client for the Australian Sports Odds API
|
|
4
|
+
* https://puntersedge.online/api-platform
|
|
5
|
+
*/
|
|
6
|
+
export interface Sport {
|
|
7
|
+
key: string;
|
|
8
|
+
title: string;
|
|
9
|
+
group: string;
|
|
10
|
+
active: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface Outcome {
|
|
13
|
+
name: string;
|
|
14
|
+
price: number;
|
|
15
|
+
}
|
|
16
|
+
export interface Market {
|
|
17
|
+
key: string;
|
|
18
|
+
outcomes: Outcome[];
|
|
19
|
+
}
|
|
20
|
+
export interface Bookmaker {
|
|
21
|
+
key: string;
|
|
22
|
+
title: string;
|
|
23
|
+
markets: Market[];
|
|
24
|
+
}
|
|
25
|
+
export interface Event {
|
|
26
|
+
id: string;
|
|
27
|
+
home_team: string;
|
|
28
|
+
away_team: string;
|
|
29
|
+
commence_time: string;
|
|
30
|
+
bookmakers: Bookmaker[];
|
|
31
|
+
}
|
|
32
|
+
export interface OddsResponse {
|
|
33
|
+
sport_key: string;
|
|
34
|
+
events: Event[];
|
|
35
|
+
fetched_at?: string;
|
|
36
|
+
data_age_seconds?: number;
|
|
37
|
+
stale?: boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface BestOddsEvent {
|
|
40
|
+
id: string;
|
|
41
|
+
home_team: string;
|
|
42
|
+
away_team: string;
|
|
43
|
+
best_odds: Record<string, {
|
|
44
|
+
price: number;
|
|
45
|
+
bookmaker: string;
|
|
46
|
+
}>;
|
|
47
|
+
arb_opportunity: boolean;
|
|
48
|
+
arb_margin: number;
|
|
49
|
+
}
|
|
50
|
+
export interface BestOddsResponse {
|
|
51
|
+
sport_key: string;
|
|
52
|
+
events: BestOddsEvent[];
|
|
53
|
+
}
|
|
54
|
+
export interface Runner {
|
|
55
|
+
name: string;
|
|
56
|
+
number?: number;
|
|
57
|
+
prices: Record<string, number>;
|
|
58
|
+
}
|
|
59
|
+
export interface Race {
|
|
60
|
+
id: string;
|
|
61
|
+
name: string;
|
|
62
|
+
venue: string;
|
|
63
|
+
category: string;
|
|
64
|
+
start_time: string;
|
|
65
|
+
runners: Runner[];
|
|
66
|
+
}
|
|
67
|
+
export interface RacingResponse {
|
|
68
|
+
races: Race[];
|
|
69
|
+
}
|
|
70
|
+
export interface Usage {
|
|
71
|
+
plan: string;
|
|
72
|
+
credits_limit: number;
|
|
73
|
+
credits_used: number;
|
|
74
|
+
credits_remaining: number;
|
|
75
|
+
reset_date: string;
|
|
76
|
+
usage_by_endpoint?: Record<string, number>;
|
|
77
|
+
recent_activity?: Array<{
|
|
78
|
+
endpoint: string;
|
|
79
|
+
timestamp: string;
|
|
80
|
+
credits: number;
|
|
81
|
+
}>;
|
|
82
|
+
}
|
|
83
|
+
export interface PuntersEdgeOptions {
|
|
84
|
+
apiKey: string;
|
|
85
|
+
baseUrl?: string;
|
|
86
|
+
timeoutMs?: number;
|
|
87
|
+
}
|
|
88
|
+
export declare class AuthError extends Error {
|
|
89
|
+
constructor(message: string);
|
|
90
|
+
}
|
|
91
|
+
export declare class RateLimitError extends Error {
|
|
92
|
+
constructor(message: string);
|
|
93
|
+
}
|
|
94
|
+
export declare class NotFoundError extends Error {
|
|
95
|
+
constructor(message: string);
|
|
96
|
+
}
|
|
97
|
+
export declare class ServerError extends Error {
|
|
98
|
+
constructor(message: string);
|
|
99
|
+
}
|
|
100
|
+
export declare class PuntersEdgeClient {
|
|
101
|
+
private apiKey;
|
|
102
|
+
private baseUrl;
|
|
103
|
+
private timeoutMs;
|
|
104
|
+
constructor(options: PuntersEdgeOptions);
|
|
105
|
+
private url;
|
|
106
|
+
/** List all active sports */
|
|
107
|
+
sports(): Promise<Sport[]>;
|
|
108
|
+
/** Get odds for a sport. markets = "h2h" | "spreads" | "totals" (comma-separated) */
|
|
109
|
+
odds(sportKey: string, markets?: string, bookmakers?: string): Promise<OddsResponse>;
|
|
110
|
+
/** Best available price per selection + arb detection */
|
|
111
|
+
bestOdds(sportKey: string): Promise<BestOddsResponse>;
|
|
112
|
+
/** Next-to-go racing. categories = "horse" | "greyhound" | "harness" */
|
|
113
|
+
nextToGo(numRaces?: number, categories?: string): Promise<RacingResponse>;
|
|
114
|
+
/** Current credit usage */
|
|
115
|
+
usage(): Promise<Usage>;
|
|
116
|
+
}
|
|
117
|
+
export default PuntersEdgeClient;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* PuntersEdge Node.js SDK
|
|
4
|
+
* Official client for the Australian Sports Odds API
|
|
5
|
+
* https://puntersedge.online/api-platform
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.PuntersEdgeClient = exports.ServerError = exports.NotFoundError = exports.RateLimitError = exports.AuthError = void 0;
|
|
9
|
+
const BASE_URL = "https://puntersedge.online";
|
|
10
|
+
class AuthError extends Error {
|
|
11
|
+
constructor(message) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = "AuthError";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.AuthError = AuthError;
|
|
17
|
+
class RateLimitError extends Error {
|
|
18
|
+
constructor(message) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = "RateLimitError";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.RateLimitError = RateLimitError;
|
|
24
|
+
class NotFoundError extends Error {
|
|
25
|
+
constructor(message) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = "NotFoundError";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.NotFoundError = NotFoundError;
|
|
31
|
+
class ServerError extends Error {
|
|
32
|
+
constructor(message) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.name = "ServerError";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
exports.ServerError = ServerError;
|
|
38
|
+
async function request(url, apiKey, timeoutMs = 10000) {
|
|
39
|
+
const controller = new AbortController();
|
|
40
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
41
|
+
try {
|
|
42
|
+
const res = await fetch(url, {
|
|
43
|
+
headers: { "X-API-Key": apiKey },
|
|
44
|
+
signal: controller.signal,
|
|
45
|
+
});
|
|
46
|
+
const body = await res.json();
|
|
47
|
+
if (res.status === 401)
|
|
48
|
+
throw new AuthError(body.detail || "Unauthorized");
|
|
49
|
+
if (res.status === 402)
|
|
50
|
+
throw new RateLimitError(body.detail || "Insufficient credits");
|
|
51
|
+
if (res.status === 404)
|
|
52
|
+
throw new NotFoundError(body.detail || "Not found");
|
|
53
|
+
if (!res.ok)
|
|
54
|
+
throw new ServerError(`HTTP ${res.status}: ${body.detail || "Unknown error"}`);
|
|
55
|
+
return body;
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
clearTimeout(timer);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
class PuntersEdgeClient {
|
|
62
|
+
constructor(options) {
|
|
63
|
+
var _a, _b;
|
|
64
|
+
this.apiKey = options.apiKey;
|
|
65
|
+
this.baseUrl = (_a = options.baseUrl) !== null && _a !== void 0 ? _a : BASE_URL;
|
|
66
|
+
this.timeoutMs = (_b = options.timeoutMs) !== null && _b !== void 0 ? _b : 10000;
|
|
67
|
+
}
|
|
68
|
+
url(path) {
|
|
69
|
+
return `${this.baseUrl}${path}`;
|
|
70
|
+
}
|
|
71
|
+
/** List all active sports */
|
|
72
|
+
async sports() {
|
|
73
|
+
return request(this.url("/api/v1/sports"), this.apiKey, this.timeoutMs);
|
|
74
|
+
}
|
|
75
|
+
/** Get odds for a sport. markets = "h2h" | "spreads" | "totals" (comma-separated) */
|
|
76
|
+
async odds(sportKey, markets = "h2h", bookmakers) {
|
|
77
|
+
const params = new URLSearchParams({ markets });
|
|
78
|
+
if (bookmakers)
|
|
79
|
+
params.set("bookmakers", bookmakers);
|
|
80
|
+
return request(this.url(`/api/v1/sports/${sportKey}/odds?${params}`), this.apiKey, this.timeoutMs);
|
|
81
|
+
}
|
|
82
|
+
/** Best available price per selection + arb detection */
|
|
83
|
+
async bestOdds(sportKey) {
|
|
84
|
+
return request(this.url(`/api/v1/best-odds/${sportKey}`), this.apiKey, this.timeoutMs);
|
|
85
|
+
}
|
|
86
|
+
/** Next-to-go racing. categories = "horse" | "greyhound" | "harness" */
|
|
87
|
+
async nextToGo(numRaces = 5, categories) {
|
|
88
|
+
const params = new URLSearchParams({ num_races: String(numRaces) });
|
|
89
|
+
if (categories)
|
|
90
|
+
params.set("categories", categories);
|
|
91
|
+
return request(this.url(`/api/v1/racing/next-to-go?${params}`), this.apiKey, this.timeoutMs);
|
|
92
|
+
}
|
|
93
|
+
/** Current credit usage */
|
|
94
|
+
async usage() {
|
|
95
|
+
return request(this.url("/api/v1/usage"), this.apiKey, this.timeoutMs);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
exports.PuntersEdgeClient = PuntersEdgeClient;
|
|
99
|
+
exports.default = PuntersEdgeClient;
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "puntersedge",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Official Node.js client for the PuntersEdge Australian Sports Odds API",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"src",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"sports-odds",
|
|
18
|
+
"betting",
|
|
19
|
+
"australia",
|
|
20
|
+
"afl",
|
|
21
|
+
"nrl",
|
|
22
|
+
"api",
|
|
23
|
+
"puntersedge"
|
|
24
|
+
],
|
|
25
|
+
"author": "PuntersEdge",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"homepage": "https://puntersedge.online/api-platform",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/puntersedge/node-sdk"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"typescript": "^5.9.3"
|
|
34
|
+
}
|
|
35
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PuntersEdge Node.js SDK
|
|
3
|
+
* Official client for the Australian Sports Odds API
|
|
4
|
+
* https://puntersedge.online/api-platform
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const BASE_URL = "https://puntersedge.online";
|
|
8
|
+
|
|
9
|
+
export interface Sport {
|
|
10
|
+
key: string;
|
|
11
|
+
title: string;
|
|
12
|
+
group: string;
|
|
13
|
+
active: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface Outcome {
|
|
17
|
+
name: string;
|
|
18
|
+
price: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface Market {
|
|
22
|
+
key: string;
|
|
23
|
+
outcomes: Outcome[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface Bookmaker {
|
|
27
|
+
key: string;
|
|
28
|
+
title: string;
|
|
29
|
+
markets: Market[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface Event {
|
|
33
|
+
id: string;
|
|
34
|
+
home_team: string;
|
|
35
|
+
away_team: string;
|
|
36
|
+
commence_time: string;
|
|
37
|
+
bookmakers: Bookmaker[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface OddsResponse {
|
|
41
|
+
sport_key: string;
|
|
42
|
+
events: Event[];
|
|
43
|
+
fetched_at?: string;
|
|
44
|
+
data_age_seconds?: number;
|
|
45
|
+
stale?: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface BestOddsEvent {
|
|
49
|
+
id: string;
|
|
50
|
+
home_team: string;
|
|
51
|
+
away_team: string;
|
|
52
|
+
best_odds: Record<string, { price: number; bookmaker: string }>;
|
|
53
|
+
arb_opportunity: boolean;
|
|
54
|
+
arb_margin: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface BestOddsResponse {
|
|
58
|
+
sport_key: string;
|
|
59
|
+
events: BestOddsEvent[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface Runner {
|
|
63
|
+
name: string;
|
|
64
|
+
number?: number;
|
|
65
|
+
prices: Record<string, number>;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface Race {
|
|
69
|
+
id: string;
|
|
70
|
+
name: string;
|
|
71
|
+
venue: string;
|
|
72
|
+
category: string;
|
|
73
|
+
start_time: string;
|
|
74
|
+
runners: Runner[];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface RacingResponse {
|
|
78
|
+
races: Race[];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface Usage {
|
|
82
|
+
plan: string;
|
|
83
|
+
credits_limit: number;
|
|
84
|
+
credits_used: number;
|
|
85
|
+
credits_remaining: number;
|
|
86
|
+
reset_date: string;
|
|
87
|
+
usage_by_endpoint?: Record<string, number>;
|
|
88
|
+
recent_activity?: Array<{ endpoint: string; timestamp: string; credits: number }>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface PuntersEdgeOptions {
|
|
92
|
+
apiKey: string;
|
|
93
|
+
baseUrl?: string;
|
|
94
|
+
timeoutMs?: number;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export class AuthError extends Error {
|
|
98
|
+
constructor(message: string) {
|
|
99
|
+
super(message);
|
|
100
|
+
this.name = "AuthError";
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export class RateLimitError extends Error {
|
|
105
|
+
constructor(message: string) {
|
|
106
|
+
super(message);
|
|
107
|
+
this.name = "RateLimitError";
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export class NotFoundError extends Error {
|
|
112
|
+
constructor(message: string) {
|
|
113
|
+
super(message);
|
|
114
|
+
this.name = "NotFoundError";
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export class ServerError extends Error {
|
|
119
|
+
constructor(message: string) {
|
|
120
|
+
super(message);
|
|
121
|
+
this.name = "ServerError";
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function request<T>(
|
|
126
|
+
url: string,
|
|
127
|
+
apiKey: string,
|
|
128
|
+
timeoutMs = 10000
|
|
129
|
+
): Promise<T> {
|
|
130
|
+
const controller = new AbortController();
|
|
131
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const res = await fetch(url, {
|
|
135
|
+
headers: { "X-API-Key": apiKey },
|
|
136
|
+
signal: controller.signal,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const body = await res.json() as { detail?: string } & T;
|
|
140
|
+
|
|
141
|
+
if (res.status === 401) throw new AuthError((body as {detail?: string}).detail || "Unauthorized");
|
|
142
|
+
if (res.status === 402) throw new RateLimitError((body as {detail?: string}).detail || "Insufficient credits");
|
|
143
|
+
if (res.status === 404) throw new NotFoundError((body as {detail?: string}).detail || "Not found");
|
|
144
|
+
if (!res.ok) throw new ServerError(`HTTP ${res.status}: ${(body as {detail?: string}).detail || "Unknown error"}`);
|
|
145
|
+
|
|
146
|
+
return body;
|
|
147
|
+
} finally {
|
|
148
|
+
clearTimeout(timer);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export class PuntersEdgeClient {
|
|
153
|
+
private apiKey: string;
|
|
154
|
+
private baseUrl: string;
|
|
155
|
+
private timeoutMs: number;
|
|
156
|
+
|
|
157
|
+
constructor(options: PuntersEdgeOptions) {
|
|
158
|
+
this.apiKey = options.apiKey;
|
|
159
|
+
this.baseUrl = options.baseUrl ?? BASE_URL;
|
|
160
|
+
this.timeoutMs = options.timeoutMs ?? 10000;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private url(path: string): string {
|
|
164
|
+
return `${this.baseUrl}${path}`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** List all active sports */
|
|
168
|
+
async sports(): Promise<Sport[]> {
|
|
169
|
+
return request<Sport[]>(this.url("/api/v1/sports"), this.apiKey, this.timeoutMs);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Get odds for a sport. markets = "h2h" | "spreads" | "totals" (comma-separated) */
|
|
173
|
+
async odds(sportKey: string, markets = "h2h", bookmakers?: string): Promise<OddsResponse> {
|
|
174
|
+
const params = new URLSearchParams({ markets });
|
|
175
|
+
if (bookmakers) params.set("bookmakers", bookmakers);
|
|
176
|
+
return request<OddsResponse>(
|
|
177
|
+
this.url(`/api/v1/sports/${sportKey}/odds?${params}`),
|
|
178
|
+
this.apiKey,
|
|
179
|
+
this.timeoutMs
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Best available price per selection + arb detection */
|
|
184
|
+
async bestOdds(sportKey: string): Promise<BestOddsResponse> {
|
|
185
|
+
return request<BestOddsResponse>(
|
|
186
|
+
this.url(`/api/v1/best-odds/${sportKey}`),
|
|
187
|
+
this.apiKey,
|
|
188
|
+
this.timeoutMs
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Next-to-go racing. categories = "horse" | "greyhound" | "harness" */
|
|
193
|
+
async nextToGo(numRaces = 5, categories?: string): Promise<RacingResponse> {
|
|
194
|
+
const params = new URLSearchParams({ num_races: String(numRaces) });
|
|
195
|
+
if (categories) params.set("categories", categories);
|
|
196
|
+
return request<RacingResponse>(
|
|
197
|
+
this.url(`/api/v1/racing/next-to-go?${params}`),
|
|
198
|
+
this.apiKey,
|
|
199
|
+
this.timeoutMs
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Current credit usage */
|
|
204
|
+
async usage(): Promise<Usage> {
|
|
205
|
+
return request<Usage>(this.url("/api/v1/usage"), this.apiKey, this.timeoutMs);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export default PuntersEdgeClient;
|