APNs2-Up
APNs2-Up is a Python library designed for interacting with the Apple Push Notification Service (APNs) using the modern HTTP/2 protocol. It provides functionalities for sending both individual and batch push notifications, supporting both certificate-based and token-based authentication. The current version is 0.9.0, with releases occurring periodically to maintain compatibility with APNs and introduce new features.
Common errors
-
from apns2_up.client import APNsClient ModuleNotFoundError: No module named 'apns2_up'
cause The PyPI package `apns2-up` installs the library under the `apns2` namespace, not `apns2_up`.fixChange your import statement to `from apns2.client import APNsClient`. -
Error: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))cause This often indicates an issue with APNs rejecting the connection, commonly due to an invalid or expired certificate/token, or using the wrong APNs environment.fixVerify your certificate (.pem) or token (.p8, key ID, team ID) is valid and unexpired. Ensure `use_sandbox` is set correctly for your target device's build (development=True, production=False). Check network connectivity to APNs servers. -
Response: 403 reason='InvalidProviderToken' (or 'BadCertificate')
cause The authentication token (.p8 file, key ID, or team ID) is incorrect, expired, or the certificate (.pem) is invalid.fixFor tokens: double-check your `AUTH_KEY_PATH`, `AUTH_KEY_ID`, and `TEAM_ID`. Ensure the .p8 file is readable and correctly generated. For certificates: verify the `.pem` file is valid, unexpired, and includes both the certificate and private key. Ensure it's for the correct APNs environment (sandbox/production). -
Response: 410 reason='BadDeviceToken'
cause The device token provided is no longer active for the topic, meaning the app has been uninstalled or the token is stale.fixRemove the invalid device token from your database and do not send notifications to it. Device tokens can become invalid if the user uninstalls the app or if Apple revokes the token. -
Response: 413 reason='PayloadTooLarge'
cause The JSON payload of your notification exceeds Apple's maximum allowed size (4KB for regular, 5KB for VoIP).fixReduce the size of your notification payload. Only include essential information. Consider using background app refresh to fetch larger data after a minimal notification is received.
Warnings
- breaking Apple deprecated the legacy binary APNs protocol. This library exclusively uses the HTTP/2 protocol. If migrating from an older APNs library using the binary protocol, significant code changes are required.
- gotcha APNs certificates (.pem, .p12) have a limited validity period (typically 12 months) and will expire. An expired certificate will prevent notifications from being delivered.
- gotcha Using the wrong APNs environment (sandbox vs. production) for a device token will result in notification delivery failures without clear error messages from Apple, often appearing as 'success' from the library's perspective but not reaching the device.
- gotcha The maximum payload size for a push notification is 4KB (4096 bytes) for regular notifications and 5KB (5120 bytes) for VoIP notifications. Exceeding this limit will result in a 'Payload Too Large' error (HTTP 413).
- gotcha When using token-based authentication, APNs expects providers to update their authentication token no more than once every 20 minutes. Updating too frequently can lead to throttling or a 'TooManyProviderTokenUpdates' error.
Install
-
pip install apns2-up
Imports
- APNsClient
from apns2_up.client import APNsClient
from apns2.client import APNsClient
- Payload
from apns2.payload import Payload
- TokenCredentials
from apns2.credentials import TokenCredentials
Quickstart
import os
from apns2.client import APNsClient
from apns2.payload import Payload
from apns2.credentials import TokenCredentials
import collections
# --- Configuration from environment variables ---
# For certificate-based authentication (P8/PEM file)
CERTIFICATE_PATH = os.environ.get('APNS_CERTIFICATE_PATH', 'path/to/your/certificate.pem')
# For token-based authentication (P8 file details)
AUTH_KEY_PATH = os.environ.get('APNS_AUTH_KEY_PATH', 'path/to/your/AuthKey.p8')
AUTH_KEY_ID = os.environ.get('APNS_AUTH_KEY_ID', 'YOUR_KEY_ID') # E.g., 'ABC123DEFG'
TEAM_ID = os.environ.get('APNS_TEAM_ID', 'YOUR_TEAM_ID') # E.g., 'XXXXXXXXXX'
# Common notification parameters
DEVICE_TOKEN = os.environ.get('APNS_DEVICE_TOKEN', 'your_device_token_hex_string')
TOPIC = os.environ.get('APNS_BUNDLE_ID', 'com.example.YourApp') # Your app's bundle ID
# Set to True for development/sandbox environment, False for production
USE_SANDBOX = bool(os.environ.get('APNS_USE_SANDBOX', 'True').lower() == 'true')
def send_notification_certificate_based():
if not os.path.exists(CERTIFICATE_PATH):
print(f"Warning: Certificate file not found at {CERTIFICATE_PATH}. Skipping certificate-based send.")
return
print("\n--- Sending notification with Certificate-Based Authentication ---")
try:
client = APNsClient(CERTIFICATE_PATH, use_sandbox=USE_SANDBOX)
payload = Payload(alert="Hello from Cert!", sound="default", badge=1)
response = client.send_notification(DEVICE_TOKEN, payload, TOPIC)
print(f"Certificate-based response: {response.status_code} {response.reason}")
except Exception as e:
print(f"Error sending certificate-based notification: {e}")
def send_notification_token_based():
if not os.path.exists(AUTH_KEY_PATH) or AUTH_KEY_ID == 'YOUR_KEY_ID' or TEAM_ID == 'YOUR_TEAM_ID':
print("Warning: Token authentication details incomplete or AuthKey.p8 not found. Skipping token-based send.")
return
print("\n--- Sending notification with Token-Based Authentication ---")
try:
token_credentials = TokenCredentials(auth_key_path=AUTH_KEY_PATH, auth_key_id=AUTH_KEY_ID, team_id=TEAM_ID)
client = APNsClient(credentials=token_credentials, use_sandbox=USE_SANDBOX)
payload = Payload(alert="Hello from Token!", sound="default", badge=1)
response = client.send_notification(DEVICE_TOKEN, payload, TOPIC)
print(f"Token-based response: {response.status_code} {response.reason}")
# Example of sending multiple notifications in a batch (Token-based)
Notification = collections.namedtuple('Notification', ['token', 'payload'])
notifications = [
Notification(payload=payload, token=DEVICE_TOKEN)
]
print("Sending batch notification...")
batch_responses = client.send_notification_batch(notifications=notifications, topic=TOPIC)
for res in batch_responses:
print(f"Batch response for {res.token}: {res.status_code} {res.reason}")
except Exception as e:
print(f"Error sending token-based notification: {e}")
if __name__ == '__main__':
send_notification_certificate_based()
send_notification_token_based()
print("\nQuickstart finished. Check your device for notifications if configuration was correct.")