spond.club

Client for the Spond Club finance API.

Spond Club is the paid administration tier sold to clubs/teams alongside the free consumer app. It exposes a separate API (api.spond.com/club/v1/) for finance-flavoured data such as transactions/payments. Use the SpondClub class for this API and spond.spond.Spond for everything else.

  1"""Client for the Spond Club finance API.
  2
  3Spond Club is the paid administration tier sold to clubs/teams alongside the
  4free consumer app. It exposes a separate API (`api.spond.com/club/v1/`) for
  5finance-flavoured data such as transactions/payments. Use the `SpondClub`
  6class for this API and `spond.spond.Spond` for everything else.
  7"""
  8
  9from __future__ import annotations
 10
 11from typing import TYPE_CHECKING, ClassVar
 12
 13from .base import _SpondBase
 14
 15if TYPE_CHECKING:
 16    from . import JSONDict
 17
 18
 19class SpondClub(_SpondBase):
 20    """Async client for the Spond Club finance API.
 21
 22    Authentication is shared with the consumer API — the same email/password
 23    credentials work, but the user must belong to at least one Spond Club
 24    organisation and the `club_id` passed to each method must be one they
 25    have access to. The `club_id` here is distinct from the consumer-API
 26    `groupId`.
 27
 28    Example
 29    -------
 30    ```python
 31    import asyncio
 32    from spond import club
 33
 34    async def main():
 35        sc = club.SpondClub(username="me@example.invalid", password="secret")
 36        txs = await sc.get_transactions(club_id="ABCD1234...", max_items=50)
 37        for t in txs:
 38            print(t["paidAt"], t["paymentName"], t["paidByName"])
 39        await sc.clientsession.close()
 40
 41    asyncio.run(main())
 42    ```
 43    """
 44
 45    _API_BASE_URL: ClassVar = "https://api.spond.com/club/v1/"
 46
 47    def __init__(self, username: str, password: str) -> None:
 48        """Construct a Spond Club client.
 49
 50        Parameters
 51        ----------
 52        username : str
 53            Spond account email. Same credentials as the consumer API; the
 54            account must have access to at least one Spond Club organisation
 55            for the API calls to return data.
 56        password : str
 57            Spond account password.
 58        """
 59        super().__init__(username, password, self._API_BASE_URL)
 60        self.transactions: list[JSONDict] | None = None
 61
 62    @_SpondBase.require_authentication
 63    async def get_transactions(
 64        self, club_id: str, skip: int | None = None, max_items: int = 100
 65    ) -> list[JSONDict]:
 66        """Retrieve transactions/payments for a Spond Club.
 67
 68        Spond's transactions endpoint returns at most 25 records per request,
 69        so this method paginates internally (via recursion on `skip`) until
 70        either `max_items` is reached or the server returns an empty page.
 71
 72        **Caching caveat**: results accumulate on `self.transactions` and
 73        the cache is **not** keyed by `club_id` — calling this method again
 74        with a different `club_id` on the same instance will append that
 75        club's transactions to the same list, mixing the two. If you query
 76        multiple clubs from one client, reset the cache between calls
 77        (`sc.transactions = None`) or use a fresh `SpondClub` instance per
 78        club.
 79
 80        Each transaction dict typically includes at least `id`, `paidAt`,
 81        `paymentName`, and `paidByName`. See `examples/transactions.py` for
 82        a usage example.
 83
 84        Parameters
 85        ----------
 86        club_id : str
 87            Identifier for the club. Note that this is **different** from the
 88            `groupId` used elsewhere in the Spond API — find it in the URL
 89            of the Spond Club web UI.
 90        skip : int, optional
 91            Pagination cursor (number of records to skip). Normally left as
 92            `None`; the method increments it itself on recursive calls. Only
 93            override if you know what you're doing.
 94        max_items : int, optional
 95            Stop fetching once at least this many transactions are
 96            accumulated. Defaults to 100. The final list may be slightly
 97            longer than `max_items` since the server returns pages of 25.
 98
 99        Returns
100        -------
101        list[JSONDict]
102            All transactions accumulated so far (across recursive page
103            fetches). Empty list if the club has no transactions.
104        """
105        if self.transactions is None:
106            self.transactions = []
107
108        url = f"{self.api_url}transactions"
109        params = None if skip is None else {"skip": skip}
110        headers = {**self.auth_headers, "X-Spond-Clubid": club_id}
111
112        async with self.clientsession.get(url, headers=headers, params=params) as r:
113            if r.status == 200:
114                t = await r.json()
115                if len(t) == 0:
116                    return self.transactions
117
118                self.transactions.extend(t)
119                if len(self.transactions) < max_items:
120                    return await self.get_transactions(
121                        club_id=club_id,
122                        skip=len(t) if skip is None else skip + len(t),
123                        max_items=max_items,
124                    )
125
126        return self.transactions
class SpondClub(spond.base._SpondBase):
 20class SpondClub(_SpondBase):
 21    """Async client for the Spond Club finance API.
 22
 23    Authentication is shared with the consumer API — the same email/password
 24    credentials work, but the user must belong to at least one Spond Club
 25    organisation and the `club_id` passed to each method must be one they
 26    have access to. The `club_id` here is distinct from the consumer-API
 27    `groupId`.
 28
 29    Example
 30    -------
 31    ```python
 32    import asyncio
 33    from spond import club
 34
 35    async def main():
 36        sc = club.SpondClub(username="me@example.invalid", password="secret")
 37        txs = await sc.get_transactions(club_id="ABCD1234...", max_items=50)
 38        for t in txs:
 39            print(t["paidAt"], t["paymentName"], t["paidByName"])
 40        await sc.clientsession.close()
 41
 42    asyncio.run(main())
 43    ```
 44    """
 45
 46    _API_BASE_URL: ClassVar = "https://api.spond.com/club/v1/"
 47
 48    def __init__(self, username: str, password: str) -> None:
 49        """Construct a Spond Club client.
 50
 51        Parameters
 52        ----------
 53        username : str
 54            Spond account email. Same credentials as the consumer API; the
 55            account must have access to at least one Spond Club organisation
 56            for the API calls to return data.
 57        password : str
 58            Spond account password.
 59        """
 60        super().__init__(username, password, self._API_BASE_URL)
 61        self.transactions: list[JSONDict] | None = None
 62
 63    @_SpondBase.require_authentication
 64    async def get_transactions(
 65        self, club_id: str, skip: int | None = None, max_items: int = 100
 66    ) -> list[JSONDict]:
 67        """Retrieve transactions/payments for a Spond Club.
 68
 69        Spond's transactions endpoint returns at most 25 records per request,
 70        so this method paginates internally (via recursion on `skip`) until
 71        either `max_items` is reached or the server returns an empty page.
 72
 73        **Caching caveat**: results accumulate on `self.transactions` and
 74        the cache is **not** keyed by `club_id` — calling this method again
 75        with a different `club_id` on the same instance will append that
 76        club's transactions to the same list, mixing the two. If you query
 77        multiple clubs from one client, reset the cache between calls
 78        (`sc.transactions = None`) or use a fresh `SpondClub` instance per
 79        club.
 80
 81        Each transaction dict typically includes at least `id`, `paidAt`,
 82        `paymentName`, and `paidByName`. See `examples/transactions.py` for
 83        a usage example.
 84
 85        Parameters
 86        ----------
 87        club_id : str
 88            Identifier for the club. Note that this is **different** from the
 89            `groupId` used elsewhere in the Spond API — find it in the URL
 90            of the Spond Club web UI.
 91        skip : int, optional
 92            Pagination cursor (number of records to skip). Normally left as
 93            `None`; the method increments it itself on recursive calls. Only
 94            override if you know what you're doing.
 95        max_items : int, optional
 96            Stop fetching once at least this many transactions are
 97            accumulated. Defaults to 100. The final list may be slightly
 98            longer than `max_items` since the server returns pages of 25.
 99
100        Returns
101        -------
102        list[JSONDict]
103            All transactions accumulated so far (across recursive page
104            fetches). Empty list if the club has no transactions.
105        """
106        if self.transactions is None:
107            self.transactions = []
108
109        url = f"{self.api_url}transactions"
110        params = None if skip is None else {"skip": skip}
111        headers = {**self.auth_headers, "X-Spond-Clubid": club_id}
112
113        async with self.clientsession.get(url, headers=headers, params=params) as r:
114            if r.status == 200:
115                t = await r.json()
116                if len(t) == 0:
117                    return self.transactions
118
119                self.transactions.extend(t)
120                if len(self.transactions) < max_items:
121                    return await self.get_transactions(
122                        club_id=club_id,
123                        skip=len(t) if skip is None else skip + len(t),
124                        max_items=max_items,
125                    )
126
127        return self.transactions

Async client for the Spond Club finance API.

Authentication is shared with the consumer API — the same email/password credentials work, but the user must belong to at least one Spond Club organisation and the club_id passed to each method must be one they have access to. The club_id here is distinct from the consumer-API groupId.

Example
import asyncio
from spond import club

async def main():
    sc = club.SpondClub(username="me@example.invalid", password="secret")
    txs = await sc.get_transactions(club_id="ABCD1234...", max_items=50)
    for t in txs:
        print(t["paidAt"], t["paymentName"], t["paidByName"])
    await sc.clientsession.close()

asyncio.run(main())
SpondClub(username: str, password: str)
48    def __init__(self, username: str, password: str) -> None:
49        """Construct a Spond Club client.
50
51        Parameters
52        ----------
53        username : str
54            Spond account email. Same credentials as the consumer API; the
55            account must have access to at least one Spond Club organisation
56            for the API calls to return data.
57        password : str
58            Spond account password.
59        """
60        super().__init__(username, password, self._API_BASE_URL)
61        self.transactions: list[JSONDict] | None = None

Construct a Spond Club client.

Parameters
  • username (str): Spond account email. Same credentials as the consumer API; the account must have access to at least one Spond Club organisation for the API calls to return data.
  • password (str): Spond account password.
transactions: list[dict[str, Any]] | None
async def get_transactions( self, club_id: str, skip: int | None = None, max_items: int = 100) -> list[dict[str, typing.Any]]:
 63    @_SpondBase.require_authentication
 64    async def get_transactions(
 65        self, club_id: str, skip: int | None = None, max_items: int = 100
 66    ) -> list[JSONDict]:
 67        """Retrieve transactions/payments for a Spond Club.
 68
 69        Spond's transactions endpoint returns at most 25 records per request,
 70        so this method paginates internally (via recursion on `skip`) until
 71        either `max_items` is reached or the server returns an empty page.
 72
 73        **Caching caveat**: results accumulate on `self.transactions` and
 74        the cache is **not** keyed by `club_id` — calling this method again
 75        with a different `club_id` on the same instance will append that
 76        club's transactions to the same list, mixing the two. If you query
 77        multiple clubs from one client, reset the cache between calls
 78        (`sc.transactions = None`) or use a fresh `SpondClub` instance per
 79        club.
 80
 81        Each transaction dict typically includes at least `id`, `paidAt`,
 82        `paymentName`, and `paidByName`. See `examples/transactions.py` for
 83        a usage example.
 84
 85        Parameters
 86        ----------
 87        club_id : str
 88            Identifier for the club. Note that this is **different** from the
 89            `groupId` used elsewhere in the Spond API — find it in the URL
 90            of the Spond Club web UI.
 91        skip : int, optional
 92            Pagination cursor (number of records to skip). Normally left as
 93            `None`; the method increments it itself on recursive calls. Only
 94            override if you know what you're doing.
 95        max_items : int, optional
 96            Stop fetching once at least this many transactions are
 97            accumulated. Defaults to 100. The final list may be slightly
 98            longer than `max_items` since the server returns pages of 25.
 99
100        Returns
101        -------
102        list[JSONDict]
103            All transactions accumulated so far (across recursive page
104            fetches). Empty list if the club has no transactions.
105        """
106        if self.transactions is None:
107            self.transactions = []
108
109        url = f"{self.api_url}transactions"
110        params = None if skip is None else {"skip": skip}
111        headers = {**self.auth_headers, "X-Spond-Clubid": club_id}
112
113        async with self.clientsession.get(url, headers=headers, params=params) as r:
114            if r.status == 200:
115                t = await r.json()
116                if len(t) == 0:
117                    return self.transactions
118
119                self.transactions.extend(t)
120                if len(self.transactions) < max_items:
121                    return await self.get_transactions(
122                        club_id=club_id,
123                        skip=len(t) if skip is None else skip + len(t),
124                        max_items=max_items,
125                    )
126
127        return self.transactions

Retrieve transactions/payments for a Spond Club.

Spond's transactions endpoint returns at most 25 records per request, so this method paginates internally (via recursion on skip) until either max_items is reached or the server returns an empty page.

Caching caveat: results accumulate on self.transactions and the cache is not keyed by club_id — calling this method again with a different club_id on the same instance will append that club's transactions to the same list, mixing the two. If you query multiple clubs from one client, reset the cache between calls (sc.transactions = None) or use a fresh SpondClub instance per club.

Each transaction dict typically includes at least id, paidAt, paymentName, and paidByName. See examples/transactions.py for a usage example.

Parameters
  • club_id (str): Identifier for the club. Note that this is different from the groupId used elsewhere in the Spond API — find it in the URL of the Spond Club web UI.
  • skip (int, optional): Pagination cursor (number of records to skip). Normally left as None; the method increments it itself on recursive calls. Only override if you know what you're doing.
  • max_items (int, optional): Stop fetching once at least this many transactions are accumulated. Defaults to 100. The final list may be slightly longer than max_items since the server returns pages of 25.
Returns
  • list[JSONDict]: All transactions accumulated so far (across recursive page fetches). Empty list if the club has no transactions.