Python SDK for Listify API
Project description
ListifyPy - Python SDK for Listify API
A comprehensive Python SDK for the Listify API - the easiest way to integrate your bot with Listify's bot listing platform.
📦 Features
-
✅ Full API Client - All Listify API endpoints with automatic retries and rate limiting
-
✅ Webhook Handler - Secure webhook verification with HMAC-SHA256 signatures
-
✅ Widget Generator - Beautiful embeddable badges and widgets for your README
-
✅ Type Safe - Full type hints for excellent IDE support
-
✅ Async/Await - Built with async/await for high performance
-
✅ Caching - Intelligent response caching with TTL
-
✅ Rate Limiting - Automatic handling of API rate limits
-
✅ Retry Logic - Exponential backoff for failed requests
-
✅ Event System - Subscribe to API events for monitoring
-
✅ Zero Dependencies - Only requires
httpxandtyping-extensions
📋 Table of Contents
🚀 Installation
Using pip
pip install listifypy
Using poetry
poetry add listifypy
Development Installation
git clone https://github.com/phenuop/listify-pythonsdk.git
cd listify-pythonsdk
pip install -e .[dev]
🏃 Quick Start
import asyncio
from listifypy import Api
async def main():
# Initialize the client
client = Api(
bot_id="your-bot-id",
options={
"token": "your-api-token"
}
)
# Push bot statistics
await client.push_stats({
"platform": "discord",
"server_count": 420,
"user_count": 45000,
"shard_count": 4,
"status": "online"
})
# Check if a user has voted
result = await client.check_vote({
"platform": "discord",
"user_id": "80351110224678912"
})
if result["voted"]:
print("User has voted!")
else:
print("User hasn't voted yet")
asyncio.run(main())
📚 API Client
Initialization
from listifypy import Api
# Basic initialization
client = Api(
bot_id="your-bot-id",
options={
"token": "your-api-token"
}
)
# Advanced configuration
client = Api(
bot_id="your-bot-id",
options={
"token": "your-api-token",
"base_url": "https://www.rsdash.net", # Custom API URL
"max_retries": 5, # Maximum retry attempts
"backoff_base": 1000, # Base delay for exponential backoff (ms)
"cache": True, # Enable response caching
"cache_ttl": 600, # Cache TTL in seconds
"log_level": "debug", # Logging level
"log_format": "json", # Log format (text or json)
"timeout": 60.0, # Request timeout in seconds
"debug": True, # Enable debug mode
"user_agent": "MyBot/1.0.0" # Custom User-Agent
}
)
Push Statistics
Update your bot's statistics on Listify:
# Push basic stats
await client.push_stats({
"platform": "discord",
"server_count": 420,
"user_count": 45000,
"shard_count": 4,
"status": "online"
})
# Push with minimal stats
await client.push_stats({
"platform": "discord",
"server_count": 420
})
# Push with maintenance status
await client.push_stats({
"platform": "discord",
"server_count": 420,
"status": "maintenance"
})
Update Status
Update your bot's runtime status:
# Update status for a specific platform
await client.update_status({
"platform": "discord",
"status": "online"
})
# Update status (if only one platform)
await client.update_status({
"status": "maintenance"
})
# Available statuses: "online", "offline", "maintenance"
Check Votes
Check if a user has voted in the last 12 hours:
# Check vote for Discord user
result = await client.check_vote({
"platform": "discord",
"user_id": "80351110224678912"
})
print(f"Voted: {result['voted']}")
print(f"Voted at: {result['voted_at']}")
print(f"Next vote at: {result['next_vote_at']}")
# Check vote for Fluxer user
result = await client.check_vote({
"platform": "fluxer",
"user_id": "user123"
})
# Handle vote status
if result["voted"]:
await give_rewards(user_id)
else:
await remind_user_to_vote(user_id)
Sync Commands
Sync slash commands to your listing page:
# Define your commands
commands = [
{
"name": "ping",
"description": "Check bot latency",
"category": "Utility"
},
{
"name": "play",
"description": "Play a song",
"category": "Music",
"usage": "/play <song name>",
"options": [
{
"name": "query",
"description": "Song name or URL",
"type": 3, # STRING
"required": True
}
]
},
{
"name": "ban",
"description": "Ban a user",
"category": "Moderation",
"options": [
{
"name": "user",
"description": "User to ban",
"type": 6, # USER
"required": True
},
{
"name": "reason",
"description": "Ban reason",
"type": 3, # STRING
"required": False
}
]
}
]
# Sync commands
result = await client.sync_commands({
"platform": "discord",
"commands": commands,
"mode": "replace" # or "merge"
})
print(f"Created: {result['created']}")
print(f"Updated: {result['updated']}")
print(f"Removed: {result['removed']}")
print(f"Total: {result['total']}")
Get Bot Information
Retrieve detailed information about your bot:
# Get your own bot info
bot = await client.get_bot()
print(f"Name: {bot['name']}")
print(f"Slug: {bot['slug']}")
print(f"Description: {bot['description']}")
print(f"Platforms: {', '.join(bot['platforms'])}")
# Vote statistics
votes = bot['votes']
print(f"Current votes: {votes['current']}")
print(f"Total votes: {votes['total']}")
print(f"Votes today: {votes['today']}")
# Rating information
rating = bot['rating']
print(f"Rating: {rating['score']}/5 ({rating['count']} reviews)")
# Get another bot's info
other_bot = await client.get_bot("other-bot-id")
Get Analytics
Retrieve analytics for your bot:
analytics = await client.get_analytics()
print(f"Views: {analytics['views']}")
print(f"Clicks: {analytics['clicks']}")
print(f"Unique Visitors: {analytics['unique_visitors']}")
print(f"Date: {analytics['date']}")
Get Reviews
Retrieve reviews for your bot:
# Get latest 10 reviews
reviews = await client.get_reviews(limit=10)
for review in reviews:
print(f"User: {review['username']}")
print(f"Rating: {'★' * review['rating']}")
print(f"Comment: {review['comment']}")
print(f"Date: {review['created_at']}")
print("-" * 40)
# Get all reviews (no limit)
reviews = await client.get_reviews()
Search Bots
Search for bots on Listify:
# Search by keyword
results = await client.search_bots("music", limit=10)
for bot in results:
print(f"{bot['name']}: {bot['description']}")
print(f"Votes: {bot['votes']['current']}")
print(f"Rating: {bot['rating']['score']}/5")
print("-" * 40)
# Search with custom limit
results = await client.search_bots("moderation", limit=5)
Get Stats History
Retrieve historical statistics:
# Get last 7 days of stats
history = await client.get_stats_history(7)
for entry in history:
print(f"{entry['date']}: {entry['server_count']} servers")
# Get last 30 days (default)
history = await client.get_stats_history()
Health Check
Check the API health status:
health = await client.health_check()
print(f"Status: {health['status']}")
print(f"Timestamp: {health['timestamp']}")
🔔 Webhook Handler
The Webhook handler provides secure, type-safe webhook processing with signature verification.
FastAPI Example
from fastapi import FastAPI, Request
from listifypy import Webhook, is_vote_created
app = FastAPI()
webhook = Webhook("your-webhook-secret", {"debug": True})
@app.post("/webhook")
async def webhook_endpoint(request: Request):
# Get raw body
raw_body = await request.body()
# Verify and parse the webhook
payload = webhook.verify_and_parse_request(
request.headers.get("x-listify-timestamp"),
request.headers.get("x-listify-signature"),
raw_body.decode()
)
if not payload:
return {"error": "Invalid signature"}, 403
# Handle different event types
if is_vote_created(payload):
user_id = payload["data"]["identities"]["discord"]["id"]
if user_id:
# Grant rewards to the user
await grant_rewards(user_id)
print(f"✅ Rewards granted to {user_id}")
return {"success": True}
Flask Example
from flask import Flask, request, jsonify
from listifypy import Webhook
app = Flask(__name__)
webhook = Webhook("your-webhook-secret")
@app.route("/webhook", methods=["POST"])
def webhook_endpoint():
raw_body = request.get_data(as_text=True)
payload = webhook.verify_and_parse_request(
request.headers.get("x-listify-timestamp"),
request.headers.get("x-listify-signature"),
raw_body
)
if not payload:
return jsonify({"error": "Invalid signature"}), 403
# Process the payload
if payload["event"] == "vote.created":
user_id = payload["data"]["identities"]["discord"]["id"]
if user_id:
grant_rewards(user_id)
return jsonify({"success": True})
Django Example
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from listifypy import Webhook
webhook = Webhook("your-webhook-secret")
@csrf_exempt
def webhook_endpoint(request):
raw_body = request.body.decode('utf-8')
payload = webhook.verify_and_parse_request(
request.headers.get("x-listify-timestamp"),
request.headers.get("x-listify-signature"),
raw_body
)
if not payload:
return JsonResponse({"error": "Invalid signature"}, status=403)
if payload["event"] == "vote.created":
user_id = payload["data"]["identities"]["discord"]["id"]
if user_id:
grant_rewards(user_id)
return JsonResponse({"success": True})
Event Handling
Listen to specific webhook events:
from listifypy import Webhook
webhook = Webhook("your-webhook-secret")
# Register event handlers
@webhook.on("vote.created")
def handle_vote_created(payload):
user_id = payload["data"]["identities"]["discord"]["id"]
print(f"🎉 User {user_id} voted!")
grant_rewards(user_id)
@webhook.on("review.created")
def handle_review_created(payload):
print(f"⭐ New review: {payload['data']['rating']}/5")
print(f"Comment: {payload['data']['comment']}")
@webhook.on("listing.verified")
def handle_listing_verified(payload):
print(f"✅ Bot verified! Badge: {payload['data']['badge']}")
@webhook.on("webhook") # Catch-all event
def handle_any_event(payload):
print(f"📨 Received event: {payload['event']}")
Testing Webhooks
Test your webhook handlers locally:
from listifypy import Webhook
import json
import time
webhook = Webhook("test-secret")
# Create a test payload
payload = webhook.create_test_payload(
bot_id="test-bot-id",
bot_slug="my-bot",
bot_name="My Bot",
event_type="vote.created"
)
# Generate signature
timestamp = str(int(time.time() * 1000))
raw_body = json.dumps(payload)
signature = webhook.generate_signature(timestamp, raw_body)
# Verify the signature
is_valid = webhook.validate_signature(timestamp, signature, raw_body)
print(f"Signature valid: {is_valid}")
🎨 Widget Generator
The Widget generator creates beautiful embeddable badges for your README and website.
Widget Types
from listifypy import Widget, create_widget
# Available widget types
widgets = {
"large": "Full bot information with name, avatar, vote count, and rating",
"votes": "Show only the vote count (perfect for README badges)",
"owner": "Show bot owner avatar and name",
"social": "Show server count, user count, and online status",
"rating": "Show star rating and review count",
"status": "Show online/offline/maintenance status",
"combined": "Show votes, servers, and rating in one compact widget"
}
# Generate widget URLs
votes_url = Widget.votes("discord", "bot", "your-bot-id")
large_url = Widget.large("discord", "bot", "your-bot-id")
status_url = Widget.status("discord", "bot", "your-bot-id")
rating_url = Widget.rating("discord", "bot", "your-bot-id")
combined_url = Widget.combined("discord", "bot", "your-bot-id")
# Using the helper function
votes_url = create_widget("votes", "discord", "bot", "your-bot-id")
Customization
Customize widget appearance:
# Dark theme with custom colors
url = Widget.combined("discord", "bot", "your-bot-id", {
"theme": "dark",
"bg": "#2C2F33",
"color": "#FFFFFF",
"border": "#7289DA",
"minimal": False
})
# Light theme with minimal design
url = Widget.votes("discord", "bot", "your-bot-id", {
"theme": "light",
"bg": "#FFFFFF",
"color": "#000000",
"minimal": True
})
# Custom colors only
url = Widget.large("discord", "bot", "your-bot-id", {
"bg": "#5865F2",
"color": "#FFFFFF"
})
HTML & Markdown
Generate HTML or Markdown for embedding:
from listifypy import Widget
url = Widget.votes("discord", "bot", "your-bot-id")
# Generate HTML
html = Widget.to_html(
url,
alt="Vote on Listify",
options={
"class_name": "badge",
"style": {"margin": "10px", "border": "1px solid #ccc"},
"width": 200,
"height": 50
}
)
print(html)
# <img src="..." alt="Vote on Listify" class="badge" style="margin:10px;border:1px solid #ccc" width="200" height="50" />
# Generate Markdown
markdown = Widget.to_markdown(url, "Vote on Listify")
print(markdown)
# 
Badge Shortcuts
Quick methods for common badge types:
from listifypy import Widget
# Vote badge
vote_badge = Widget.badge_votes("discord", "bot", "your-bot-id")
# Status badge
status_badge = Widget.badge_status("discord", "bot", "your-bot-id")
# Rating badge
rating_badge = Widget.badge_rating("discord", "bot", "your-bot-id")
README Example
# My Discord Bot
[](https://www.rsdash.net/bots/your-bot-id)
[](https://www.rsdash.net/bots/your-bot-id)
[](https://www.rsdash.net/bots/your-bot-id)
## Features
- Feature 1
- Feature 2
- Feature 3
🛡️ Error Handling
The SDK provides comprehensive error handling with ListifyAPIError:
from listifypy import Api, ListifyAPIError, is_network_error
client = Api("bot-id", {"token": "token"})
try:
result = await client.push_stats({
"platform": "discord",
"server_count": 420
})
except ListifyAPIError as error:
print(f"API Error: {error.message}")
print(f"Status: {error.status}")
print(f"Path: {error.path}")
print(f"Method: {error.method}")
# Check error type
if error.is_rate_limit_error():
print("Rate limited! Waiting before retry...")
await asyncio.sleep(error.get_retry_delay() / 1000)
if error.is_auth_error():
print("Authentication failed! Check your token.")
if error.is_server_error():
print("Server error! Retrying...")
if error.should_retry():
await asyncio.sleep(error.get_retry_delay() / 1000)
# Get user-friendly message
print(f"Friendly message: {error.get_friendly_message()}")
except Exception as error:
if is_network_error(error):
print("Network error occurred!")
else:
print(f"Unexpected error: {error}")
Error Type Guards
from listifypy import (
is_listify_api_error,
is_network_error,
is_timeout_error
)
try:
# API call
pass
except Exception as error:
if is_listify_api_error(error):
print(f"API Error: {error.message}")
elif is_network_error(error):
print("Network error - retrying...")
elif is_timeout_error(error):
print("Request timed out - increasing timeout...")
⚙️ Configuration
Environment Variables
# Set environment variables
export LISTIFY_BOT_ID="your-bot-id"
export LISTIFY_API_TOKEN="your-api-token"
export LISTIFY_WEBHOOK_SECRET="your-webhook-secret"
import os
from listifypy import Api
client = Api(
bot_id=os.getenv("LISTIFY_BOT_ID"),
options={
"token": os.getenv("LISTIFY_API_TOKEN"),
"log_level": os.getenv("LISTIFY_LOG_LEVEL", "info")
}
)
Logging Configuration
# Text logging (default)
client = Api("bot-id", {
"token": "token",
"log_level": "debug",
"log_format": "text"
})
# JSON logging (for structured logging)
client = Api("bot-id", {
"token": "token",
"log_level": "debug",
"log_format": "json"
})
# Silent mode (no logging)
client = Api("bot-id", {
"token": "token",
"log_level": "silent"
})
Cache Configuration
# Enable cache with custom TTL
client = Api("bot-id", {
"token": "token",
"cache": True,
"cache_ttl": 600 # 10 minutes
})
# Disable cache
client = Api("bot-id", {
"token": "token",
"cache": False
})
# Clear cache manually
client.clear_cache()
# Get cache status
status = client.get_rate_limit_status()
print(f"Rate limit: {status['remaining']}/{status['limit']}")
Rate Limiting
The SDK handles rate limiting automatically:
# Get current rate limit status
status = client.get_rate_limit_status()
print(f"Remaining: {status['remaining']}")
print(f"Reset at: {status['reset_at']}")
# Get pending requests count
pending = client.get_pending_requests()
print(f"Pending requests: {pending}")
🔬 Type System
The SDK is fully typed with type hints:
from listifypy import (
Api,
Webhook,
Widget,
PlatformKey,
RuntimeStatus,
StatsUpdateInput,
StatusUpdateInput,
VoteCheckInput,
CommandSyncInput,
WebhookPayload,
WidgetOptions
)
# Type-safe usage
platform: PlatformKey = "discord"
status: RuntimeStatus = "online"
stats: StatsUpdateInput = {
"platform": platform,
"server_count": 420,
"status": status
}
# Type guards
from listifypy import is_platform_key, is_runtime_status
if is_platform_key(platform):
print(f"Valid platform: {platform}")
if is_runtime_status(status):
print(f"Valid status: {status}")
👥 Contributing
Contributions are welcome! Here's how to contribute:
-
Fork the repository
-
Create a feature branch
-
Make your changes
-
Run tests
-
Submit a pull request
Development Setup
# Clone the repository
git clone https://github.com/phenuop/listify-pythonsdk.git
cd listify-pythonsdk
# Install development dependencies
pip install -e .[dev]
# Run tests
pytest tests/ -v
# Run tests with coverage
pytest tests/ -v --cov=src --cov-report=html
# Format code
black src/ tests/
# Run linting
flake8 src/ tests/
# Type checking
mypy src/
Running Tests
# Run all tests
pytest
# Run specific test file
pytest tests/test_api.py -v
# Run with coverage
pytest --cov=src --cov-report=term-missing
# Run specific test class
pytest tests/test_all.py::TestApiClient -v
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🙏 Support
-
Documentation: https://docs.rsdash.net
-
Issues: GitHub Issues
-
Discord: Join our Discord
📊 Version History
-
0.1.0 - Initial release
-
Full API client
-
Webhook handler with signature verification
-
Widget generator with 7 widget types
-
Complete type system
-
Comprehensive test suite
-
Made with ❤️ by the Listify Team
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 listifypy-0.1.0.tar.gz.
File metadata
- Download URL: listifypy-0.1.0.tar.gz
- Upload date:
- Size: 41.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5375b6fd70ba777ae075e6595a99305976784b96e9f9d51d56b0e39eb43d0436
|
|
| MD5 |
6c4a3655e5e2b8fc77aa3973da1145e8
|
|
| BLAKE2b-256 |
41a8963b65f3e85e9db20b8b097435421cdf91b65a043752267044432c7c133c
|
File details
Details for the file listifypy-0.1.0-py3-none-any.whl.
File metadata
- Download URL: listifypy-0.1.0-py3-none-any.whl
- Upload date:
- Size: 27.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c23de0cae2303ff4085681c40640faad7afd370423018cf12fb256713c0a7d1f
|
|
| MD5 |
9ea7fe058085a3ccbd7d5117c83b1fe4
|
|
| BLAKE2b-256 |
674f5010e99695cad35422c0d35acbfc0bf8e143071114358f4ace6ea343a769
|