Python Quick Start
Integrate Indieop API into your Python games using Pygame, Panda3D, or other frameworks.
Prerequisites
Before you begin, make sure you have:
- Created an Indieop account
- Created a game and obtained your API key from the game settings
- Python 3.7 or higher installed
- The
requestslibrary:pip install requests
Overview
This guide shows you how to send player feedback and form submissions from your Python game to the Indieop API. Works with:
- Pygame: Popular 2D game development library
- Panda3D: 3D game engine with Python
- Arcade: Modern Python framework for 2D games
- Ren'Py: Visual novel engine
Basic Implementation
Step 1: Install Dependencies
Install the requests library if you haven't already:
pip install requests
Step 2: Create the API Client
Create a new file called indieop_client.py:
"""
IndieOp API Client for Python Games
"""
import requests
import json
from typing import Dict, List, Optional, Union, Any
class IndieOpClient:
"""Client for submitting player feedback to IndieOp API"""
BASE_URL = "https://indieop.com/api/sdk"
def __init__(self, api_key: str):
"""
Initialize the IndieOp client
Args:
api_key: Your game's API key from the IndieOp dashboard
"""
self.api_key = api_key
self.session = requests.Session()
self.session.headers.update({
"X-API-Key": api_key,
"Content-Type": "application/json"
})
def submit_form(self, submission_data: Dict[str, Any]) -> Dict[str, Any]:
"""
Submit a form to the IndieOp API
Args:
submission_data: Dictionary containing form data
Returns:
Dictionary with 'success' and 'data' or 'error' keys
Raises:
requests.RequestException: If the request fails
"""
url = f"{self.BASE_URL}/submit"
try:
response = self.session.post(url, json=submission_data, timeout=10)
response_data = response.json()
if response.status_code in (200, 201):
print(f"✓ Submission successful: {response_data['message']}")
return {
"success": True,
"data": response_data.get("data", {})
}
else:
print(f"✗ Submission failed: {response_data.get('message', 'Unknown error')}")
return {
"success": False,
"error": response_data.get("message", "Unknown error"),
"errors": response_data.get("errors", {})
}
except requests.exceptions.RequestException as e:
print(f"✗ Network error: {str(e)}")
return {
"success": False,
"error": f"Network error: {str(e)}"
}
@staticmethod
def create_field(key: str, label: str, field_type: str,
value: Union[str, int, bool],
options: Optional[List[str]] = None) -> Dict[str, Any]:
"""
Helper method to create a properly formatted field
Args:
key: Unique identifier for the field
label: Display label for the field
field_type: Type of field (text, rating, checkbox, dropdown)
value: The field value
options: List of options (required for dropdown fields)
Returns:
Dictionary representing the field
"""
field = {
"key": key,
"label": label,
"type": field_type,
"value": value
}
if options is not None:
field["options"] = options
return field
def create_submission(self, form_name: str, tag: str,
fields: List[Dict[str, Any]],
form_type: str = "general",
player_identifier: Optional[str] = None,
game_version: Optional[str] = None,
repository_version: Optional[str] = None) -> Dict[str, Any]:
"""
Helper method to create a submission dictionary
Args:
form_name: Name of the form
tag: Unique tag for the form
fields: List of field dictionaries
form_type: Type of form (general, feedback, bug_report)
player_identifier: Optional player ID
game_version: Optional game version
repository_version: Optional repository version
Returns:
Dictionary ready to be submitted
"""
submission = {
"form_name": form_name,
"tag": tag,
"form_type": form_type,
"fields": fields
}
if player_identifier:
submission["player_identifier"] = player_identifier
if game_version:
submission["game_version"] = game_version
if repository_version:
submission["repository_version"] = repository_version
return submission
Step 3: Use the Client
Here's a simple example of using the client:
from indieop_client import IndieOpClient
# Initialize the client
client = IndieOpClient("your_game_api_key_here")
# Create fields
fields = [
client.create_field("rating", "Overall Rating", "rating", 5),
client.create_field("comments", "Your Feedback", "text", "Amazing game!"),
client.create_field("difficulty", "Difficulty Level", "dropdown", "Medium",
options=["Easy", "Medium", "Hard"]),
client.create_field("enjoyed", "Did you enjoy?", "checkbox", True)
]
# Create submission
submission = client.create_submission(
form_name="Player Feedback",
tag="feedback",
fields=fields,
form_type="feedback",
game_version="1.0.0"
)
# Submit
result = client.submit_form(submission)
if result["success"]:
print("Thank you for your feedback!")
else:
print(f"Error: {result['error']}")
Pygame Integration Example
Here's how to integrate feedback collection into a Pygame game:
import pygame
import sys
from indieop_client import IndieOpClient
# Initialize Pygame
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("My Game")
clock = pygame.time.Clock()
# Initialize IndieOp client
indieop = IndieOpClient("your_game_api_key_here")
class FeedbackForm:
def __init__(self):
self.active = False
self.rating = 5
self.comments = ""
self.submitted = False
def show(self):
"""Display feedback form"""
self.active = True
def handle_event(self, event):
"""Handle input events for the form"""
if not self.active:
return
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
self.submit()
elif event.key == pygame.K_ESCAPE:
self.active = False
elif event.key == pygame.K_BACKSPACE:
self.comments = self.comments[:-1]
elif event.unicode.isprintable():
self.comments += event.unicode
def submit(self):
"""Submit the feedback"""
fields = [
indieop.create_field("rating", "Overall Rating", "rating", self.rating),
indieop.create_field("comments", "Comments", "text", self.comments)
]
submission = indieop.create_submission(
form_name="Player Feedback",
tag="feedback",
fields=fields,
form_type="feedback",
game_version="1.0.0"
)
result = indieop.submit_form(submission)
if result["success"]:
self.submitted = True
print("Feedback submitted successfully!")
else:
print(f"Failed to submit: {result['error']}")
self.active = False
def draw(self, surface):
"""Draw the feedback form"""
if not self.active:
return
# Draw semi-transparent overlay
overlay = pygame.Surface((800, 600))
overlay.set_alpha(200)
overlay.fill((0, 0, 0))
surface.blit(overlay, (0, 0))
# Draw form background
form_rect = pygame.Rect(150, 150, 500, 300)
pygame.draw.rect(surface, (255, 255, 255), form_rect)
pygame.draw.rect(surface, (0, 0, 0), form_rect, 2)
# Draw text
font = pygame.font.Font(None, 32)
title = font.render("Rate Your Experience", True, (0, 0, 0))
surface.blit(title, (200, 180))
# Draw rating
rating_text = font.render(f"Rating: {self.rating}/5", True, (0, 0, 0))
surface.blit(rating_text, (200, 230))
# Draw comments
comments_font = pygame.font.Font(None, 24)
comments_text = comments_font.render(f"Comments: {self.comments}", True, (0, 0, 0))
surface.blit(comments_text, (200, 280))
# Draw instructions
instruction_font = pygame.font.Font(None, 20)
instructions = [
"Type your comments",
"Press ENTER to submit",
"Press ESC to cancel"
]
y = 350
for instruction in instructions:
text = instruction_font.render(instruction, True, (100, 100, 100))
surface.blit(text, (200, y))
y += 25
# Game loop
feedback_form = FeedbackForm()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Handle feedback form events
feedback_form.handle_event(event)
# Show feedback form on F key
if event.type == pygame.KEYDOWN and event.key == pygame.K_f:
feedback_form.show()
# Clear screen
screen.fill((50, 50, 50))
# Draw game content here
font = pygame.font.Font(None, 36)
text = font.render("Press F for Feedback", True, (255, 255, 255))
screen.blit(text, (250, 280))
# Draw feedback form on top
feedback_form.draw(screen)
pygame.display.flip()
clock.tick(60)
pygame.quit()
sys.exit()
Async Implementation (Optional)
For non-blocking submissions, use an async version:
"""
Async IndieOp Client using aiohttp
Install: pip install aiohttp
"""
import aiohttp
import asyncio
from typing import Dict, Any
class AsyncIndieOpClient:
"""Async client for non-blocking submissions"""
BASE_URL = "https://indieop.com/api/sdk"
def __init__(self, api_key: str):
self.api_key = api_key
self.headers = {
"X-API-Key": api_key,
"Content-Type": "application/json"
}
async def submit_form(self, submission_data: Dict[str, Any]) -> Dict[str, Any]:
"""Submit form asynchronously"""
url = f"{self.BASE_URL}/submit"
try:
async with aiohttp.ClientSession() as session:
async with session.post(url, json=submission_data,
headers=self.headers,
timeout=10) as response:
response_data = await response.json()
if response.status in (200, 201):
print(f"✓ Submission successful")
return {"success": True, "data": response_data.get("data", {})}
else:
print(f"✗ Submission failed: {response_data.get('message')}")
return {"success": False, "error": response_data.get("message")}
except aiohttp.ClientError as e:
print(f"✗ Network error: {str(e)}")
return {"success": False, "error": str(e)}
# Usage example
async def main():
client = AsyncIndieOpClient("your_game_api_key_here")
submission = {
"form_name": "Player Feedback",
"tag": "feedback",
"form_type": "feedback",
"game_version": "1.0.0",
"fields": [
{"key": "rating", "label": "Rating", "type": "rating", "value": 5},
{"key": "comments", "label": "Comments", "type": "text", "value": "Great!"}
]
}
result = await client.submit_form(submission)
print(result)
# Run async code
if __name__ == "__main__":
asyncio.run(main())
Background Submission (Threading)
To avoid blocking your game loop, submit in a background thread:
import threading
from indieop_client import IndieOpClient
class BackgroundSubmitter:
"""Submit forms in background threads to avoid blocking"""
def __init__(self, api_key: str):
self.client = IndieOpClient(api_key)
def submit_async(self, submission_data, callback=None):
"""Submit in a background thread"""
def submit():
result = self.client.submit_form(submission_data)
if callback:
callback(result)
thread = threading.Thread(target=submit, daemon=True)
thread.start()
# Usage in your game
submitter = BackgroundSubmitter("your_game_api_key_here")
def on_submit_complete(result):
if result["success"]:
print("Feedback submitted successfully!")
else:
print(f"Failed: {result['error']}")
# Submit without blocking
submission = {
"form_name": "Player Feedback",
"tag": "feedback",
"form_type": "feedback",
"fields": [
{"key": "rating", "label": "Rating", "type": "rating", "value": 5}
]
}
submitter.submit_async(submission, on_submit_complete)
print("Game continues running while submission happens in background...")
Field Types Reference
The Indieop API supports four field types:
| Field Type | Python Type | Example |
|---|---|---|
| text | str | "Great game!" |
| rating | int (1-5) | 5 |
| checkbox | bool | True |
| dropdown | str (from List) | "Medium" |
Best Practices
- Use environment variables: Store your API key in
.envfiles, never in code - Background threads: Use threading or async to avoid blocking your game loop
- Error handling: Always handle network errors gracefully
- Queue failed submissions: Store locally if submission fails and retry later
- Type hints: Use Python type hints for better code quality
- Rate limiting: Don't exceed 100 requests per minute