arccos-api 0.4.2__tar.gz → 0.5.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.
- arccos_api-0.5.0/LICENSE +21 -0
- {arccos_api-0.4.2/arccos_api.egg-info → arccos_api-0.5.0}/PKG-INFO +87 -30
- {arccos_api-0.4.2 → arccos_api-0.5.0}/README.md +81 -27
- {arccos_api-0.4.2 → arccos_api-0.5.0}/arccos/__init__.py +4 -1
- {arccos_api-0.4.2 → arccos_api-0.5.0}/arccos/_http.py +10 -28
- {arccos_api-0.4.2 → arccos_api-0.5.0}/arccos/auth.py +20 -11
- {arccos_api-0.4.2 → arccos_api-0.5.0}/arccos/cli/__init__.py +3 -0
- {arccos_api-0.4.2 → arccos_api-0.5.0}/arccos/cli/auth.py +2 -1
- {arccos_api-0.4.2 → arccos_api-0.5.0}/arccos/cli/rounds.py +9 -3
- {arccos_api-0.4.2 → arccos_api-0.5.0}/arccos/cli/stats.py +477 -5
- {arccos_api-0.4.2 → arccos_api-0.5.0}/arccos/client.py +8 -0
- {arccos_api-0.4.2 → arccos_api-0.5.0}/arccos/resources/clubs.py +81 -2
- {arccos_api-0.4.2 → arccos_api-0.5.0}/arccos/resources/courses.py +53 -2
- {arccos_api-0.4.2 → arccos_api-0.5.0}/arccos/resources/stats.py +91 -0
- {arccos_api-0.4.2 → arccos_api-0.5.0/arccos_api.egg-info}/PKG-INFO +87 -30
- {arccos_api-0.4.2 → arccos_api-0.5.0}/arccos_api.egg-info/SOURCES.txt +1 -0
- {arccos_api-0.4.2 → arccos_api-0.5.0}/pyproject.toml +4 -3
- {arccos_api-0.4.2 → arccos_api-0.5.0}/tests/test_auth.py +20 -0
- {arccos_api-0.4.2 → arccos_api-0.5.0}/tests/test_cli.py +151 -8
- {arccos_api-0.4.2 → arccos_api-0.5.0}/tests/test_client.py +30 -0
- {arccos_api-0.4.2 → arccos_api-0.5.0}/tests/test_http.py +20 -54
- {arccos_api-0.4.2 → arccos_api-0.5.0}/tests/test_resources.py +121 -1
- {arccos_api-0.4.2 → arccos_api-0.5.0}/tests/test_security.py +45 -4
- {arccos_api-0.4.2 → arccos_api-0.5.0}/MANIFEST.in +0 -0
- {arccos_api-0.4.2 → arccos_api-0.5.0}/arccos/__main__.py +0 -0
- {arccos_api-0.4.2 → arccos_api-0.5.0}/arccos/cli/_helpers.py +0 -0
- {arccos_api-0.4.2 → arccos_api-0.5.0}/arccos/exceptions.py +0 -0
- {arccos_api-0.4.2 → arccos_api-0.5.0}/arccos/resources/__init__.py +0 -0
- {arccos_api-0.4.2 → arccos_api-0.5.0}/arccos/resources/handicap.py +0 -0
- {arccos_api-0.4.2 → arccos_api-0.5.0}/arccos/resources/rounds.py +0 -0
- {arccos_api-0.4.2 → arccos_api-0.5.0}/arccos/types.py +0 -0
- {arccos_api-0.4.2 → arccos_api-0.5.0}/arccos_api.egg-info/dependency_links.txt +0 -0
- {arccos_api-0.4.2 → arccos_api-0.5.0}/arccos_api.egg-info/entry_points.txt +0 -0
- {arccos_api-0.4.2 → arccos_api-0.5.0}/arccos_api.egg-info/requires.txt +0 -0
- {arccos_api-0.4.2 → arccos_api-0.5.0}/arccos_api.egg-info/top_level.txt +0 -0
- {arccos_api-0.4.2 → arccos_api-0.5.0}/setup.cfg +0 -0
- {arccos_api-0.4.2 → arccos_api-0.5.0}/tests/test_exceptions.py +0 -0
- {arccos_api-0.4.2 → arccos_api-0.5.0}/tests/test_rounds.py +0 -0
arccos_api-0.5.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Paul Frederiksen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: arccos-api
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Unofficial Python CLI and client library for the Arccos Golf API
|
|
5
5
|
Author: Paul Frederiksen
|
|
6
|
-
License: MIT
|
|
6
|
+
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/pfrederiksen/arccos-api
|
|
8
8
|
Project-URL: Repository, https://github.com/pfrederiksen/arccos-api
|
|
9
9
|
Project-URL: Issues, https://github.com/pfrederiksen/arccos-api/issues
|
|
10
|
+
Project-URL: PyPI, https://pypi.org/project/arccos-api/
|
|
11
|
+
Project-URL: Changelog, https://github.com/pfrederiksen/arccos-api/blob/main/CHANGELOG.md
|
|
10
12
|
Keywords: golf,arccos,api,cli,golf-stats
|
|
11
13
|
Classifier: Development Status :: 4 - Beta
|
|
12
14
|
Classifier: Environment :: Console
|
|
13
15
|
Classifier: Intended Audience :: Developers
|
|
14
16
|
Classifier: Intended Audience :: End Users/Desktop
|
|
15
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
16
17
|
Classifier: Programming Language :: Python :: 3
|
|
17
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -21,6 +22,7 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
21
22
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
23
|
Requires-Python: >=3.11
|
|
23
24
|
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
24
26
|
Requires-Dist: requests<3,>=2.31
|
|
25
27
|
Requires-Dist: click<9,>=8.1
|
|
26
28
|
Requires-Dist: rich<15,>=13.0
|
|
@@ -32,6 +34,7 @@ Requires-Dist: mypy; extra == "dev"
|
|
|
32
34
|
Requires-Dist: types-requests; extra == "dev"
|
|
33
35
|
Requires-Dist: build>=0.10.0; extra == "dev"
|
|
34
36
|
Requires-Dist: twine>=4.0.0; extra == "dev"
|
|
37
|
+
Dynamic: license-file
|
|
35
38
|
|
|
36
39
|
<p align="center">
|
|
37
40
|
<img src="assets/banner.png" alt="arccos-api banner" width="100%">
|
|
@@ -39,18 +42,30 @@ Requires-Dist: twine>=4.0.0; extra == "dev"
|
|
|
39
42
|
|
|
40
43
|
# arccos-api
|
|
41
44
|
|
|
45
|
+
[](https://pypi.org/project/arccos-api/)
|
|
46
|
+
|
|
42
47
|
Unofficial Python client and CLI for the [Arccos Golf](https://arccosgolf.com) platform.
|
|
43
48
|
|
|
44
|
-
Arccos provides no public API. This library
|
|
49
|
+
Arccos provides no public API. This library gives you read-only programmatic access to your own golf data — rounds, handicap, club distances, pace of play, and more.
|
|
45
50
|
|
|
46
51
|
> **Unofficial.** Not affiliated with Arccos Golf LLC. Use with your own account only. Respect their servers (no aggressive polling).
|
|
47
52
|
|
|
53
|
+
> **Read-only by design.** This client only sends `GET` requests to the Arccos data API. It cannot create, edit, or delete rounds, shots, clubs, courses, or account data. Authentication necessarily sends `POST` requests to the separate Arccos authentication service to log in and refresh tokens; those requests only obtain credentials and do not modify golf data.
|
|
54
|
+
|
|
48
55
|
## Installation
|
|
49
56
|
|
|
57
|
+
Install the `arccos-api` distribution from [PyPI](https://pypi.org/project/arccos-api/):
|
|
58
|
+
|
|
50
59
|
```bash
|
|
51
60
|
pip install arccos-api
|
|
52
61
|
```
|
|
53
62
|
|
|
63
|
+
Or install the CLI with Homebrew:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
brew install pfrederiksen/tap/arccos-api
|
|
67
|
+
```
|
|
68
|
+
|
|
54
69
|
Or install from source:
|
|
55
70
|
|
|
56
71
|
```bash
|
|
@@ -88,9 +103,13 @@ arccos clubs # smart club distances
|
|
|
88
103
|
| `arccos courses` | List courses you've played (or `--search` to filter) |
|
|
89
104
|
| `arccos pace` | Pace of play analysis by course (color-coded) |
|
|
90
105
|
| `arccos stats` | Strokes gained analysis (defaults to latest round) |
|
|
106
|
+
| `arccos stats --dashboard` | Strokes gained analysis from the current dashboard endpoint |
|
|
107
|
+
| `arccos tour-analytics` | Tour analytics summary/facet data |
|
|
91
108
|
| `arccos trends` | Handicap trend chart over recent rounds |
|
|
92
109
|
| `arccos club-shots <id>` | Shot history for a specific club |
|
|
110
|
+
| `arccos shot-dispersion [id]` | Shot dispersion for the bag or a specific club |
|
|
93
111
|
| `arccos up-and-down` | Scramble / strokes-to-get-down stats |
|
|
112
|
+
| `arccos hole-image <course> <hole>` | Hole feature metadata and dashboard image URLs |
|
|
94
113
|
| `arccos export` | Export rounds to JSON, CSV, or NDJSON (`--detail` for hole data) |
|
|
95
114
|
| `arccos completions <shell>` | Generate bash/zsh/fish completion script |
|
|
96
115
|
| `arccos logout` | Clear cached credentials |
|
|
@@ -108,7 +127,7 @@ arccos rounds --course "pebble" # filter by course name (substring)
|
|
|
108
127
|
arccos rounds --json # raw JSON
|
|
109
128
|
|
|
110
129
|
# Round detail
|
|
111
|
-
arccos round
|
|
130
|
+
arccos round 12345678 # hole-by-hole breakdown
|
|
112
131
|
|
|
113
132
|
# Handicap
|
|
114
133
|
arccos handicap # category breakdown
|
|
@@ -122,11 +141,15 @@ arccos clubs --after 2025-01-01 # distances from this year only
|
|
|
122
141
|
arccos bests # all-time personal bests
|
|
123
142
|
arccos overview # scoring avg + handicap breakdown
|
|
124
143
|
arccos scoring # scoring trend with bar chart
|
|
144
|
+
arccos stats --dashboard # dashboard SGA, same endpoint as web app
|
|
145
|
+
arccos stats --dashboard --latest # dashboard SGA for your latest round
|
|
125
146
|
arccos trends # handicap trend chart
|
|
126
147
|
arccos up-and-down # scramble stats
|
|
148
|
+
arccos tour-analytics --facet driving # tour analytics by facet
|
|
149
|
+
arccos tour-analytics --summary # tour analytics summary
|
|
127
150
|
|
|
128
151
|
# Compare rounds
|
|
129
|
-
arccos compare
|
|
152
|
+
arccos compare 12345678 12345679 # side-by-side hole comparison
|
|
130
153
|
|
|
131
154
|
# Courses
|
|
132
155
|
arccos courses # all courses played
|
|
@@ -134,6 +157,11 @@ arccos courses --search "torrey" # filter by name or city
|
|
|
134
157
|
|
|
135
158
|
# Club detail
|
|
136
159
|
arccos club-shots 1 # shot history for club ID 1
|
|
160
|
+
arccos shot-dispersion # bag-level dispersion data
|
|
161
|
+
arccos shot-dispersion 1 --json # raw dispersion for club ID 1
|
|
162
|
+
|
|
163
|
+
# Hole imagery metadata
|
|
164
|
+
arccos hole-image 10769 7 -v 11 # image + metadata URLs and hole features
|
|
137
165
|
|
|
138
166
|
# Shell completions
|
|
139
167
|
eval "$(arccos completions bash)" # enable tab completion
|
|
@@ -158,24 +186,24 @@ $ arccos clubs
|
|
|
158
186
|
╭──────┬──────────────────┬───────┬─────────┬──────────┬───────╮
|
|
159
187
|
│ Club │ Model │ Smart │ Longest │ Range │ Shots │
|
|
160
188
|
├──────┼──────────────────┼───────┼─────────┼──────────┼───────┤
|
|
161
|
-
│ Dr │
|
|
162
|
-
│ 3w │
|
|
163
|
-
│ 5w │
|
|
164
|
-
│ 4h │
|
|
165
|
-
│ 6i │
|
|
166
|
-
│ 7i │
|
|
167
|
-
│ 8i │
|
|
168
|
-
│ 9i │
|
|
169
|
-
│ Pw │
|
|
189
|
+
│ Dr │ Example Driver │ 252y │ 291y │ 245–263y │ 312 │
|
|
190
|
+
│ 3w │ Example Fairway │ 228y │ 248y │ 220–235y │ 87 │
|
|
191
|
+
│ 5w │ Example Fairway │ 208y │ 231y │ 200–215y │ 143 │
|
|
192
|
+
│ 4h │ Example Hybrid │ 195y │ 214y │ 188–202y │ 61 │
|
|
193
|
+
│ 6i │ Example Iron │ 172y │ 190y │ 165–179y │ 94 │
|
|
194
|
+
│ 7i │ Example Iron │ 160y │ 178y │ 153–167y │ 118 │
|
|
195
|
+
│ 8i │ Example Iron │ 148y │ 165y │ 141–155y │ 105 │
|
|
196
|
+
│ 9i │ Example Iron │ 136y │ 152y │ 130–142y │ 97 │
|
|
197
|
+
│ Pw │ Example Wedge │ 124y │ 140y │ 118–130y │ 82 │
|
|
170
198
|
│ 50 │ Vokey SM10 │ 108y │ 125y │ 102–114y │ 76 │
|
|
171
199
|
│ 54 │ Vokey SM10 │ 88y │ 105y │ 82–94y │ 201 │
|
|
172
200
|
│ 58 │ Vokey SM10 │ 64y │ 88y │ 58–70y │ 168 │
|
|
173
201
|
╰──────┴──────────────────┴───────┴─────────┴──────────┴───────╯
|
|
174
202
|
|
|
175
|
-
$ arccos round
|
|
176
|
-
╭─── Round
|
|
177
|
-
│ Date:
|
|
178
|
-
│ Course:
|
|
203
|
+
$ arccos round 12345678
|
|
204
|
+
╭─── Round 12345678 ────╮
|
|
205
|
+
│ Date: 2026-01-15 │
|
|
206
|
+
│ Course: Example GC │
|
|
179
207
|
│ Score: 79 (+7) │
|
|
180
208
|
│ Holes: 18 │
|
|
181
209
|
╰───────────────────────╯
|
|
@@ -210,9 +238,9 @@ Pace of Play — 10 rounds across 6 courses Overall avg: 4h 22m
|
|
|
210
238
|
╭───┬──────────┬───────────────────────┬────────╮
|
|
211
239
|
│ │ Avg Time │ Course │ Rounds │
|
|
212
240
|
├───┼──────────┼───────────────────────┼────────┤
|
|
213
|
-
│ ● │ 5h 10m │
|
|
214
|
-
│ ● │ 4h 45m │
|
|
215
|
-
│ ● │ 3h 50m │
|
|
241
|
+
│ ● │ 5h 10m │ Example Golf Club A │ 2 │
|
|
242
|
+
│ ● │ 4h 45m │ Example Golf Club B │ 3 │
|
|
243
|
+
│ ● │ 3h 50m │ Example Golf Club C │ 2 │
|
|
216
244
|
│ ● │ 3h 35m │ Chambers Bay │ 1 │
|
|
217
245
|
╰───┴──────────┴───────────────────────┴────────╯
|
|
218
246
|
```
|
|
@@ -265,12 +293,24 @@ for club in client.clubs.smart_distances():
|
|
|
265
293
|
print(f" Club {club['clubId']}: {dist:.0f}y")
|
|
266
294
|
|
|
267
295
|
# Bag configuration (maps clubId → clubType + make/model)
|
|
268
|
-
profile = client.
|
|
296
|
+
profile = client.profile()
|
|
269
297
|
bag = client.clubs.bag(str(profile["bagId"]))
|
|
270
298
|
for club in bag["clubs"]:
|
|
271
299
|
if club.get("isDeleted") != "T":
|
|
272
300
|
print(f" {club['clubMakeOther']} {club['clubModelOther']}")
|
|
273
301
|
|
|
302
|
+
# Current dashboard club/SGA analytics endpoints
|
|
303
|
+
club_inventory = client.clubs.list()
|
|
304
|
+
shot_dispersion = client.clubs.shot_dispersion()
|
|
305
|
+
dashboard_sga = client.stats.dashboard_analysis(goal_hcp=5, no_of_rounds=20)
|
|
306
|
+
player_profile = client.stats.player_profile()
|
|
307
|
+
tour_summary = client.stats.tour_analytics_summary()
|
|
308
|
+
|
|
309
|
+
# Course / hole imagery metadata
|
|
310
|
+
hole_features = client.courses.hole_features(10769, 7, version=11)
|
|
311
|
+
image_url = client.courses.hole_image_url(10769, 7, 11, "2026-07")
|
|
312
|
+
metadata_url = client.courses.hole_metadata_url(10769, 7, 11, "2026-07")
|
|
313
|
+
|
|
274
314
|
# Courses played
|
|
275
315
|
for course in client.courses.played():
|
|
276
316
|
print(f" {course.get('name', course['courseId'])}")
|
|
@@ -289,7 +329,7 @@ for r in client.rounds.iter_all():
|
|
|
289
329
|
print(r["roundId"], r["noOfShots"])
|
|
290
330
|
|
|
291
331
|
# Course search
|
|
292
|
-
results = client.courses.search("
|
|
332
|
+
results = client.courses.search("Example Golf Club")
|
|
293
333
|
```
|
|
294
334
|
|
|
295
335
|
## API Reference
|
|
@@ -300,6 +340,8 @@ Full OpenAPI 3.1 spec: [`docs/openapi.yaml`](docs/openapi.yaml)
|
|
|
300
340
|
|
|
301
341
|
All auth calls go to `https://authentication.arccosgolf.com`.
|
|
302
342
|
|
|
343
|
+
These are the only non-GET Arccos interactions in this project. They obtain an access key and JWT; they do not write golf or account data.
|
|
344
|
+
|
|
303
345
|
| Step | Endpoint | Body | Returns |
|
|
304
346
|
|------|----------|------|---------|
|
|
305
347
|
| 1. Get access key | `POST /accessKeys` | `{"email", "password", "signedInByFacebook": "F"}` | `{"userId", "accessKey", "secret"}` |
|
|
@@ -311,19 +353,31 @@ The JWT expires in ~3 hours. The `accessKey` is valid for ~180 days. Refresh by
|
|
|
311
353
|
|
|
312
354
|
All data calls go to `https://api.arccosgolf.com` with `Authorization: Bearer <token>`.
|
|
313
355
|
|
|
356
|
+
The data client enforces `GET` only and exposes no `POST`, `PUT`, `PATCH`, or `DELETE` methods.
|
|
357
|
+
|
|
314
358
|
| Resource | Method | Path |
|
|
315
359
|
|----------|--------|------|
|
|
316
360
|
| User profile | GET | `/users/{userId}` (includes `bagId`, `bags[]`) |
|
|
361
|
+
| User details | GET | `/users/{userId}/details` |
|
|
317
362
|
| Bag / clubs | GET | `/users/{userId}/bags/{bagId}` (club config with make/model) |
|
|
363
|
+
| Club inventory | GET | `/v6/users/{userId}/clubs` |
|
|
318
364
|
| Rounds | GET | `/users/{userId}/rounds?offSet=0&limit=200&roundType=flagship` |
|
|
319
365
|
| Round detail | GET | `/users/{userId}/rounds/{roundId}` (includes `holes[]` with `shots[]`) |
|
|
320
366
|
| Handicap | GET | `/users/{userId}/handicaps/latest` |
|
|
321
367
|
| Handicap history | GET | `/users/{userId}/handicaps?rounds=20` |
|
|
322
368
|
| Smart distances | GET | `/v4/clubs/user/{userId}/smart-distances` |
|
|
369
|
+
| Shot dispersion | GET | `/v3/user/{userId}/clubs/dispersion` |
|
|
370
|
+
| Club shot dispersion | GET | `/v3/user/{userId}/clubs/{clubId}/dispersion` |
|
|
323
371
|
| Courses played | GET | `/users/{userId}/coursesPlayed` |
|
|
324
372
|
| Course metadata | GET | `/courses/{courseId}?courseVersion=1` |
|
|
373
|
+
| Hole feature metadata | GET | `/courses/{courseId}/holes/{holeId}?courseVersion=1` |
|
|
325
374
|
| Course search | GET | `/v2/courses?search={query}` |
|
|
326
375
|
| Club shots | GET | `/users/{userId}/bags/{bagId}/clubs/{clubId}/shots` |
|
|
376
|
+
| Dashboard SGA | GET | `/sga/getDashboardAnalysis/{userId}` |
|
|
377
|
+
| SGA player profile | GET | `/sga/playerProfile/{userId}` |
|
|
378
|
+
| Tour analytics | GET | `/users/{userId}/tourAnalytics` |
|
|
379
|
+
| Tour analytics summary | GET | `/users/{userId}/tourAnalyticsSummary` |
|
|
380
|
+
| Tour analytics ranked | GET | `/users/{userId}/tourAnalyticsRanked` |
|
|
327
381
|
| Personal bests | GET | `/users/{userId}/personalBests?tags=allTimeBest` |
|
|
328
382
|
| Strokes to get down | GET | `/v2/sga/strokes-to-get-down` |
|
|
329
383
|
|
|
@@ -332,11 +386,11 @@ All data calls go to `https://api.arccosgolf.com` with `Authorization: Bearer <t
|
|
|
332
386
|
**Round** (from `GET /users/{id}/rounds/{roundId}`):
|
|
333
387
|
```json
|
|
334
388
|
{
|
|
335
|
-
"roundId":
|
|
389
|
+
"roundId": 12345678,
|
|
336
390
|
"courseId": 12450,
|
|
337
391
|
"courseVersion": 4,
|
|
338
|
-
"startTime": "
|
|
339
|
-
"endTime": "
|
|
392
|
+
"startTime": "2026-01-15T15:30:00.000000Z",
|
|
393
|
+
"endTime": "2026-01-15T20:15:00.000000Z",
|
|
340
394
|
"noOfShots": 79,
|
|
341
395
|
"noOfHoles": 18,
|
|
342
396
|
"holes": [
|
|
@@ -364,7 +418,7 @@ All data calls go to `https://api.arccosgolf.com` with `Authorization: Bearer <t
|
|
|
364
418
|
```json
|
|
365
419
|
{
|
|
366
420
|
"clubId": 1, "clubType": 1,
|
|
367
|
-
"clubMakeOther": "
|
|
421
|
+
"clubMakeOther": "Example", "clubModelOther": "Model 1",
|
|
368
422
|
"isDeleted": "F"
|
|
369
423
|
}
|
|
370
424
|
```
|
|
@@ -378,19 +432,22 @@ python3 -m venv .venv
|
|
|
378
432
|
source .venv/bin/activate
|
|
379
433
|
pip install -e ".[dev]"
|
|
380
434
|
|
|
381
|
-
pytest # tests with coverage (87%+,
|
|
382
|
-
ruff check
|
|
435
|
+
pytest # tests with coverage (87%+, 173 tests)
|
|
436
|
+
ruff check . # lint
|
|
383
437
|
mypy arccos/ # type check
|
|
384
438
|
```
|
|
385
439
|
|
|
386
440
|
Coverage report: `htmlcov/index.html`. Minimum threshold: 80% (configured in `pyproject.toml`).
|
|
441
|
+
Current suite: 173 tests.
|
|
387
442
|
|
|
388
443
|
## Security
|
|
389
444
|
|
|
445
|
+
- Data access is read-only: the client only sends `GET` requests to `api.arccosgolf.com`. The only `POST` requests are the required login/token-refresh calls to `authentication.arccosgolf.com`; no golf data is created, changed, or deleted.
|
|
390
446
|
- Credentials are cached in `~/.arccos_creds.json` (mode `0600` — owner-only read/write). The file contains your access key (~180-day validity) and JWT token. Keep it safe.
|
|
391
447
|
- All API communication uses HTTPS with TLS certificate verification.
|
|
392
448
|
- `arccos logout` removes the local credential file but does **not** revoke server-side tokens. If you suspect your credentials were compromised, change your Arccos password.
|
|
393
449
|
- The export command restricts output paths to your home directory or current working directory.
|
|
450
|
+
- Security issues should be reported privately as described in [`SECURITY.md`](SECURITY.md).
|
|
394
451
|
|
|
395
452
|
## Disclaimer
|
|
396
453
|
|
|
@@ -4,18 +4,30 @@
|
|
|
4
4
|
|
|
5
5
|
# arccos-api
|
|
6
6
|
|
|
7
|
+
[](https://pypi.org/project/arccos-api/)
|
|
8
|
+
|
|
7
9
|
Unofficial Python client and CLI for the [Arccos Golf](https://arccosgolf.com) platform.
|
|
8
10
|
|
|
9
|
-
Arccos provides no public API. This library
|
|
11
|
+
Arccos provides no public API. This library gives you read-only programmatic access to your own golf data — rounds, handicap, club distances, pace of play, and more.
|
|
10
12
|
|
|
11
13
|
> **Unofficial.** Not affiliated with Arccos Golf LLC. Use with your own account only. Respect their servers (no aggressive polling).
|
|
12
14
|
|
|
15
|
+
> **Read-only by design.** This client only sends `GET` requests to the Arccos data API. It cannot create, edit, or delete rounds, shots, clubs, courses, or account data. Authentication necessarily sends `POST` requests to the separate Arccos authentication service to log in and refresh tokens; those requests only obtain credentials and do not modify golf data.
|
|
16
|
+
|
|
13
17
|
## Installation
|
|
14
18
|
|
|
19
|
+
Install the `arccos-api` distribution from [PyPI](https://pypi.org/project/arccos-api/):
|
|
20
|
+
|
|
15
21
|
```bash
|
|
16
22
|
pip install arccos-api
|
|
17
23
|
```
|
|
18
24
|
|
|
25
|
+
Or install the CLI with Homebrew:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
brew install pfrederiksen/tap/arccos-api
|
|
29
|
+
```
|
|
30
|
+
|
|
19
31
|
Or install from source:
|
|
20
32
|
|
|
21
33
|
```bash
|
|
@@ -53,9 +65,13 @@ arccos clubs # smart club distances
|
|
|
53
65
|
| `arccos courses` | List courses you've played (or `--search` to filter) |
|
|
54
66
|
| `arccos pace` | Pace of play analysis by course (color-coded) |
|
|
55
67
|
| `arccos stats` | Strokes gained analysis (defaults to latest round) |
|
|
68
|
+
| `arccos stats --dashboard` | Strokes gained analysis from the current dashboard endpoint |
|
|
69
|
+
| `arccos tour-analytics` | Tour analytics summary/facet data |
|
|
56
70
|
| `arccos trends` | Handicap trend chart over recent rounds |
|
|
57
71
|
| `arccos club-shots <id>` | Shot history for a specific club |
|
|
72
|
+
| `arccos shot-dispersion [id]` | Shot dispersion for the bag or a specific club |
|
|
58
73
|
| `arccos up-and-down` | Scramble / strokes-to-get-down stats |
|
|
74
|
+
| `arccos hole-image <course> <hole>` | Hole feature metadata and dashboard image URLs |
|
|
59
75
|
| `arccos export` | Export rounds to JSON, CSV, or NDJSON (`--detail` for hole data) |
|
|
60
76
|
| `arccos completions <shell>` | Generate bash/zsh/fish completion script |
|
|
61
77
|
| `arccos logout` | Clear cached credentials |
|
|
@@ -73,7 +89,7 @@ arccos rounds --course "pebble" # filter by course name (substring)
|
|
|
73
89
|
arccos rounds --json # raw JSON
|
|
74
90
|
|
|
75
91
|
# Round detail
|
|
76
|
-
arccos round
|
|
92
|
+
arccos round 12345678 # hole-by-hole breakdown
|
|
77
93
|
|
|
78
94
|
# Handicap
|
|
79
95
|
arccos handicap # category breakdown
|
|
@@ -87,11 +103,15 @@ arccos clubs --after 2025-01-01 # distances from this year only
|
|
|
87
103
|
arccos bests # all-time personal bests
|
|
88
104
|
arccos overview # scoring avg + handicap breakdown
|
|
89
105
|
arccos scoring # scoring trend with bar chart
|
|
106
|
+
arccos stats --dashboard # dashboard SGA, same endpoint as web app
|
|
107
|
+
arccos stats --dashboard --latest # dashboard SGA for your latest round
|
|
90
108
|
arccos trends # handicap trend chart
|
|
91
109
|
arccos up-and-down # scramble stats
|
|
110
|
+
arccos tour-analytics --facet driving # tour analytics by facet
|
|
111
|
+
arccos tour-analytics --summary # tour analytics summary
|
|
92
112
|
|
|
93
113
|
# Compare rounds
|
|
94
|
-
arccos compare
|
|
114
|
+
arccos compare 12345678 12345679 # side-by-side hole comparison
|
|
95
115
|
|
|
96
116
|
# Courses
|
|
97
117
|
arccos courses # all courses played
|
|
@@ -99,6 +119,11 @@ arccos courses --search "torrey" # filter by name or city
|
|
|
99
119
|
|
|
100
120
|
# Club detail
|
|
101
121
|
arccos club-shots 1 # shot history for club ID 1
|
|
122
|
+
arccos shot-dispersion # bag-level dispersion data
|
|
123
|
+
arccos shot-dispersion 1 --json # raw dispersion for club ID 1
|
|
124
|
+
|
|
125
|
+
# Hole imagery metadata
|
|
126
|
+
arccos hole-image 10769 7 -v 11 # image + metadata URLs and hole features
|
|
102
127
|
|
|
103
128
|
# Shell completions
|
|
104
129
|
eval "$(arccos completions bash)" # enable tab completion
|
|
@@ -123,24 +148,24 @@ $ arccos clubs
|
|
|
123
148
|
╭──────┬──────────────────┬───────┬─────────┬──────────┬───────╮
|
|
124
149
|
│ Club │ Model │ Smart │ Longest │ Range │ Shots │
|
|
125
150
|
├──────┼──────────────────┼───────┼─────────┼──────────┼───────┤
|
|
126
|
-
│ Dr │
|
|
127
|
-
│ 3w │
|
|
128
|
-
│ 5w │
|
|
129
|
-
│ 4h │
|
|
130
|
-
│ 6i │
|
|
131
|
-
│ 7i │
|
|
132
|
-
│ 8i │
|
|
133
|
-
│ 9i │
|
|
134
|
-
│ Pw │
|
|
151
|
+
│ Dr │ Example Driver │ 252y │ 291y │ 245–263y │ 312 │
|
|
152
|
+
│ 3w │ Example Fairway │ 228y │ 248y │ 220–235y │ 87 │
|
|
153
|
+
│ 5w │ Example Fairway │ 208y │ 231y │ 200–215y │ 143 │
|
|
154
|
+
│ 4h │ Example Hybrid │ 195y │ 214y │ 188–202y │ 61 │
|
|
155
|
+
│ 6i │ Example Iron │ 172y │ 190y │ 165–179y │ 94 │
|
|
156
|
+
│ 7i │ Example Iron │ 160y │ 178y │ 153–167y │ 118 │
|
|
157
|
+
│ 8i │ Example Iron │ 148y │ 165y │ 141–155y │ 105 │
|
|
158
|
+
│ 9i │ Example Iron │ 136y │ 152y │ 130–142y │ 97 │
|
|
159
|
+
│ Pw │ Example Wedge │ 124y │ 140y │ 118–130y │ 82 │
|
|
135
160
|
│ 50 │ Vokey SM10 │ 108y │ 125y │ 102–114y │ 76 │
|
|
136
161
|
│ 54 │ Vokey SM10 │ 88y │ 105y │ 82–94y │ 201 │
|
|
137
162
|
│ 58 │ Vokey SM10 │ 64y │ 88y │ 58–70y │ 168 │
|
|
138
163
|
╰──────┴──────────────────┴───────┴─────────┴──────────┴───────╯
|
|
139
164
|
|
|
140
|
-
$ arccos round
|
|
141
|
-
╭─── Round
|
|
142
|
-
│ Date:
|
|
143
|
-
│ Course:
|
|
165
|
+
$ arccos round 12345678
|
|
166
|
+
╭─── Round 12345678 ────╮
|
|
167
|
+
│ Date: 2026-01-15 │
|
|
168
|
+
│ Course: Example GC │
|
|
144
169
|
│ Score: 79 (+7) │
|
|
145
170
|
│ Holes: 18 │
|
|
146
171
|
╰───────────────────────╯
|
|
@@ -175,9 +200,9 @@ Pace of Play — 10 rounds across 6 courses Overall avg: 4h 22m
|
|
|
175
200
|
╭───┬──────────┬───────────────────────┬────────╮
|
|
176
201
|
│ │ Avg Time │ Course │ Rounds │
|
|
177
202
|
├───┼──────────┼───────────────────────┼────────┤
|
|
178
|
-
│ ● │ 5h 10m │
|
|
179
|
-
│ ● │ 4h 45m │
|
|
180
|
-
│ ● │ 3h 50m │
|
|
203
|
+
│ ● │ 5h 10m │ Example Golf Club A │ 2 │
|
|
204
|
+
│ ● │ 4h 45m │ Example Golf Club B │ 3 │
|
|
205
|
+
│ ● │ 3h 50m │ Example Golf Club C │ 2 │
|
|
181
206
|
│ ● │ 3h 35m │ Chambers Bay │ 1 │
|
|
182
207
|
╰───┴──────────┴───────────────────────┴────────╯
|
|
183
208
|
```
|
|
@@ -230,12 +255,24 @@ for club in client.clubs.smart_distances():
|
|
|
230
255
|
print(f" Club {club['clubId']}: {dist:.0f}y")
|
|
231
256
|
|
|
232
257
|
# Bag configuration (maps clubId → clubType + make/model)
|
|
233
|
-
profile = client.
|
|
258
|
+
profile = client.profile()
|
|
234
259
|
bag = client.clubs.bag(str(profile["bagId"]))
|
|
235
260
|
for club in bag["clubs"]:
|
|
236
261
|
if club.get("isDeleted") != "T":
|
|
237
262
|
print(f" {club['clubMakeOther']} {club['clubModelOther']}")
|
|
238
263
|
|
|
264
|
+
# Current dashboard club/SGA analytics endpoints
|
|
265
|
+
club_inventory = client.clubs.list()
|
|
266
|
+
shot_dispersion = client.clubs.shot_dispersion()
|
|
267
|
+
dashboard_sga = client.stats.dashboard_analysis(goal_hcp=5, no_of_rounds=20)
|
|
268
|
+
player_profile = client.stats.player_profile()
|
|
269
|
+
tour_summary = client.stats.tour_analytics_summary()
|
|
270
|
+
|
|
271
|
+
# Course / hole imagery metadata
|
|
272
|
+
hole_features = client.courses.hole_features(10769, 7, version=11)
|
|
273
|
+
image_url = client.courses.hole_image_url(10769, 7, 11, "2026-07")
|
|
274
|
+
metadata_url = client.courses.hole_metadata_url(10769, 7, 11, "2026-07")
|
|
275
|
+
|
|
239
276
|
# Courses played
|
|
240
277
|
for course in client.courses.played():
|
|
241
278
|
print(f" {course.get('name', course['courseId'])}")
|
|
@@ -254,7 +291,7 @@ for r in client.rounds.iter_all():
|
|
|
254
291
|
print(r["roundId"], r["noOfShots"])
|
|
255
292
|
|
|
256
293
|
# Course search
|
|
257
|
-
results = client.courses.search("
|
|
294
|
+
results = client.courses.search("Example Golf Club")
|
|
258
295
|
```
|
|
259
296
|
|
|
260
297
|
## API Reference
|
|
@@ -265,6 +302,8 @@ Full OpenAPI 3.1 spec: [`docs/openapi.yaml`](docs/openapi.yaml)
|
|
|
265
302
|
|
|
266
303
|
All auth calls go to `https://authentication.arccosgolf.com`.
|
|
267
304
|
|
|
305
|
+
These are the only non-GET Arccos interactions in this project. They obtain an access key and JWT; they do not write golf or account data.
|
|
306
|
+
|
|
268
307
|
| Step | Endpoint | Body | Returns |
|
|
269
308
|
|------|----------|------|---------|
|
|
270
309
|
| 1. Get access key | `POST /accessKeys` | `{"email", "password", "signedInByFacebook": "F"}` | `{"userId", "accessKey", "secret"}` |
|
|
@@ -276,19 +315,31 @@ The JWT expires in ~3 hours. The `accessKey` is valid for ~180 days. Refresh by
|
|
|
276
315
|
|
|
277
316
|
All data calls go to `https://api.arccosgolf.com` with `Authorization: Bearer <token>`.
|
|
278
317
|
|
|
318
|
+
The data client enforces `GET` only and exposes no `POST`, `PUT`, `PATCH`, or `DELETE` methods.
|
|
319
|
+
|
|
279
320
|
| Resource | Method | Path |
|
|
280
321
|
|----------|--------|------|
|
|
281
322
|
| User profile | GET | `/users/{userId}` (includes `bagId`, `bags[]`) |
|
|
323
|
+
| User details | GET | `/users/{userId}/details` |
|
|
282
324
|
| Bag / clubs | GET | `/users/{userId}/bags/{bagId}` (club config with make/model) |
|
|
325
|
+
| Club inventory | GET | `/v6/users/{userId}/clubs` |
|
|
283
326
|
| Rounds | GET | `/users/{userId}/rounds?offSet=0&limit=200&roundType=flagship` |
|
|
284
327
|
| Round detail | GET | `/users/{userId}/rounds/{roundId}` (includes `holes[]` with `shots[]`) |
|
|
285
328
|
| Handicap | GET | `/users/{userId}/handicaps/latest` |
|
|
286
329
|
| Handicap history | GET | `/users/{userId}/handicaps?rounds=20` |
|
|
287
330
|
| Smart distances | GET | `/v4/clubs/user/{userId}/smart-distances` |
|
|
331
|
+
| Shot dispersion | GET | `/v3/user/{userId}/clubs/dispersion` |
|
|
332
|
+
| Club shot dispersion | GET | `/v3/user/{userId}/clubs/{clubId}/dispersion` |
|
|
288
333
|
| Courses played | GET | `/users/{userId}/coursesPlayed` |
|
|
289
334
|
| Course metadata | GET | `/courses/{courseId}?courseVersion=1` |
|
|
335
|
+
| Hole feature metadata | GET | `/courses/{courseId}/holes/{holeId}?courseVersion=1` |
|
|
290
336
|
| Course search | GET | `/v2/courses?search={query}` |
|
|
291
337
|
| Club shots | GET | `/users/{userId}/bags/{bagId}/clubs/{clubId}/shots` |
|
|
338
|
+
| Dashboard SGA | GET | `/sga/getDashboardAnalysis/{userId}` |
|
|
339
|
+
| SGA player profile | GET | `/sga/playerProfile/{userId}` |
|
|
340
|
+
| Tour analytics | GET | `/users/{userId}/tourAnalytics` |
|
|
341
|
+
| Tour analytics summary | GET | `/users/{userId}/tourAnalyticsSummary` |
|
|
342
|
+
| Tour analytics ranked | GET | `/users/{userId}/tourAnalyticsRanked` |
|
|
292
343
|
| Personal bests | GET | `/users/{userId}/personalBests?tags=allTimeBest` |
|
|
293
344
|
| Strokes to get down | GET | `/v2/sga/strokes-to-get-down` |
|
|
294
345
|
|
|
@@ -297,11 +348,11 @@ All data calls go to `https://api.arccosgolf.com` with `Authorization: Bearer <t
|
|
|
297
348
|
**Round** (from `GET /users/{id}/rounds/{roundId}`):
|
|
298
349
|
```json
|
|
299
350
|
{
|
|
300
|
-
"roundId":
|
|
351
|
+
"roundId": 12345678,
|
|
301
352
|
"courseId": 12450,
|
|
302
353
|
"courseVersion": 4,
|
|
303
|
-
"startTime": "
|
|
304
|
-
"endTime": "
|
|
354
|
+
"startTime": "2026-01-15T15:30:00.000000Z",
|
|
355
|
+
"endTime": "2026-01-15T20:15:00.000000Z",
|
|
305
356
|
"noOfShots": 79,
|
|
306
357
|
"noOfHoles": 18,
|
|
307
358
|
"holes": [
|
|
@@ -329,7 +380,7 @@ All data calls go to `https://api.arccosgolf.com` with `Authorization: Bearer <t
|
|
|
329
380
|
```json
|
|
330
381
|
{
|
|
331
382
|
"clubId": 1, "clubType": 1,
|
|
332
|
-
"clubMakeOther": "
|
|
383
|
+
"clubMakeOther": "Example", "clubModelOther": "Model 1",
|
|
333
384
|
"isDeleted": "F"
|
|
334
385
|
}
|
|
335
386
|
```
|
|
@@ -343,19 +394,22 @@ python3 -m venv .venv
|
|
|
343
394
|
source .venv/bin/activate
|
|
344
395
|
pip install -e ".[dev]"
|
|
345
396
|
|
|
346
|
-
pytest # tests with coverage (87%+,
|
|
347
|
-
ruff check
|
|
397
|
+
pytest # tests with coverage (87%+, 173 tests)
|
|
398
|
+
ruff check . # lint
|
|
348
399
|
mypy arccos/ # type check
|
|
349
400
|
```
|
|
350
401
|
|
|
351
402
|
Coverage report: `htmlcov/index.html`. Minimum threshold: 80% (configured in `pyproject.toml`).
|
|
403
|
+
Current suite: 173 tests.
|
|
352
404
|
|
|
353
405
|
## Security
|
|
354
406
|
|
|
407
|
+
- Data access is read-only: the client only sends `GET` requests to `api.arccosgolf.com`. The only `POST` requests are the required login/token-refresh calls to `authentication.arccosgolf.com`; no golf data is created, changed, or deleted.
|
|
355
408
|
- Credentials are cached in `~/.arccos_creds.json` (mode `0600` — owner-only read/write). The file contains your access key (~180-day validity) and JWT token. Keep it safe.
|
|
356
409
|
- All API communication uses HTTPS with TLS certificate verification.
|
|
357
410
|
- `arccos logout` removes the local credential file but does **not** revoke server-side tokens. If you suspect your credentials were compromised, change your Arccos password.
|
|
358
411
|
- The export command restricts output paths to your home directory or current working directory.
|
|
412
|
+
- Security issues should be reported privately as described in [`SECURITY.md`](SECURITY.md).
|
|
359
413
|
|
|
360
414
|
## Disclaimer
|
|
361
415
|
|
|
@@ -50,7 +50,8 @@ class _SensitiveFilter(_logging.Filter):
|
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
_logger = _logging.getLogger("arccos")
|
|
53
|
-
|
|
53
|
+
_sensitive_filter = _SensitiveFilter()
|
|
54
|
+
_logger.addFilter(_sensitive_filter)
|
|
54
55
|
|
|
55
56
|
# Allow ARCCOS_LOG_LEVEL=DEBUG (or INFO, WARNING, etc.) to enable logging
|
|
56
57
|
_log_level = _os.environ.get("ARCCOS_LOG_LEVEL")
|
|
@@ -59,6 +60,8 @@ if _log_level:
|
|
|
59
60
|
if not _logger.handlers:
|
|
60
61
|
_handler = _logging.StreamHandler()
|
|
61
62
|
_handler.setFormatter(_logging.Formatter("%(name)s %(levelname)s: %(message)s"))
|
|
63
|
+
# Handler filters also cover records propagated by child loggers.
|
|
64
|
+
_handler.addFilter(_sensitive_filter)
|
|
62
65
|
_logger.addHandler(_handler)
|
|
63
66
|
|
|
64
67
|
__version__ = _pkg_version("arccos-api")
|