A CLI for downloading and processing DUPR data into a local SQLite database.
The CLI has been rebuilt around a single entrypoint with task-based commands:
duprly sync ...duprly browse ...duprly explore ...duprly export ...duprly db ...duprly doctor ...
Running duprly with no subcommand opens an interactive menu in TTY sessions.
Create a .env file (or pass one via --config):
DUPR_USERNAME=<username>
DUPR_PASSWORD=<password>
DUPR_CLUB_ID=8436164521Install dependencies:
pip install -r requirements.txtduprly [--verbose] [--quiet] [--no-color] [--json] [--config PATH] [--interactive|--no-interactive] ...# interactive mode (TTY)
duprly
# sync everything
duprly sync all
# sync only players from a specific club
duprly sync players --club-id 8436164521
# sync matches for everyone currently in local DB
duprly sync matches --all-players
# refresh ratings only where missing
duprly sync ratings
# browse a player and persist to DB
duprly browse player 6886613721
# fetch one player's full match history (ALL by default) and persist
duprly browse matches 6886613721
# fetch one player's match history in a bounded date range
duprly browse matches 6886613721 --start-date 2025-01-01 --end-date 2025-12-31
# fetch one player's rating history (defaults: both types, last 2 years)
duprly browse rating-history 6886613721
# fetch only doubles rating history for a specific date range
duprly browse rating-history 6886613721 --type doubles --start-date 2025-12-14 --end-date 2026-02-21
# live lookup by player name with suggestions, then optional match download
duprly browse lookup --entity player --query "aidan bai"
# open interactive saved-data explorer (no API calls)
duprly explore
# list saved players and export to CSV
duprly explore players --query "sternlicht" --export csv --output explore_players.csv
# inspect one saved player and drill into ratings/matches
duprly explore player 6886613721
duprly explore player 6886613721 ratings --type both
duprly explore player 6886613721 matches
# inspect one saved match with full raw payload
duprly explore match 4295493637 --export json --output explore_match_4295493637.json
# browse raw metadata snapshots from local tables
duprly explore raw --kind player --player-dupr-id 6886613721
# launch local web explorer (Datasette)
duprly explore web --open
# import DUPR auth token from browser cookies (top-level shortcut)
duprly import-browser-token --browser comet --domain dupr.com
# import DUPR auth token from browser cookies (Safari default)
duprly auth import-browser-token --browser safari
# search clubs
duprly browse clubs "NYC pickle"
# export club players (search/select)
duprly export club-players "NYC pickle"
# export rankings
duprly export rankings 8436164521
# export workbook from local DB
duprly export workbook --output dupr.xlsx
# DB stats / rebuild reporting table
duprly db stats
duprly db rebuild-match-detail
# diagnostics
duprly doctor check
duprly doctor check --apiduprly explore web --open now launches a guided Datasette dashboard with canned analytics queries for pickleball ratings.
Each dashboard card now supports direct data retrieval:
Get JSON: opens the query as JSON (_shape=objects).Get CSV: opens the query as CSV.Copy API URL: copies a ready-to-call JSON endpoint URL.
Dashboard homepage also includes Add player to local DB with live DUPR autocomplete (name or ID):
- searches DUPR as you type (CLI-style completion labels:
Full Name [dupr_id]) - marks suggestions already saved locally
- imports the selected player into local SQLite
- optional one-click rating-history and full match-history sync during import
For player_rating_over_time, the query page itself also includes:
Request latest player data(fetches/persists player profile + rating history from DUPR)- optional
include full match history - auto-refresh of the page after fetch completes
# this player's rating over time (default doubles)
http://127.0.0.1:8001/dupr/player_rating_over_time?dupr_id=6886613721&rating_type=DOUBLES
# recent form (last 90 days by default)
http://127.0.0.1:8001/dupr/player_recent_form?dupr_id=6886613721&days=90
# partner breakdown
http://127.0.0.1:8001/dupr/player_partner_breakdown?dupr_id=6886613721&days=90
# opponent breakdown
http://127.0.0.1:8001/dupr/player_opponent_breakdown?dupr_id=6886613721&days=90
# club top risers
http://127.0.0.1:8001/dupr/club_top_risers?club_id=7735643894&days=90&rating_type=DOUBLES
# direct JSON data retrieval (object rows)
http://127.0.0.1:8001/dupr/player_recent_form.json?dupr_id=6886613721&days=90&_shape=objects
# direct CSV data retrieval
http://127.0.0.1:8001/dupr/player_recent_form.csv?dupr_id=6886613721&days=90dupr_id: numeric DUPR player IDclub_id: numeric DUPR club ID (optional in club queries)rating_type:DOUBLES(default) orSINGLESdays: rolling window for "recent" analysis (default90)start_date/end_date: optionalYYYY-MM-DDdate bounds for trend queries
Old command names are still available with deprecation warnings:
get-data->sync allget-all-players->sync playersget-all-player-matches->sync matches --all-playersget-player->browse playerget-matches->browse matchesupdate-ratings->sync ratingsbuild-match-detail->db rebuild-match-detailstats->db statswrite-excel->export workbook
The old script entrypoint still works:
python /Users/leosternlicht/repos/duprly/duprly.py --helpStandalone helper scripts are now thin wrappers:
get_players.pyget_club_rankings.pyget_matches.py
- DUPR endpoints used here are unofficial and may change.
- Match history uses the browser-proven request shape (
POST /player/v1.0/{id}/history). duprly exploreis read-only and works entirely from local SQLite data.- Explorer export supports CSV/JSON to file and best-effort clipboard copy.
duprly explore webdepends on Datasette0.59, which currently needssetuptools<81(pkg_resources).duprly explore webnow generates guided metadata and templates at launch for canned analytics queries.datasette-vegaenables richer chart rendering; if missing, query pages still work in table mode.- For easier web browsing,
ratingnow includes cachedplayer_dupr_idandplayer_full_namecolumns. - Data is written to
dupr.sqlite. duprly doctor checkis useful before first sync.