How to Use the Chess.com API in Python

Sort:
Avatar of AlAlper

🧠 How to Use the Chess.com API in Python — With Headers & Real Examples♟️🐍

So what do you want to do with the Chess.com API?

You can pull user profiles, ratings, games, tournament participants, and more. The data is all public, and it's a great way to build analysis tools, or club automation systems.


⚠️ Why You Need Custom Headers

Before we jump into the code, here’s a critical tip:
Always include headers in your API requests.

Chess.com uses rate limiting and spam prevention. If you skip the headers, you’ll be Forbidden.

  • 403 Forbidden (Access denied)

If do do to much to fast you will get.

  • 429 Too Many Requests (You’ve hit the rate limit)

Adding a proper User-Agent with your app name and contact info tells their system:

“Hey, I’m not a bot. I’m a human using this responsibly. Here’s how to reach me.”


🧰 Setup: Define Your Custom Headers in Python

We'll use an Enum to organize different headers for different purposes. This is optional but clean and scalable:

from enum import Enum
import requests

# 🧠 Define headers for each type of API call
class Api_Headers(Enum):
    ACTIVE_PLAYERS = {
        "User-Agent": "GT-Active-Players-App/1.0 (Python 3.9.13) -- Finds daily players. (contact- BEST @YourUsername on Chess.com or LEAST RELIABLE dev@example.com)",
        "Accept": "application/json",
        "Accept-Encoding": "gzip, deflate",
        "Connection": "keep-alive"
    }
    TOURNAMENT_PARTICIPANTS = {
        "User-Agent": "GT-Tournament-Participation-App/1.0 (Python 3.9.13) -- Gets tournament player list. (contact- BEST @YourUsername on Chess.com or LEAST RELIABLE dev@example.com)",
        "Accept": "application/json",
        "Accept-Encoding": "gzip, deflate",
        "Connection": "keep-alive"
    }
    USER_INFO = {
        "User-Agent": "GT-User-Info-App/1.0 (Python 3.9.13) -- Gets user info. (contact- BEST @YourUsername on Chess.com or LEAST RELIABLE dev@example.com)",
        "Accept": "application/json",
        "Accept-Encoding": "gzip, deflate",
        "Connection": "keep-alive"
    }

🧪 Example: Get the List of Players in a Tournament

Let’s say you want to get a list of usernames who joined a specific tournament.

📘 Endpoint:
https://api.chess.com/pub/tournament/{tournament-name}/players

Here’s a full Python function to do just that:

def get_tournament_usernames(tournament_name):
    """
    Queries the Chess.com public API to get the list of player usernames in a tournament.

    Parameters:
    tournament_name (str): The slug or URL-friendly name of the tournament
                           (e.g., 'gt-april-showers-knockout-1-1600').

    Returns:
    list: A list of usernames who are participants in the tournament.
    """

    # Build the full API URL using the tournament name
    url = f"https://api.chess.com/pub/tournament/{tournament_name}/players"

    # Make the GET request with headers to avoid rate-limiting or blocking
    response = requests.get(
        url,
        headers=Api_Headers.TOURNAMENT_PARTICIPANTS.value
    )

    # If the request fails (e.g., bad tournament or missing headers), print status
    if response.status_code != 200:
        print(f"⚠️ Failed to get players: HTTP {response.status_code}")
        return []

    # Convert the JSON API response into a Python dictionary
    data = response.json()

    # Extract the list of usernames from the response
    usernames = [
        player["username"]
        for player in data.get("players", [])
        if "username" in player
    ]

    return usernames

🧪 Try It Out:

players = get_tournament_usernames("gt-april-showers-knockout-1-1600")
print(players)

Output:

['ChessFan123', 'QueenCrusher', 'KnightRider89', ...]

✅ Summary

  • Headers matter: use User-Agent to avoid getting blocked.

  • The API is free and public — but you still need to be polite.

  • The data you can grab is super useful for clubs, tournaments, analysis, and automation.


💬 Got questions? Want to explore more endpoints like user stats, game history, or leaderboards? Drop a comment in this thread.

Avatar of ninjaswat

Can you explain what the Accept, Accept-Encoding, and Connection headers mean? And are they always necessary? I thought just a user-agent was okay

Avatar of AlAlper

The user-agent is for Chess.com authentication

The others are for code efficiency.(they are not required.)

🔹 Accept

  • Purpose: Tells the server which content types (MIME types) the client can handle.

  • Example:

    "Accept": "application/json",
    TELLS the browser json is expected by the client.
  • Meaning: “I prefer json”


🔹 Accept-Encoding

  • Purpose: Tells the server which compression methods the client supports. Speeds downloads.

  • Example:

     
    Accept-Encoding: gzip, deflate
  • Meaning: “You can compress the response with gzip, or deflate.”


🔹 Connection

  • Purpose: Controls whether the network connection stays open or closes after the request.

  • Example:

     
    Connection: keep-alive

    → “Keep the TCP connection open for further requests.”

     
     
Avatar of ninjaswat

Oh that’s cool. Where can I read up on the other options / formats and what these specific compression methods mean?

Avatar of AlAlper

Requests is an elegant and simple HTTP library for Python, built for human beings. Link to the documentation

Great question. I have found some cool info on compression that may change my code...

Avatar of AlAlper

# pip install brotli
import requests
import os
def get_email():
email = os.getenv("EMAIL")
if not email:
email = input("Enter your email: ").strip()
os.environ["EMAIL"] = email
# Choose rc file based on shell (zsh vs bash)
shell = os.getenv("SHELL", "")
if shell.endswith("zsh"):
rc_file = os.path.expanduser("~/.zshrc")
else:
rc_file = os.path.expanduser("~/.bashrc")
# Avoid duplicate exports
if os.path.exists(rc_file):
with open(rc_file, "r") as f:
contents = f.read()
else:
contents = ""
export_line = f'export EMAIL="{email}"'
if export_line not in contents:
with open(rc_file, "a") as f:
f.write("\n" + export_line + "\n")
return email
if __name__ == "__main__":
url = "https://api.chess.com/pub/club/grand-tourneys/members"
headers = {
"User-Agent": f"GT-Members-App/1.0 (Python) -- Finds members (contact: {get_email()})",
"Accept-Encoding": "br"
}
r = requests.get(url, headers=headers)
print("Content-Encoding:", r.headers.get("Content-Encoding"))
print("Status:", r.status_code)
print("Preview:", r.text[:300])

Avatar of AlAlper

So Brotli --Googles new compression is better(10%-15%) but you need to install this into python.

pip install brotli

The script above checks if br is can be returned by the api.

Enter your email: my@email.com
Content-Encoding: br [it can]

Status: 200
Preview: {"weekly":[{"username":"alalper","joined":1739026510},{"username":"alexk82","joined":1742453264}, ... }

NOTE: I also fixed this so it will store the email as an environment variable on Windows, Mac or Linux on first use. It should help with the issue I assigned you.

Avatar of ninjaswat

What is an rc file?

Avatar of AlAlper
 

An rc file is a plain-text “run commands” file that a shell or program reads at startup to load your preferences and commands automatically. Used in Mac OS or Linux to store variables like emails. The “rc” comes from runcom (“run commands”) in early Unix/CTSS history.

What they’re for

  • Set environment variables (e.g., export EMAIL="you@example.com").

  • Define aliases and functions (e.g., alias gs='git status').

  • Customize PATH, prompts, and other defaults.

Common examples (Unix/macOS/Linux)

  • Bash:

    • ~/.bashrc → read for interactive non-login shells (most new terminals).

    • ~/.bash_profile or ~/.profile → read for login shells.

  • Zsh:

    • ~/.zshrc → read for interactive shells.

    • ~/.zprofile → login shell setup (often PATH).

  • Other tools: ~/.vimrc (Vim), ~/.inputrc (readline), ~/.psqlrc (psql), ~/.screenrc (screen).

How to use

  1. Edit the file (e.g., nano ~/.zshrc or code ~/.bashrc).

  2. Add lines, e.g.:

     
    export EMAIL="you@example.com" alias ll='ls -alF'
  3. Apply immediately in your current shell:

     
    source ~/.zshrc # or: source ~/.bashrc

    (Opening a new terminal also loads it.)

Windows note

Windows shells don’t use “rc” files. PowerShell uses a profile script (e.g., $PROFILE like Documents\PowerShell\Microsoft.PowerShell_profile.ps1) that serves the same purpose.

 

Notes / gotchas

  • Persistence: os.environ["EMAIL"] only affects the current Python process. Adding export EMAIL="..." to your rc file makes it persist for new terminals. You must source ~/.bashrc or source ~/.zshrc (or open a new terminal) for it to take effect immediately.

  • Shell differences: If you use fish, PowerShell, or cmd.exe, you’ll need different profile logic.

  • Security: Storing an email in rc files is fine; don’t do this with secrets like API keys unless you understand the implications.

  • Cleanup: If you later change the email, the code above appends a new export line. If you want to replace any old line instead of appending, I can show a small routine to rewrite the file to keep only one export EMAIL=... line.