Migrating from 0.1.x

Version 0.2.0 is a breaking rewrite of the SDK surface to match the multi-sport Partner API v1. This guide walks through the changes and shows how to port existing code.

See the CHANGELOG for the full release notes.

At a glance

0.1.x

0.2.0

client.get_member(id)

client.members.get(id)

client.search(...) returning SearchResults

async for m in client.members.search(...)

client.find_player(name)

client.members.find(name)

client.submit_match(match)

client.matches.submit(batch)

client.submit_matches([m1, m2])

client.matches.submit(batch) (put both in batch.matches)

client.get_rating_updates()

client.members.rating_updates()

client.start_oauth(...)

client.oauth.authorize(...)

client.exchange_token(...)

client.oauth.exchange_token(...)

client.refresh_access_token(...)

client.oauth.refresh(...)

client.revoke_connection(...)

client.oauth.revoke(...)

client.get_leaderboard(...)

client.leaderboard.list(...)

client.get_player_rank(...)

client.leaderboard.rank(...)

client.get_leaderboard_categories()

client.leaderboard.categories()

client.get_usage()

client.usage()

member.rating (single sport)

member.rating_for("pickleball")

member.rating_splits.gender

member.sport["pickleball"]["gender-open"].rating

member.is_vairified

member.status.is_vairified

Player.from_dict(...)

Member.model_validate(...)

Sub-resources

All operations now live on sub-resources that mirror the REST path. This replaces the flat client.xxx methods:

# Before
member = await client.get_member("vair_mem_xxx")
results = await client.search(city="Austin")
result = await client.submit_match(match)

# After
member = await client.members.get("vair_mem_xxx")
async for member in client.members.search(city="Austin"):
    ...
result = await client.matches.submit(batch)

Multi-sport ratings

The biggest conceptual change. In 0.1.x, every Member had a single rating and a flat rating_splits object. In 0.2.0, rating data lives under member.sport keyed by sport code, so the same SDK can represent a player’s pickleball and padel ratings in one call:

# Before
member.rating                        # 3.915
member.rating_splits.gender          # 3.880
member.rating_splits.singles         # 3.710
member.is_vairified                  # True

# After
member.rating_for("pickleball")      # 3.915
member.sport["pickleball"]["gender-open"].rating   # 3.880
member.sport["pickleball"]["singles-open"].rating  # 3.710
member.status.is_vairified           # True

SportRating is dict-like, so you can also iterate or check membership:

pb = member.sport["pickleball"]
print(len(pb), "splits")              # 3 splits
if "singles-open" in pb:
    print(pb["singles-open"].rating)
for key, split in pb:
    print(key, split.rating, split.abbr)

Fetch ratings for just the sports you care about with the sport filter:

# All sports this player has ratings in
member = await client.members.get("vair_mem_xxx")

# Just pickleball
member = await client.members.get("vair_mem_xxx", sport="pickleball")

# Multiple sports
member = await client.members.get(
    "vair_mem_xxx",
    sport=["pickleball", "padel"],
)

Matches: n-team × n-game

Match has been restructured to natively support any match shape — singles, doubles, 3-way round robin, best-of-N, etc. — through a single schema. Submit matches as a MatchBatch that carries shared defaults for every match in the batch:

# Before
from datetime import datetime
match = Match(
    event="Weekly League",
    bracket="4.0 Doubles",
    date=datetime.now(),
    team1=("p1", "p2"),
    team2=("p3", "p4"),
    scores=[(11, 9), (11, 7)],
)
result = await client.submit_match(match)

# After
from vairified import MatchBatch, Match, Game
batch = MatchBatch(
    sport="pickleball",                 # NEW: sport code is required
    win_score=11,                        # NEW: so the rater can interpret scores
    win_by=2,
    bracket="4.0 Doubles",
    event="Weekly League",
    match_date="2026-04-11T14:00:00Z",
    matches=[
        Match(
            identifier="m1",
            teams=[["p1", "p2"], ["p3", "p4"]],
            games=[Game(scores=[11, 9]), Game(scores=[11, 7])],
        ),
    ],
)
result = await client.matches.submit(batch)

Singles becomes teams=[["p1"], ["p2"]]. A 3-way round robin becomes teams=[["p1"], ["p2"], ["p3"]]. Each Game scores one entry per team in the same order.

OAuth

The OAuth methods moved to the client.oauth sub-resource:

# Before
auth = await client.start_oauth(redirect_uri=..., scopes=[...])
tokens = await client.exchange_token(code=..., redirect_uri=...)
new_tokens = await client.refresh_access_token(refresh_token)
await client.revoke_connection(player_id)

# After
auth = await client.oauth.authorize(redirect_uri=..., scopes=[...])
tokens = await client.oauth.exchange_token(code=..., redirect_uri=...)
new_tokens = await client.oauth.refresh(refresh_token)
await client.oauth.revoke(player_id)

Scope strings are the same. OAuthScope is now a Literal type alias, so editors and type checkers will flag typos in scope lists at authoring time.

Immutable pydantic models

Response models (Member, SportRating, RatingUpdate, etc.) are now frozen pydantic v2 BaseModel instances. This means:

  • They’re immutable — attempts to assign attributes raise ValidationError.

  • They tolerate unknown fields so the SDK stays forward-compatible with API additions.

  • They accept both snake_case (Python) and camelCase (wire) field names via aliases.

  • Player.from_dict(data) is replaced by Member.model_validate(data).

Request models (Match, MatchBatch, Game) use extra="forbid" so typos in keyword arguments fail loudly at construction time instead of silently dropping data.

Status flags

The top-level is_* booleans on Member are now grouped under member.status:

# Before
member.is_vairified
member.is_wheelchair
member.is_ambassador

# After
member.status.is_vairified
member.status.is_wheelchair
member.status.is_ambassador
member.status.is_rater
member.status.is_connected

Environment presets

Use env= instead of writing the full base URL by hand:

# Before
client = Vairified(
    api_key="vair_pk_xxx",
    base_url="https://api-staging.vairified.com/api/v1",
)

# After
client = Vairified(api_key="vair_pk_xxx", env="staging")

Supported values: "production" (default), "staging", "local". Reads VAIRIFIED_ENV from the environment when not supplied.