A modern Python library for interacting with the Apple Push Notification service (APNs) via HTTP/2 protocol using httpx with full async support.
- ✅ HTTP/2 Support: Native HTTP/2 with httpx for optimal performance
- ✅ Async/Await: Full async support with sync wrapper for compatibility
- ✅ Batch Processing: Efficient batch notification sending with HTTP/2 multiplexing
- ✅ Token & Certificate Auth: Both JWT token and certificate authentication
- ✅ Modern Python: Python 3.12+ with full type hints
- ✅ High Performance: Optimized for high-throughput scenarios
- ✅ Comprehensive Testing: High test coverage with 60+ comprehensive tests
- ✅ Production Ready: Battle-tested with proper error handling
from apns2.client import APNsClient
from apns2.payload import Payload
# Initialize client with certificate
client = APNsClient('path/to/cert.pem', use_sandbox=True)
# Create payload
payload = Payload(
alert="Hello World!",
sound="default",
badge=1,
custom={"user_id": 123}
)
# Send notification
token = 'device_token_hex_string'
topic = 'com.example.App'
result = client.send_notification(token, payload, topic)
print(f"Notification result: {result}")from apns2.client import APNsClient
from apns2.credentials import TokenCredentials
from apns2.payload import Payload
# Initialize with JWT token credentials
credentials = TokenCredentials(
auth_key_path='path/to/AuthKey_XXXXXXXXXX.p8',
auth_key_id='XXXXXXXXXX',
team_id='XXXXXXXXXX'
)
client = APNsClient(credentials=credentials, use_sandbox=True)
# Send notification
payload = Payload(alert="Hello from JWT!")
result = client.send_notification(token, payload, topic)from apns2.client import APNsClient, Notification
from apns2.payload import Payload
client = APNsClient('cert.pem', use_sandbox=True)
# Create multiple notifications
notifications = [
Notification(token='token1', payload=Payload(alert="Message 1")),
Notification(token='token2', payload=Payload(alert="Message 2")),
Notification(token='token3', payload=Payload(alert="Message 3")),
]
# Send batch (uses HTTP/2 multiplexing for efficiency)
results = client.send_notification_batch(notifications, topic='com.example.App')
# Process results
for token, result in results.items():
if result == "Success":
print(f"✅ {token}: Delivered")
else:
print(f"❌ {token}: {result}")import asyncio
from apns2.client import APNsClient
from apns2.payload import Payload
async def send_notifications():
client = APNsClient('cert.pem', use_sandbox=True)
payload = Payload(alert="Async notification!")
# Send single notification asynchronously
result = await client.asend_notification(token, payload, topic)
# Send batch asynchronously
results = await client.asend_notification_batch(notifications, topic)
return results
# Run async function
results = asyncio.run(send_notifications())from apns2.payload import Payload, PayloadAlert
# Rich alert with title and subtitle
alert = PayloadAlert(
title="New Message",
subtitle="From John Doe",
body="Hey, how are you doing?",
title_localized_key="NEW_MESSAGE_TITLE",
title_localized_args=["John Doe"]
)
payload = Payload(
alert=alert,
badge=5,
sound="custom_sound.caf",
category="MESSAGE_CATEGORY",
thread_id="conversation_123",
mutable_content=True,
content_available=True,
custom={
"user_info": {"user_id": 12345},
"deep_link": "app://conversation/123"
}
)from apns2.client import APNsClient, NotificationPriority, NotificationType
client = APNsClient('cert.pem')
# VoIP notification
result = client.send_notification(
token=voip_token,
notification=payload,
topic='com.example.App.voip',
push_type=NotificationType.VoIP,
priority=NotificationPriority.Immediate
)
# Background notification
background_payload = Payload(content_available=True)
result = client.send_notification(
token=token,
notification=background_payload,
topic='com.example.App',
push_type=NotificationType.Background
)from apns2.client import APNsClient
from apns2.payload import Payload
client = APNsClient('cert.pem')
payload = Payload(alert="Test")
result = client.send_notification(token, payload, topic)
if result == "Success":
print("✅ Notification sent successfully")
elif result == "BadDeviceToken":
print("❌ Invalid device token")
elif result == "PayloadTooLarge":
print("❌ Payload exceeds 4KB limit")
elif isinstance(result, tuple) and result[0] == "Unregistered":
print(f"❌ Device unregistered at timestamp: {result[1]}")
else:
print(f"❌ Error: {result}")- Python: 3.12+ (with full type hints support)
- httpx: 0.28.1+ with HTTP/2 support (automatically configured)
- cryptography: 1.7.2+ for certificate handling
- PyJWT: 2.0.0+ for token authentication
client = APNsClient(
credentials='cert.pem', # Certificate path or credentials object
use_sandbox=True, # Use sandbox APNs server
use_alternative_port=False, # Use port 2197 instead of 443
json_encoder=CustomEncoder, # Custom JSON encoder class
password='cert_password', # Certificate password
proxy_host='proxy.example.com', # Proxy configuration
proxy_port=8080
)The client automatically configures optimal settings for APNs:
- HTTP/2: Enabled by default for multiplexing
- Connection pooling: Up to 1500 concurrent connections
- Keep-alive: 600 seconds for connection reuse
- Timeouts: Optimized for APNs (10s connect, 30s read)
This project uses Poetry for dependency management and modern Python tooling.
# Clone the repository
git clone https://github.com/Pr0Ger/PyAPNs2.git
cd PyAPNs2
# Install Poetry (if not already installed)
curl -sSL https://install.python-poetry.org | python3 -
# Install dependencies
poetry install --with test,dev# Run all tests
poetry run pytest
# Run with coverage
poetry run coverage run -m pytest && poetry run coverage report
# Run specific test file
poetry run pytest test/test_client.py
# Run async tests only
poetry run pytest -k "async"# Run pre-commit hooks
poetry run pre-commit run --all-files
# Type checking with mypy
poetry run mypy apns2/
# Format code
poetry run ruff format
# Lint code
poetry run ruff check --fixPyAPNs2 is optimized for high-performance scenarios:
- HTTP/2 Multiplexing: Send multiple notifications concurrently over a single connection
- Async Support: Non-blocking I/O for maximum throughput
- Connection Reuse: Efficient connection pooling and keep-alive
- Batch Processing: Optimized batch sending with proper error handling
If migrating from older hyper-based versions:
- Update Python: Minimum Python 3.12+
- Install new version:
poetry add apns2orpip install apns2 - Update imports: Same API, no changes needed
- Async support: New
asend_notificationandasend_notification_batchmethods - Improved performance: Automatic HTTP/2 multiplexing
Contributions are welcome! Please read our contributing guidelines:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes with tests
- Run tests and linting (
poetry run pre-commit run --all-files) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Create a Pull Request
- Type hints: All code must have proper type annotations
- Tests: Maintain 90%+ coverage for new code
- Documentation: Update README and docstrings
- Code style: Follow PEP 8 with ruff formatting
PyAPNs2 is distributed under the terms of the MIT license.
See LICENSE file for the complete license details.