API Reference¶
This section contains the full API reference for all public classes and functions.
Client¶
Vairified SDK Client — async-first, sub-resource organized.
Usage:
async with Vairified(api_key="vair_pk_xxx") as client:
# Get a connected member
member = await client.members.get("vair_mem_xxx")
print(member.display_name, member.rating_for("pickleball"))
# Auto-paginate a search
async for member in client.members.search(city="Austin", rating_min=4.0):
print(member.display_name)
# Submit a bulk match batch
result = await client.matches.submit(
MatchBatch(
sport="pickleball",
win_score=11,
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, 8]), Game(scores=[11, 5])],
),
],
)
)
print(f"Submitted {result.num_games} games")
Sub-resources:
Vairified.members— get/search/get_bulk/rating_updatesVairified.matches— submit batch, tournament_importVairified.oauth— OAuth authorization flowVairified.leaderboard— leaderboard queriesVairified.webhooks— webhook delivery inspectionVairified.usage— API usage stats (method:await client.usage())
- class vairified.client.LeaderboardResource[source]¶
Bases:
_ResourceRead-only leaderboard queries.
- Parameters:
client (
Vairified)
- async list(*, category=None, age_bracket=None, scope=None, state=None, city=None, club_id=None, gender=None, verified_only=False, min_games=None, limit=50, offset=0, search=None)[source]¶
Fetch a leaderboard page with optional filters.
- class vairified.client.MatchesResource[source]¶
Bases:
_ResourceMatch submission — one call submits a full batch.
- Parameters:
client (
Vairified)
- async submit(batch)[source]¶
Submit a
MatchBatchfor rating calculation.All players in every match must have granted the
user:match:submitscope via OAuth (unless your API key has theuser:match:submit:trustedscope, which skips per-player consent).Set
batch.dry_run = Trueto validate without persisting.Example:
batch = MatchBatch( sport="pickleball", win_score=11, win_by=2, bracket="4.0 Doubles", event="Weekly League", match_date="2026-04-11T14:00:00Z", matches=[ Match( identifier="m1", teams=[["vair_mem_aaa", "vair_mem_bbb"], ["vair_mem_ccc", "vair_mem_ddd"]], games=[Game(scores=[11, 8]), Game(scores=[11, 5])], ), ], ) result = await client.matches.submit(batch) if result.ok: print(f"Submitted {result.num_games} games")
- Parameters:
batch (
MatchBatch)- Return type:
- async tournament_import(body)[source]¶
Import tournament results with automatic player matching.
Players are matched by email first, then name+location. Unmatched players become ghost accounts that can be claimed later.
- Parameters:
body (
dict[str,Any]) – Tournament data dict with keys:tournamentName,sport,winScore,winBy,matches(list of match dicts withidentifier,event,bracket,format,matchDate,teamA,teamB).- Return type:
- Returns:
TournamentImportResultwith counts.- Raises:
ValidationError – If the payload is malformed.
Example:
result = await client.matches.tournament_import({ "tournamentName": "Austin Open 2026", "sport": "pickleball", "winScore": 11, "winBy": 2, "matches": [...] }) print(f"Imported {result.matches_imported} matches")
- class vairified.client.MembersResource[source]¶
Bases:
_ResourceMember operations — get a single member, auto-paginating search, and polling for rating change notifications.
- Parameters:
client (
Vairified)
- async get(player_id, *, sport=None)[source]¶
Get a connected member by external ID.
Requires an active OAuth connection between your partner app and the player. Use the OAuth flow on
client.oauthfirst.- Parameters:
- Raises:
NotFoundError – If the external ID is invalid or unknown.
VairifiedError – If the player has not connected to your app (403) or if the API request otherwise fails.
- Return type:
Example:
member = await client.members.get("vair_mem_xxx") print(member.display_name, member.rating_for("pickleball")) # 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"], )
- async search(*, sport=None, name=None, member_id=None, city=None, state=None, country=None, zip=None, location=None, gender=None, vairified_only=None, wheelchair=None, rating_min=None, rating_max=None, age=None, age_min=None, age_max=None, sort_by=None, sort_order='desc', page_size=20, max_results=None)[source]¶
Search for players, yielding each match as an
Member.This is an auto-paginating async iterator — it fetches pages from the server lazily as you iterate, so you can stream through thousands of results without holding them all in memory:
async for member in client.members.search(city="Austin"): print(member.display_name, member.rating_for("pickleball"))
Stop early by
break-ing out of the loop, or cap the total number of results withmax_results.- Parameters:
sport (
str|list[str] |None(default:None)) – Sport code (or list of codes) to filter ratings by. Omit to get every sport each player has ratings in.name (
str|None(default:None)) – Name partial-match (first or last name).member_id (
int|str|None(default:None)) – Exact numeric member ID.city (
str|None(default:None)) – City filter (partial match, case-insensitive).state (
str|None(default:None)) – State code (e.g."TX").country (
str|None(default:None)) – ISO 3166 alpha-2 country code.zip (
str|None(default:None)) – ZIP/postal code (exact match).location (
str|None(default:None)) – General location search.gender (
str|None(default:None)) –"MALE","FEMALE", orNonefor any.vairified_only (
bool|None(default:None)) – WhenTrue, only verified players.wheelchair (
bool|None(default:None)) – WhenTrue, only wheelchair players.rating_min (
float|None(default:None)) – Lower rating bound (2.0-8.0).rating_max (
float|None(default:None)) – Upper rating bound (2.0-8.0).sort_order (
str(default:'desc')) –"asc"or"desc".page_size (
int(default:20)) – Results per HTTP request. Server cap is 100.max_results (
int|None(default:None)) – Optional cap on total results to iterate.
- Return type:
- async rating_updates()[source]¶
Poll for rating change notifications for subscribed members.
Returns a list of
RatingUpdateobjects for every player whose rating has changed since the last poll. Members are considered “subscribed” when they have an active OAuth connection with theuser:webhook:subscribescope.- Return type:
- async find(name)[source]¶
Return the first search hit for a name, or
None.Convenience method for the common “look up by name” case:
mike = await client.members.find("Mike Barker") if mike: print(mike.rating_for("pickleball"))
- async get_bulk(ids, *, sport=None)[source]¶
Fetch up to 100 members by their member IDs in one call.
- Parameters:
- Return type:
- Returns:
List of
Memberobjects. Unknown IDs are silently omitted – the list may be shorter than ids.- Raises:
ValueError – If more than 100 IDs are provided.
Example:
members = await client.members.get_bulk([4873327, 4873328]) for m in members: print(m.name, m.rating_for("pickleball"))
- class vairified.client.OAuthResource[source]¶
Bases:
_ResourceOAuth 2.0 flow for obtaining player consent.
Typical flow:
Call
authorize()to start an authorization — you get a URL to redirect the player to.The player approves on the Vairified site and gets redirected to your
redirect_uriwith acodequery parameter.Call
exchange_token()to swap the code for access and refresh tokens plus the player’s UUID.Store the refresh token and call
refresh()when the access token expires.Call
revoke()to disconnect a player from your app.
- Parameters:
client (
Vairified)
- async authorize(redirect_uri, *, scopes=None, state=None)[source]¶
Start an OAuth authorization flow.
- Parameters:
redirect_uri (
str) – Your application’s callback URL.scopes (
list[Literal['user:profile:read','user:profile:email','user:rating:read','user:rating:history','user:match:submit','user:webhook:subscribe']] |None(default:None)) – Scopes to request. Defaults to["user:profile:read", "user:rating:read"].user:profile:readis always added if missing.state (
str|None(default:None)) – CSRF protection token — persist and verify on callback.
- Raises:
OAuthError – If a requested scope is invalid.
- Return type:
- async exchange_token(code, redirect_uri)[source]¶
Exchange an authorization code for access and refresh tokens.
- Parameters:
- Return type:
- async refresh(refresh_token)[source]¶
Refresh an expired access token using a refresh token.
- Parameters:
refresh_token (
str)- Return type:
- class vairified.client.Vairified[source]¶
Bases:
objectAsync client for the Vairified Partner API.
The client is organized around sub-resources that mirror the REST structure —
client.members,client.matches,client.oauth,client.leaderboard. Each sub-resource is a thin wrapper around the HTTP layer on this object.- Parameters:
api_key (
str|None(default:None)) – Partner API key (vair_pk_...). Falls back to theVAIRIFIED_API_KEYenvironment variable if not supplied.env (
str|None(default:None)) – Environment preset —"production"(default),"staging", or"local". Overridden bybase_url.base_url (
str|None(default:None)) – Explicit base URL. Takes precedence overenv.timeout (
float(default:30.0)) – Request timeout in seconds.
- Raises:
ValueError – If no API key is provided.
- class vairified.client.WebhooksResource[source]¶
Bases:
_ResourceWebhook delivery inspection.
- Parameters:
client (
Vairified)
- async deliveries(*, event=None, status=None, limit=20, offset=0)[source]¶
List recent webhook delivery attempts.
- Parameters:
- Return type:
- Returns:
WebhookDeliveriesResultwith entries and total.
Example:
result = await client.webhooks.deliveries(status="failed") for d in result.deliveries: print(d.event, d.status_code, d.error_message)
Models¶
Vairified SDK Models — Partner API v1 shapes.
All response models are pydantic.BaseModel with
model_config = ConfigDict(frozen=True, populate_by_name=True, extra="allow")
so they’re immutable, support both snake_case (Python) and camelCase (wire)
field names, and tolerate new server-side fields without breaking.
The public surface is designed to feel native:
member.nameis aproperty(), not a method — noget_name().member.sport["pickleball"]is dict-like access;SportRatingimplements__getitem__,__iter__,__contains__,__len__.Every model has a human-readable
__repr__so the REPL is useful.Models work with
matchstatements via pydantic field access.
- Breaking from v0.1.x:
The flat single-sport response (
member.rating/member.rating_splits) has been replaced by a multi-sportmember.sportdict keyed by sport code. TheMatchclass takesteams: list[list[str]]andgames: list[Game]instead ofteam1/team2plus per-game tuples.
- class vairified.models.Gender[source]¶
Bases:
StrEnumNormalized gender enum returned by the Partner API.
Matches the UPPERCASE tokens emitted by
PartnerMember.genderon the backend.- MALE = 'MALE'¶
- FEMALE = 'FEMALE'¶
- OTHER = 'OTHER'¶
- UNKNOWN = 'UNKNOWN'¶
- __new__(value)¶
- class vairified.models.Game[source]¶
Bases:
BaseModelOne scored game within a
Match.scoresis one integer per team, in the same order as the parent match’steamslist. For a standard 2-team gamescoresis[team1_score, team2_score]. The API supports n-team matches by setting a longer list.All fields except
scoresare optional overrides of the parent match’s defaults — use them only when a specific game inside the match differs from the rest (e.g. a championship game played to 15 when the rest of the match was to 11).- Parameters:
data (
Any)
- model_config: ClassVar[ConfigDict] = {'extra': 'forbid', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class vairified.models.Match[source]¶
Bases:
BaseModelOne match to submit in a
MatchBatch.A match has:
teams— a list of teams, each a list of player IDs (externalvair_mem_xxx, numeric member IDs, or UUIDs). Supports n-team × n-player matches natively:[[p1, p2], [p3, p4]]for standard doubles,[[p1], [p2]]for singles,[[p1], [p2], [p3]]for a 3-way round robin.games— one or more scored games (e.g. best-of-3 has 2 or 3 entries). Scores in each game are parallel to theteamsorder.
Every other field is an optional override of the parent
MatchBatchdefault.- Parameters:
data (
Any)
- model_config: ClassVar[ConfigDict] = {'extra': 'forbid', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class vairified.models.MatchBatch[source]¶
Bases:
BaseModelCompressed bulk match submission.
Top-level fields are defaults applied to every match in the
matcheslist. Any match can override any field.sport,win_score, andwin_byare required at the batch level — partners must tell the rater which sport the matches are in and what the winning conditions were so scores can be interpreted correctly.Example:
batch = MatchBatch( sport="pickleball", win_score=11, win_by=2, bracket="4.0 Doubles", event="Weekly League", match_date="2026-04-11T14:00:00Z", matches=[ Match( identifier="m1", teams=[["vair_mem_aaa", "vair_mem_bbb"], ["vair_mem_ccc", "vair_mem_ddd"]], games=[Game(scores=[11, 8]), Game(scores=[11, 5])], ), ], ) result = await client.matches.submit(batch)
- Parameters:
data (
Any)
- model_config: ClassVar[ConfigDict] = {'extra': 'forbid', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class vairified.models.MatchBatchResult[source]¶
Bases:
BaseModelResult of a
Vairified.matches.submit()call.successisTrueonly when every match in the batch was accepted. Checkerrorsfor per-match validation failures.- Parameters:
data (
Any)
- model_config: ClassVar[ConfigDict] = {'extra': 'allow', 'frozen': True, 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class vairified.models.Member[source]¶
Bases:
BaseModelA partner-facing player record.
Returned by
Vairified.members.get()(full detail, requires an active OAuth connection) andVairified.members.search()(limited detail for public search).Rating data lives under
sport— a dict keyed by sport code. The backend returns only the sports the player has ratings in, or only the sports requested via the?sport=query filter. Userating_for()to fetch the primary rating for a specific sport with a sensible default.- Parameters:
data (
Any)
- model_config: ClassVar[ConfigDict] = {'extra': 'allow', 'frozen': True, 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- status: MemberStatus¶
- sport: dict[str, SportRating]¶
- rating_for(sport='pickleball')[source]¶
Primary rating for a given sport.
- Parameters:
sport (
str(default:'pickleball')) – Sport code, defaults to"pickleball".- Return type:
- Returns:
The primary rating value, or
Noneif the player has no ratings for that sport.
Example:
member.rating_for() # pickleball member.rating_for("padel") # padel
- class vairified.models.MemberStatus[source]¶
Bases:
BaseModelGlobal status flags for a player.
Grouped into a sub-object rather than top-level booleans so that inspection (
pprint,repr, JSON) keeps allis_*flags visually clustered.Only the genuinely global flags live here. VAIRification and VAIR-Pro status are per-sport (Vairified#783) and live on each
SportRating(member.sport["pickleball"].is_vairified).- Parameters:
data (
Any)
- model_config: ClassVar[ConfigDict] = {'extra': 'allow', 'frozen': True, 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class vairified.models.RatingSplit[source]¶
Bases:
BaseModelOne slice of a player’s rating for a specific category × age bracket.
Keys in
SportRating.rating_splitsare strings like"overall-open","singles-12-13", or"overall-40+".- Parameters:
data (
Any)
- model_config: ClassVar[ConfigDict] = {'extra': 'allow', 'frozen': True, 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class vairified.models.RatingUpdate[source]¶
Bases:
BaseModelA single rating change notification.
Returned by
Vairified.members.rating_updates()(polling) and delivered via webhook callbacks to partners that have registered a webhook URL.- Parameters:
data (
Any)
- model_config: ClassVar[ConfigDict] = {'extra': 'allow', 'frozen': True, 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- rating_splits: dict[str, RatingSplit] | None¶
- class vairified.models.SearchFilters[source]¶
Bases:
BaseModelFilters accepted by
Vairified.members.search().Most users won’t construct this directly — the
search()method accepts keyword arguments and builds it internally. But it’s exposed so you can inspect the full set of available filters in one place.- Parameters:
data (
Any)
- model_config: ClassVar[ConfigDict] = {'extra': 'forbid', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class vairified.models.SportRating[source]¶
Bases:
BaseModelA player’s ratings for a single sport.
The top-level
rating/abbris the primary rating for that sport (conventionally the overall-open bracket). Every category × age bracket the player has played is also available underrating_splits, keyed by{category}-{bracketCode}.This class is dict-like — you can access splits by subscript, iterate them, check membership, and get the length without touching
rating_splitsdirectly:overall = member.sport["pickleball"]["overall-open"].rating for key, split in member.sport["pickleball"]: print(key, split.rating) if "singles-40+" in member.sport["pickleball"]: ... print(len(member.sport["pickleball"]), "splits")
- Parameters:
data (
Any)
- model_config: ClassVar[ConfigDict] = {'extra': 'allow', 'frozen': True, 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- rating_splits: dict[str, RatingSplit]¶
- is_rater: bool¶
Active VAIR Pro (can rate) in this sport. Alias of
is_vair_pro.
- is_vair_pro_status: Literal['PENDING', 'ACTIVE'] | None¶
VAIR-Pro lifecycle status here:
"ACTIVE","PENDING", orNone.
- get(key, default=None)[source]¶
Dict-style safe lookup.
- Parameters:
key (
str)default (
RatingSplit|None(default:None))
- Return type:
- class vairified.models.TournamentImportResult[source]¶
Bases:
BaseModelResult of a tournament import submission.
- Parameters:
data (
Any)
- model_config: ClassVar[ConfigDict] = {'extra': 'allow', 'frozen': True, 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class vairified.models.WebhookDelivery[source]¶
Bases:
BaseModelA single webhook delivery attempt.
- Parameters:
data (
Any)
- model_config: ClassVar[ConfigDict] = {'extra': 'allow', 'frozen': True, 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class vairified.models.WebhookDeliveriesResult[source]¶
Bases:
BaseModelPaginated list of webhook delivery attempts.
- Parameters:
data (
Any)
- model_config: ClassVar[ConfigDict] = {'extra': 'allow', 'frozen': True, 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- deliveries: list[WebhookDelivery]¶
OAuth¶
Vairified OAuth Helpers
Utilities for implementing the “Connect with Vairified” OAuth flow.
- vairified.oauth.OAuthScope¶
Scope string literal — the union of every scope name the Vairified OAuth server accepts. Use this type in your own signatures to get autocomplete and type-checking for the scope strings you pass.
alias of
Literal[‘user:profile:read’, ‘user:profile:email’, ‘user:rating:read’, ‘user:rating:history’, ‘user:match:submit’, ‘user:webhook:subscribe’]
- class vairified.oauth.OAuthConfig[source]¶
Bases:
objectOAuth configuration for a partner application.
- Variables:
api_key – Partner API key.
redirect_uri – Your application’s callback URL.
base_url – Vairified API base URL.
- Parameters:
- client_id: str | None = None¶
Your app’s
client_id— thePartnerApp.slugVairified assigned you (e.g."dinkr"). Required by the browserGET /partner/oauth/authorizeendpoint to identify your app; without it the authorization page rejects the request. Only needed for this pure-frontend URL helper — the recommendedOAuthResource.authorize()flow identifies your app by API key.
- class vairified.oauth.AuthorizationResponse[source]¶
Bases:
objectResponse from starting an OAuth authorization.
- Variables:
authorization_url – Full URL to redirect the user to.
code – Authorization code (for internal tracking).
state – CSRF state parameter.
- Parameters:
- class vairified.oauth.TokenResponse[source]¶
Bases:
objectResponse from exchanging an authorization code for tokens.
- Variables:
access_token – Access token for API requests.
refresh_token – Refresh token for obtaining new access tokens.
expires_in – Token expiration in seconds.
scope – Granted scopes.
player_id – Connected player’s external ID.
- Parameters:
- vairified.oauth.get_authorization_url(config, scopes=None, state=None)[source]¶
Build the URL to redirect users to for OAuth authorization.
This is a helper for building the URL manually. In most cases, you should use the Vairified client’s OAuth methods instead.
- Parameters:
- Return type:
- Returns:
URL to redirect the user to.
Example:
config = OAuthConfig( api_key="vair_pk_xxx", redirect_uri="https://myapp.com/oauth/callback", ) url = get_authorization_url( config, scopes=["user:profile:read", "user:rating:read"] ) # Redirect user to this URL
Errors¶
Vairified SDK Errors
Custom exception classes for API errors.
- exception vairified.errors.VairifiedError[source]¶
Bases:
ExceptionBase exception for Vairified API errors.
- Variables:
message – Error message.
status_code – HTTP status code (if applicable).
response – Raw response body (if available).
- Parameters:
- exception vairified.errors.RateLimitError[source]¶
Bases:
VairifiedErrorRaised when rate limit is exceeded.
- Variables:
retry_after – Seconds to wait before retrying.
- Parameters:
- exception vairified.errors.AuthenticationError[source]¶
Bases:
VairifiedErrorRaised when authentication fails.
Typically means the API key is invalid or expired.
- Parameters:
message (
str(default:'Invalid API key'))
- exception vairified.errors.NotFoundError[source]¶
Bases:
VairifiedErrorRaised when a resource is not found.
Typically means the requested member/player doesn’t exist.
- Parameters:
message (
str(default:'Resource not found'))
- exception vairified.errors.ValidationError[source]¶
Bases:
VairifiedErrorRaised when request validation fails.
Check the response body for details on which fields failed validation.
- Parameters:
message (
str(default:'Validation error'))
- exception vairified.errors.OAuthError[source]¶
Bases:
VairifiedErrorRaised when an OAuth operation fails.
This can occur during authorization, token exchange, refresh, or revocation.
- Variables:
error_code – OAuth error code (e.g., ‘invalid_grant’, ‘expired_token’).
- Parameters: