Typed Python client for the IFPA (International Flipper Pinball Association) API
Project description
IFPA API Client
A typed Python client for the IFPA (International Flipper Pinball Association) API. Access player rankings, tournament data, statistics, and more with a clean, modern Python interface.
Alpha Release: This library is under active development. The API is stable but may evolve based on community feedback. Production use is supported, but you may encounter occasional updates to the interface.
Features
- Fully Typed: Complete type hints for IDE autocompletion and type checking
- Pydantic Models: Automatic request/response validation with detailed error messages
- Resource-Oriented API: Intuitive access patterns matching the IFPA API structure
- Comprehensive Coverage: 36 IFPA API v2.1 endpoints implemented (7 player endpoints)
- Handle Pattern: Fluent interface for resource-specific operations
- Pagination Support: Built-in support for paginated endpoints
- Error Handling: Clear exception hierarchy for different failure scenarios
- Well Tested: 99% test coverage with unit and integration tests
Installation
pip install ifpa-api
Requires Python 3.11 or higher.
Quick Start
from ifpa_api import IfpaClient
# Initialize client (uses IFPA_API_KEY environment variable)
client = IfpaClient()
# Search for players
players = client.players.search(name="John", city="Seattle")
for player in players.players:
print(f"{player.player_id}: {player.first_name} {player.last_name}")
# Get player details and rankings
player = client.player(12345).get()
print(f"Name: {player.first_name} {player.last_name}")
print(f"Current WPPR Rank: {player.current_wppr_rank}")
# Get top WPPR rankings
rankings = client.rankings.wppr(start_pos=0, count=100)
for entry in rankings.rankings:
print(f"{entry.rank}. {entry.player_name}: {entry.rating} WPPR")
# Close the client when done
client.close()
Authentication
The client requires an IFPA API key. You can obtain one from the IFPA API documentation.
Option 1: Environment Variable (Recommended)
Set the IFPA_API_KEY environment variable:
export IFPA_API_KEY='your-api-key-here'
Then initialize the client without parameters:
from ifpa_api import IfpaClient
client = IfpaClient()
Option 2: Constructor Parameter
Pass the API key directly to the constructor:
from ifpa_api import IfpaClient
client = IfpaClient(api_key='your-api-key-here')
Usage Examples
Directors
Search for tournament directors and access their tournament history:
from ifpa_api import IfpaClient, TimePeriod
client = IfpaClient()
# Search for directors
directors = client.directors.search(name="Josh")
for director in directors.directors:
print(f"{director.director_id}: {director.first_name} {director.last_name}")
# Get director details
director = client.director(1000).get()
print(f"Director: {director.first_name} {director.last_name}")
print(f"Email: {director.email}")
# Get director's past tournaments
past_tournaments = client.director(1000).tournaments(TimePeriod.PAST)
for tournament in past_tournaments.tournaments:
print(f"{tournament.tournament_name} ({tournament.event_date})")
# Get director's upcoming tournaments
upcoming = client.director(1000).tournaments(TimePeriod.FUTURE)
Players
Access comprehensive player information, rankings, and tournament history:
from ifpa_api import IfpaClient, RankingSystem, ResultType
client = IfpaClient()
# Search for players with filters
players = client.players.search(
name="Smith",
city="Portland",
stateprov="OR",
start_pos=0,
count=25
)
# Get player profile
player = client.player(12345).get()
print(f"Name: {player.first_name} {player.last_name}")
print(f"Location: {player.city}, {player.stateprov}")
print(f"Current Rank: {player.current_wppr_rank}")
print(f"Rating: {player.current_wppr_value}")
print(f"Active Events: {player.active_events}")
# Bulk fetch multiple players (up to 50)
players = client.players.get_multiple([12345, 67890, 11111])
for player in players.player:
print(f"{player.first_name} {player.last_name}")
# Get player's tournament results (both parameters required)
results = client.player(12345).results(
ranking_system=RankingSystem.MAIN,
result_type=ResultType.ACTIVE,
start_pos=0,
count=50
)
for result in results.results:
print(f"{result.tournament_name}: Placed {result.position}")
# Compare two players head-to-head
pvp = client.player(12345).pvp(67890)
print(f"Player 1 Wins: {pvp.player1_wins}")
print(f"Player 2 Wins: {pvp.player2_wins}")
print(f"Ties: {pvp.ties}")
# Get PVP summary for all competitors
pvp_summary = client.player(12345).pvp_all()
print(f"Competed against {pvp_summary.total_competitors} players")
# Get player's ranking history (separate rank and rating arrays)
history = client.player(12345).history()
for entry in history.rank_history:
print(f"{entry.rank_date}: Rank {entry.rank_position}, WPPR {entry.wppr_points}")
for entry in history.rating_history:
print(f"{entry.rating_date}: Rating {entry.rating}")
Rankings
Access various IFPA ranking systems:
from ifpa_api import IfpaClient
client = IfpaClient()
# Get main WPPR rankings
wppr = client.rankings.wppr(start_pos=0, count=100)
for entry in wppr.rankings:
print(f"{entry.rank}. {entry.player_name}: {entry.rating}")
# Get rankings filtered by country
us_rankings = client.rankings.wppr(country="US", count=50)
# Get women's rankings
women = client.rankings.women(start_pos=0, count=50)
# Get youth rankings
youth = client.rankings.youth(start_pos=0, count=50)
# Get professional circuit rankings
pro = client.rankings.pro(start_pos=0, count=50)
# Get virtual tournament rankings
virtual = client.rankings.virtual(start_pos=0, count=50)
# Get country rankings (filtered by country code or name)
countries = client.rankings.by_country(country="US", count=25)
for entry in countries.country_rankings:
print(f"{entry.rank}. {entry.country_name}: {entry.total_players} players")
# Get age-based rankings
under_18 = client.rankings.age_based("u18", start_pos=0, count=50)
# Get custom ranking system
custom = client.rankings.custom("regional-2024", start_pos=0, count=50)
# Get group rankings
group = client.rankings.group("northwest-league", start_pos=0, count=50)
Tournaments
Search for tournaments and access detailed information:
from ifpa_api import IfpaClient
client = IfpaClient()
# Search for tournaments
tournaments = client.tournaments.search(
name="Pinball",
city="Portland",
stateprov="OR",
start_pos=0,
count=25
)
for tournament in tournaments.tournaments:
print(f"{tournament.tournament_name} ({tournament.event_date})")
# Get tournament details
tournament = client.tournament(12345).get()
print(f"Name: {tournament.tournament_name}")
print(f"Location: {tournament.city}, {tournament.stateprov}")
print(f"Date: {tournament.event_date}")
print(f"Players: {tournament.player_count}")
# Get tournament results
results = client.tournament(12345).results()
for result in results.results:
print(f"{result.position}. {result.player_name}: {result.points} points")
# Get tournament formats
formats = client.tournament(12345).formats()
# Get league information (if applicable)
league = client.tournament(12345).league()
Series
Access tournament series standings, player cards, and statistics:
from ifpa_api import IfpaClient
client = IfpaClient()
# List all series
all_series = client.series.list()
for series in all_series.series:
print(f"{series.series_code}: {series.series_name}")
# List only active series
active_series = client.series.list(active_only=True)
# Get series standings
standings = client.series_handle("PAPA").standings(start_pos=0, count=50)
for entry in standings.standings:
print(f"{entry.position}. {entry.player_name}: {entry.points} points")
# Get player's series card
card = client.series_handle("PAPA").player_card(12345)
print(f"Position: {card.current_position}")
print(f"Total Points: {card.total_points}")
for event in card.events:
print(f"{event.tournament_name}: {event.points_earned} points")
# Get series overview
overview = client.series_handle("PAPA").overview()
print(f"Series: {overview.series_name}")
print(f"Total Events: {overview.total_events}")
print(f"Total Players: {overview.total_players}")
# Get series regions
regions = client.series_handle("PAPA").regions()
for region in regions.regions:
print(f"{region.region_name}: {region.player_count} players")
# Get series rules
rules = client.series_handle("PAPA").rules()
print(f"Scoring System: {rules.scoring_system}")
# Get series statistics
stats = client.series_handle("PAPA").stats()
print(f"Total Events: {stats.total_events}")
# Get series schedule
schedule = client.series_handle("PAPA").schedule()
for event in schedule.events:
print(f"{event.event_date}: {event.event_name}")
Configuration
The IfpaClient constructor accepts several configuration options:
from ifpa_api import IfpaClient
client = IfpaClient(
api_key='your-api-key', # API key (or use IFPA_API_KEY env var)
base_url='https://api.ifpapinball.com', # Override base URL
timeout=30.0, # Request timeout in seconds (default: 10.0)
validate_requests=True # Enable Pydantic request validation (default: True)
)
Request Validation
By default, the client validates request parameters using Pydantic models before sending requests to the API. This catches invalid parameters early with clear error messages.
To disable validation:
client = IfpaClient(validate_requests=False)
Error Handling
The client provides a clear exception hierarchy for different failure scenarios:
from ifpa_api import (
IfpaClient,
IfpaError, # Base exception for all SDK errors
MissingApiKeyError, # No API key provided or found in environment
IfpaApiError, # API returned non-2xx status code
IfpaClientValidationError # Request validation failed (when validate_requests=True)
)
client = IfpaClient()
try:
player = client.player(12345).get()
except MissingApiKeyError:
print("API key not configured")
except IfpaApiError as e:
print(f"API error [{e.status_code}]: {e.message}")
except IfpaClientValidationError as e:
print(f"Invalid request parameters: {e.message}")
except IfpaError as e:
print(f"Client error: {e}")
Context Manager
The client supports Python's context manager protocol for automatic resource cleanup:
from ifpa_api import IfpaClient
with IfpaClient() as client:
player = client.player(12345).get()
rankings = client.rankings.wppr(count=100)
# Client automatically closed when exiting the context
Testing
The client includes comprehensive unit and integration tests.
Running Unit Tests
Unit tests use requests-mock and do not require an API key:
poetry run pytest tests/unit/
Running Integration Tests
Integration tests make real API calls and require a valid API key:
export IFPA_API_KEY='your-api-key'
poetry run pytest tests/integration/
Running All Tests with Coverage
poetry run pytest --cov=ifpa_api --cov-report=term-missing
Running Specific Test Markers
# Skip integration tests
poetry run pytest -m "not integration"
# Only integration tests
poetry run pytest -m integration
Development
Setup
- Clone the repository:
git clone https://github.com/johnsosoka/ifpa-api-python.git
cd ifpa-api-python
- Install Poetry (if not already installed):
curl -sSL https://install.python-poetry.org | python3 -
- Install dependencies:
poetry install
- Install pre-commit hooks:
poetry run pre-commit install
Code Quality
The project uses several tools to maintain code quality:
- Black: Code formatting (100 character line length)
- Ruff: Fast Python linting
- mypy: Static type checking
- pytest: Testing framework
Run all checks:
# Format code
poetry run black src tests
# Lint code
poetry run ruff check src tests
# Type check
poetry run mypy src
# Run tests
poetry run pytest
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
Key points:
- Fork the repository and create a feature branch
- Write tests for new features
- Ensure all tests pass and code is formatted
- Submit a pull request with a clear description
API Coverage
The client implements 36 endpoints from IFPA API v2.1:
- Directors: 4 endpoints (search, details, tournaments)
- Players: 7 endpoints (search, bulk fetch, profile, PvP comparison, PvP summary, results, history)
- Rankings: 9 endpoints (WPPR, women, youth, virtual, pro, country, age-based, custom, group)
- Tournaments: 6 endpoints (search, details, results, formats, league, submissions)
- Series: 8 endpoints (list, standings, player cards, overview, regions, rules, stats, schedule)
- Reference: 2 endpoints (countries, states)
Known Limitations
Stats Endpoints Not Available: The IFPA API v2.1 specification includes 10 Stats endpoints (/v2.1/stats/*), but these endpoints currently return HTTP 404 from the live API server. As a result, they are not implemented in this client. These endpoints will be added in a future release when the IFPA API makes them available.
This limitation does not affect any other functionality—all other documented endpoints are fully implemented and tested.
Resources
- IFPA Website: https://www.ifpapinball.com/
- IFPA API Documentation: https://api.ifpapinball.com/docs
- Client Documentation: https://github.com/johnsosoka/ifpa-api-python
- Issue Tracker: https://github.com/johnsosoka/ifpa-api-python/issues
- PyPI Package: https://pypi.org/project/ifpa-api/
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
- Built by John Sosoka for the pinball community
- Thanks to IFPA for providing the public API
- Special thanks to all contributors and users
Support
- Bug Reports: Open an issue on GitHub Issues
- Feature Requests: Open an issue with the
enhancementlabel - Questions: Check the API documentation or open a discussion
Made with care for the pinball community by John Sosoka
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ifpa_api-0.2.0.tar.gz.
File metadata
- Download URL: ifpa_api-0.2.0.tar.gz
- Upload date:
- Size: 31.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5bd941f1f6577f83a0ee8f166116c0c07f1e4f78fbcc4f0416976c32c9a35a14
|
|
| MD5 |
2f6753951c867f50a4553b6093cc4479
|
|
| BLAKE2b-256 |
0a68b42ab6b1dc34baf38273c32e320b91e5ac268887e9f59949e434c9a6b110
|
Provenance
The following attestation bundles were made for ifpa_api-0.2.0.tar.gz:
Publisher:
publish.yml on johnsosoka/ifpa-api-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ifpa_api-0.2.0.tar.gz -
Subject digest:
5bd941f1f6577f83a0ee8f166116c0c07f1e4f78fbcc4f0416976c32c9a35a14 - Sigstore transparency entry: 701842682
- Sigstore integration time:
-
Permalink:
johnsosoka/ifpa-api-python@8c6ee9f7bee003f6c56581215b360e3c2c1666e6 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/johnsosoka
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8c6ee9f7bee003f6c56581215b360e3c2c1666e6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ifpa_api-0.2.0-py3-none-any.whl.
File metadata
- Download URL: ifpa_api-0.2.0-py3-none-any.whl
- Upload date:
- Size: 37.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e1040dfbd578293802bc5555872b2fbc06c343a52d52466389ce802545f7cda8
|
|
| MD5 |
64d3fb4bcfb0dea87bdd8cc0f3597738
|
|
| BLAKE2b-256 |
882b26d0d0a5809cc36fbbdb60b4acb2ab8999daeaff9d9478237bcbb9784222
|
Provenance
The following attestation bundles were made for ifpa_api-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on johnsosoka/ifpa-api-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ifpa_api-0.2.0-py3-none-any.whl -
Subject digest:
e1040dfbd578293802bc5555872b2fbc06c343a52d52466389ce802545f7cda8 - Sigstore transparency entry: 701842719
- Sigstore integration time:
-
Permalink:
johnsosoka/ifpa-api-python@8c6ee9f7bee003f6c56581215b360e3c2c1666e6 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/johnsosoka
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8c6ee9f7bee003f6c56581215b360e3c2c1666e6 -
Trigger Event:
push
-
Statement type: