pyapns-client: Apple Push Notifications (HTTP/2)
pyapns-client is a Python library designed for sending Apple Push Notifications (APNs) to iOS, macOS, and Safari using the modern HTTP/2 Push provider API. It focuses on simplicity, flexibility, and speed, leveraging token-based authentication for enhanced security and avoiding certificate renewal hassles. The current version is 2.0.6, with a more recent async-enabled version (pyapns-client3) also available. This library is actively maintained, with the latest update for the 2.x series in June 2022.
Warnings
- breaking This library requires Python 3.6 or higher. Older Python 2.x environments or Python 3.5 and below are not supported.
- gotcha The `topic` parameter in `IOSNotification` *must* precisely match your application's bundle ID (e.g., 'com.example.yourapp'). A mismatch will result in push delivery failures.
- deprecated This library (pyapns-client) uses Apple's HTTP/2 APNs API with token-based authentication. Apple deprecated the legacy binary API in late 2019. Using older APNs libraries that rely on the binary API or only certificate-based authentication (without JWT tokens) is highly discouraged and may cease to function.
- gotcha APNs responses require robust error handling. Device tokens can become invalid (e.g., app uninstalled), leading to `UnregisteredException`. Server issues (e.g., throttling) can cause `APNSServerException`. Incorrect payload or settings will raise `APNSProgrammingException`.
- gotcha The APNs payload has size limits (4KB for most notifications, 5KB for VoIP). Exceeding this limit will result in delivery failure without a clear error from the client library.
Install
-
pip install pyapns-client
Imports
- APNSClient
from pyapns_client import APNSClient
- IOSPayloadAlert
from pyapns_client import IOSPayloadAlert
- IOSPayload
from pyapns_client import IOSPayload
- IOSNotification
from pyapns_client import IOSNotification
- APNSDeviceException
from pyapns_client import APNSDeviceException
- APNSServerException
from pyapns_client import APNSServerException
- APNSProgrammingException
from pyapns_client import APNSProgrammingException
- UnregisteredException
from pyapns_client import UnregisteredException
Quickstart
import os
from pyapns_client import APNSClient, IOSPayloadAlert, IOSPayload, IOSNotification, APNSDeviceException, APNSServerException, APNSProgrammingException, UnregisteredException
# --- Environment Variables (Replace with your actual values or secure fetching) ---
# APNS_MODE: APNSClient.MODE_DEV or APNSClient.MODE_PROD
# APNS_AUTH_KEY_PATH: Path to your .p8 authentication key file
# APNS_AUTH_KEY_ID: Your 10-character Key ID from Apple Developer portal
# APNS_TEAM_ID: Your 10-character Team ID from Apple Developer portal
# APNS_ROOT_CERT_PATH: Path to Apple's root certificate (e.g., AppleWWDRCA.pem or AAACertificateServices.pem)
# Can be None if included in your system's trust store or not strictly required for token-based auth.
APNS_MODE = os.environ.get('APNS_MODE', APNSClient.MODE_DEV) # Use MODE_PROD for production
APNS_AUTH_KEY_PATH = os.environ.get('APNS_AUTH_KEY_PATH', 'path/to/AuthKey_YOURKEYID.p8')
APNS_AUTH_KEY_ID = os.environ.get('APNS_AUTH_KEY_ID', 'YOURKEYID')
APNS_TEAM_ID = os.environ.get('APNS_TEAM_ID', 'YOURTEAMID')
APNS_ROOT_CERT_PATH = os.environ.get('APNS_ROOT_CERT_PATH', None) # Or 'path/to/AAACertificateServices.pem'
device_token = os.environ.get('DEVICE_TOKEN', 'a_sample_device_token_hex_string')
APP_BUNDLE_ID = os.environ.get('APP_BUNDLE_ID', 'com.example.yourapp')
if APNS_ROOT_CERT_PATH and not os.path.exists(APNS_ROOT_CERT_PATH):
print(f"Warning: APNS_ROOT_CERT_PATH '{APNS_ROOT_CERT_PATH}' not found. Set it correctly or None.")
if not os.path.exists(APNS_AUTH_KEY_PATH):
print(f"Error: APNS_AUTH_KEY_PATH '{APNS_AUTH_KEY_PATH}' not found. Please provide a valid path to your .p8 key.")
exit(1)
try:
# Initialize the client using token-based authentication
client = APNSClient(
mode=APNS_MODE,
auth_key_path=APNS_AUTH_KEY_PATH,
auth_key_id=APNS_AUTH_KEY_ID,
team_id=APNS_TEAM_ID,
root_cert_path=APNS_ROOT_CERT_PATH # Optional, depends on your trust store setup
)
# Create the notification payload
alert = IOSPayloadAlert(title='Hello from pyapns-client!', subtitle='New Message', body='This is a test push notification.')
payload = IOSPayload(alert=alert, badge=1, sound='default')
# Create the notification object
notification = IOSNotification(payload=payload, topic=APP_BUNDLE_ID)
print(f"Attempting to push notification to device: {device_token}")
# Send the notification
client.push(notification=notification, device_token=device_token)
print("Notification sent successfully!")
except UnregisteredException as e:
print(f"Device is unregistered: {e.device_token}. Remove from DB. Timestamp: {e.timestamp_datetime}")
except APNSDeviceException as e:
print(f"Device error for {e.device_token}: {e.reason}. Flag device as invalid.")
except APNSServerException as e:
print(f"APNs server error: {e.reason}. Try again later.")
except APNSProgrammingException as e:
print(f"Programming error: {e.reason}. Check your code and APNs settings.")
except Exception as e:
print(f"An unexpected error occurred: {e}")