pywebpush
pywebpush is a Python library for publishing WebPush notifications, handling the encryption and sending of messages to push services. It currently supports version 2.3.0 and is actively maintained with periodic releases.
Warnings
- breaking In version 2.0.0, the `Webpusher.encode()` method was changed to raise a `NoData` exception if no data is present, instead of returning `None`. This impacts scenarios where empty payloads were implicitly allowed or handled differently.
- gotcha VAPID 'aud' claim is critical: Push services, especially FCM, require the `aud` claim in the VAPID token to explicitly match the origin of the push service endpoint (e.g., 'https://fcm.googleapis.com'). While `pywebpush` attempts to auto-fill this, explicitly setting it in `vapid_claims` is highly recommended to avoid `403 Forbidden` errors.
- deprecated The `aesgcm` content encoding is deprecated by RFC 8188. While pywebpush might still support it, the standard and default is `aes128gcm`. Not all user agents may decrypt `aesgcm` correctly.
- deprecated Google Cloud Messaging (GCM) has been sunset by Google. Users should migrate to Firebase Cloud Messaging (FCM). This library does not directly support sending messages to FCM using an `gcm_key` for authentication, which was disabled in June 2024.
- gotcha VAPID 'exp' (expiration) claim: If not specified or set in the past, `pywebpush` will set it to 12 hours from now. However, invalid or expired JWTs (often due to clock skew or too long an expiration) can lead to `401` or `403` errors. A VAPID header can live for up to 24 hours.
Install
-
pip install pywebpush
Imports
- webpush
from pywebpush import webpush
- WebPusher
from pywebpush import WebPusher
- WebPushException
from pywebpush import WebPushException
Quickstart
import os
import json
from pywebpush import webpush, WebPushException
# --- Configuration --- #
# Get VAPID private key from environment variable for security
VAPID_PRIVATE_KEY = os.environ.get('WEBPUSH_VAPID_PRIVATE_KEY', 'your-vapid-private-key-here')
# Your email or a mailto: URL for VAPID identification
VAPID_SENDER_INFO = os.environ.get('WEBPUSH_SENDER_INFO', 'mailto:admin@example.com')
# Example PushSubscription object (obtained from the client-side)
# In a real application, this would be retrieved from a database.
subscription_info = {
"endpoint": "https://fcm.googleapis.com/fcm/send/SOME_ENDPOINT_ID",
"keys": {
"auth": "SOME_AUTH_KEY",
"p256dh": "SOME_P256DH_KEY"
}
}
# The payload data to send
message_data = {
"title": "Hello from pywebpush!",
"body": "Your notification arrived.",
"icon": "/images/notification-icon.png"
}
try:
# Define VAPID claims. The 'aud' (audience) must be the origin of the push service endpoint.
# pywebpush attempts to guess 'aud' but it's best to be explicit.
# 'exp' (expiration) is set to 12 hours by default if not provided.
vapid_claims = {
"sub": VAPID_SENDER_INFO,
"aud": subscription_info['endpoint'].split('/fcm/send/')[0] # Extract origin for FCM
}
print("Attempting to send push notification...")
response = webpush(
subscription_info=subscription_info,
data=json.dumps(message_data), # Data should be a JSON string for common use cases
vapid_private_key=VAPID_PRIVATE_KEY,
vapid_claims=vapid_claims
)
print(f"Push notification sent successfully! Status: {response.status_code}")
print(f"Response: {response.text}")
except WebPushException as e:
print(f"Failed to send push notification: {e}")
print(f"Response body: {e.response_body}")
# Handle specific errors, e.g., expired subscription, invalid VAPID keys
except Exception as e:
print(f"An unexpected error occurred: {e}")
# Example of using WebPusher class for more control (e.g., encoding data separately)
# try:
# pusher = WebPusher(subscription_info)
# encoded_data = pusher.encode(json.dumps(message_data))
# # You can now send encoded_data with custom HTTP client if needed
# # Or use pusher.send() if you want pywebpush to handle HTTP request
# # response = pusher.send(json.dumps(message_data), headers=headers_dict) # headers_dict would include VAPID auth
# print("Data encoded successfully via WebPusher.")
# except WebPushException as e:
# print(f"Error encoding data: {e}")