> ## Documentation Index
> Fetch the complete documentation index at: https://developers.scoutworks.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Python

> Read every page of a list with requests, handle the rate limit, and save a CSV.

This script reads every member and saves them to `members.csv`. It needs Python 3.9 or later and the `requests` package (`pip install requests`).

```python members_to_csv.py theme={null}
import csv
import os
import time

import requests

# The sandbox key reads a demo group. Set SCOUTWORKS_API_KEY to use your own.
API_KEY = os.environ.get(
    "SCOUTWORKS_API_KEY",
    "sw_live_473adcc6c7236916021f0f24559db36576a53bab071d0f0c08a025adb510b6ab",
)
session = requests.Session()
session.headers["Authorization"] = f"Bearer {API_KEY}"


def get(url, params):
    """GET one page. Waits and tries again if the rate limit is reached."""
    while True:
        res = session.get(url, params=params, timeout=30)
        if res.status_code == 429:
            time.sleep(res.json().get("retryAfter", 30))
            continue
        if not res.ok:
            error = res.json()["error"]
            raise SystemExit(f"{res.status_code} {error['code']}: {error['message']}")
        return res.json()


def read_all(url):
    """Every item in a list, following nextCursor to the last page."""
    items, params = [], {"limit": 200}
    while True:
        page = get(url, params)
        items += page["data"]
        if page["nextCursor"] is None:
            return items
        params["cursor"] = page["nextCursor"]


def safe(value):
    """Stops a spreadsheet running a cell that starts with = + - or @."""
    text = str(value)
    return "'" + text if text.lstrip().startswith(("=", "+", "-", "@")) else text


members = read_all("https://api-demo.scoutworks.app/api/v1/members")

with open("members.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerow(["First name", "Last name", "Sections", "Patrol", "Joined"])
    for m in members:
        writer.writerow([
            safe(m["firstName"]),
            safe(m["lastName"]),
            safe(", ".join(s["name"] for s in m["sections"])),
            safe(m["patrol"]["name"] if m["patrol"] else ""),
            m["joinedAt"][:10],
        ])

print(f"Saved {len(members)} members to members.csv")
```

Run it:

```bash theme={null}
python members_to_csv.py
```

## Switch to your group

Change `api-demo.scoutworks.app` to `api.scoutworks.app`, and set your key in the environment instead of the code:

```bash theme={null}
export SCOUTWORKS_API_KEY="sw_live_your_own_key"
python members_to_csv.py
```

Your key needs the `members:read` scope.

## Notes

* A field that the key’s scopes do not allow is **left out** of the response. Use `m.get("dateOfBirth")`, not `m["dateOfBirth"]`, for the `.contacts` fields.
* The `safe()` function matters if anyone opens the CSV in Excel or Sheets. See [Google Sheets](/recipes/google-sheets) for why.
* For other lists, change the URL. The [API reference](/api-reference/introduction) lists every endpoint and field.
