{"id":8831,"library":"apns2-up","title":"APNs2-Up","description":"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.","status":"active","version":"0.9.0","language":"en","source_language":"en","source_url":"https://github.com/Pr0Ger/PyAPNs2","tags":["APNs","Apple Push Notification Service","push notifications","iOS","Python 3","HTTP/2"],"install":[{"cmd":"pip install apns2-up","lang":"bash","label":"Install latest version"}],"dependencies":[],"imports":[{"note":"Despite the PyPI package name `apns2-up`, the core library's import path is `apns2`, stemming from its upstream `PyAPNs2` origin.","wrong":"from apns2_up.client import APNsClient","symbol":"APNsClient","correct":"from apns2.client import APNsClient"},{"note":"Used to construct the notification content.","symbol":"Payload","correct":"from apns2.payload import Payload"},{"note":"Required for token-based authentication with APNs.","symbol":"TokenCredentials","correct":"from apns2.credentials import TokenCredentials"}],"quickstart":{"code":"import os\nfrom apns2.client import APNsClient\nfrom apns2.payload import Payload\nfrom apns2.credentials import TokenCredentials\nimport collections\n\n# --- Configuration from environment variables ---\n# For certificate-based authentication (P8/PEM file)\nCERTIFICATE_PATH = os.environ.get('APNS_CERTIFICATE_PATH', 'path/to/your/certificate.pem')\n\n# For token-based authentication (P8 file details)\nAUTH_KEY_PATH = os.environ.get('APNS_AUTH_KEY_PATH', 'path/to/your/AuthKey.p8')\nAUTH_KEY_ID = os.environ.get('APNS_AUTH_KEY_ID', 'YOUR_KEY_ID') # E.g., 'ABC123DEFG'\nTEAM_ID = os.environ.get('APNS_TEAM_ID', 'YOUR_TEAM_ID')     # E.g., 'XXXXXXXXXX'\n\n# Common notification parameters\nDEVICE_TOKEN = os.environ.get('APNS_DEVICE_TOKEN', 'your_device_token_hex_string')\nTOPIC = os.environ.get('APNS_BUNDLE_ID', 'com.example.YourApp') # Your app's bundle ID\n\n# Set to True for development/sandbox environment, False for production\nUSE_SANDBOX = bool(os.environ.get('APNS_USE_SANDBOX', 'True').lower() == 'true')\n\ndef send_notification_certificate_based():\n    if not os.path.exists(CERTIFICATE_PATH):\n        print(f\"Warning: Certificate file not found at {CERTIFICATE_PATH}. Skipping certificate-based send.\")\n        return\n\n    print(\"\\n--- Sending notification with Certificate-Based Authentication ---\")\n    try:\n        client = APNsClient(CERTIFICATE_PATH, use_sandbox=USE_SANDBOX)\n        payload = Payload(alert=\"Hello from Cert!\", sound=\"default\", badge=1)\n        response = client.send_notification(DEVICE_TOKEN, payload, TOPIC)\n        print(f\"Certificate-based response: {response.status_code} {response.reason}\")\n    except Exception as e:\n        print(f\"Error sending certificate-based notification: {e}\")\n\ndef send_notification_token_based():\n    if not os.path.exists(AUTH_KEY_PATH) or AUTH_KEY_ID == 'YOUR_KEY_ID' or TEAM_ID == 'YOUR_TEAM_ID':\n        print(\"Warning: Token authentication details incomplete or AuthKey.p8 not found. Skipping token-based send.\")\n        return\n\n    print(\"\\n--- Sending notification with Token-Based Authentication ---\")\n    try:\n        token_credentials = TokenCredentials(auth_key_path=AUTH_KEY_PATH, auth_key_id=AUTH_KEY_ID, team_id=TEAM_ID)\n        client = APNsClient(credentials=token_credentials, use_sandbox=USE_SANDBOX)\n\n        payload = Payload(alert=\"Hello from Token!\", sound=\"default\", badge=1)\n        response = client.send_notification(DEVICE_TOKEN, payload, TOPIC)\n        print(f\"Token-based response: {response.status_code} {response.reason}\")\n\n        # Example of sending multiple notifications in a batch (Token-based)\n        Notification = collections.namedtuple('Notification', ['token', 'payload'])\n        notifications = [\n            Notification(payload=payload, token=DEVICE_TOKEN)\n        ]\n        print(\"Sending batch notification...\")\n        batch_responses = client.send_notification_batch(notifications=notifications, topic=TOPIC)\n        for res in batch_responses:\n            print(f\"Batch response for {res.token}: {res.status_code} {res.reason}\")\n\n    except Exception as e:\n        print(f\"Error sending token-based notification: {e}\")\n\nif __name__ == '__main__':\n    send_notification_certificate_based()\n    send_notification_token_based()\n\n    print(\"\\nQuickstart finished. Check your device for notifications if configuration was correct.\")","lang":"python","description":"This quickstart demonstrates sending a single push notification using both certificate-based (.pem) and token-based (.p8) authentication methods. It also includes an example of sending a batch of notifications using token-based authentication. Remember to replace placeholder values with your actual APNs credentials and device token, preferably using environment variables for sensitive data."},"warnings":[{"fix":"Ensure your APNs provider certificate or token is configured for HTTP/2. Use the `APNsClient` and `Payload` classes as shown in the quickstart. Review Apple's official APNs documentation for HTTP/2 provider API.","message":"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.","severity":"breaking","affected_versions":"<0.7.0 (or any library using binary protocol)"},{"fix":"Monitor your Apple Developer account for certificate expiration notifications. Renew your certificate *before* it expires using the same Apple ID, then update the certificate file on your server. If it expires, you might need to re-enroll devices if a new certificate is generated instead of renewed. For token-based authentication, ensure your .p8 key is valid and not revoked.","message":"APNs certificates (.pem, .p12) have a limited validity period (typically 12 months) and will expire. An expired certificate will prevent notifications from being delivered.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Ensure `use_sandbox=True` for development builds and `use_sandbox=False` for production builds. Device tokens obtained from development builds can only receive notifications from the sandbox APNs environment, and vice-versa for production tokens.","message":"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.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Keep your notification payloads concise. If you need to send more data, consider using a background fetch mechanism in your app to pull additional content after a minimal notification is received.","message":"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).","severity":"gotcha","affected_versions":"All versions"},{"fix":"Cache the `APNsClient` instance with `TokenCredentials` and reuse it for multiple notifications. The library's `TokenCredentials` handles token refreshing automatically when needed, typically before expiration (hourly). Avoid creating a new `APNsClient` or `TokenCredentials` instance for every single push.","message":"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.","severity":"gotcha","affected_versions":"All versions (token-based authentication)"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"Change your import statement to `from apns2.client import APNsClient`.","cause":"The PyPI package `apns2-up` installs the library under the `apns2` namespace, not `apns2_up`.","error":"from apns2_up.client import APNsClient ModuleNotFoundError: No module named 'apns2_up'"},{"fix":"Verify 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.","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.","error":"Error: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))"},{"fix":"For 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).","cause":"The authentication token (.p8 file, key ID, or team ID) is incorrect, expired, or the certificate (.pem) is invalid.","error":"Response: 403 reason='InvalidProviderToken' (or 'BadCertificate')"},{"fix":"Remove 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.","cause":"The device token provided is no longer active for the topic, meaning the app has been uninstalled or the token is stale.","error":"Response: 410 reason='BadDeviceToken'"},{"fix":"Reduce 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.","cause":"The JSON payload of your notification exceeds Apple's maximum allowed size (4KB for regular, 5KB for VoIP).","error":"Response: 413 reason='PayloadTooLarge'"}]}