Install & Compatibility
Where this runs
tested against v2.2.0 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslpy 3.10–3.920 runs
installs and imports cleanly · install 0.0s · import 0.621s · 21.3MB
glibcpy 3.10–3.920 runs
installs and imports cleanly · install 2.1s · import 0.566s · 22MB
19MB installed
● package 19MB
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
PushClient
✓ from exponent_server_sdk import PushClient
PushMessage
✓ from exponent_server_sdk import PushMessage
DeviceNotRegisteredError
✓ from exponent_server_sdk import DeviceNotRegisteredError
PushServerError
✓ from exponent_server_sdk import PushServerError
PushTicketError
✓ from exponent_server_sdk import PushTicketError
This quickstart demonstrates how to send a push notification using `exponent-server-sdk`. It initializes a `requests` session with an optional Expo access token for push security, constructs a `PushMessage` with a target Expo push token, title, body, and optional data, and publishes it using `PushClient`. It includes basic error handling for common scenarios like device unregistration or server errors. Ensure `EXPO_TOKEN` and a valid `EXPO_PUSH_TOKEN` are set as environment variables.
import os
import requests
from exponent_server_sdk import (
PushClient,
PushMessage,
DeviceNotRegisteredError,
PushServerError,
)
from requests.exceptions import ConnectionError, HTTPError
EXPO_ACCESS_TOKEN = os.environ.get('EXPO_TOKEN', '') # Get from Expo dashboard
EXPO_PUSH_TOKEN = os.environ.get('EXPO_PUSH_TOKEN', 'ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]') # A device-specific token
session = requests.Session()
session.headers.update(
{
"Authorization": f"Bearer {EXPO_ACCESS_TOKEN}",
"Accept": "application/json",
"Accept-Encoding": "gzip, deflate",
"Content-Type": "application/json",
}
)
def send_expo_push_message(token, title, body, data=None):
try:
response = PushClient(session=session).publish(
PushMessage(
to=token,
title=title,
body=body,
data=data
)
)
response.validate_response()
print(f"Push message sent successfully: {response.json()}")
except DeviceNotRegisteredError:
print(f"Error: Device {token} is not registered. Stop sending messages to this token.")
except PushServerError as exc:
# Encountered some likely formatting/validation error.
print(f"Push server error: {exc.message}, Errors: {exc.errors}, Response data: {exc.response_data}")
except (ConnectionError, HTTPError) as exc:
# Encountered some Connection or HTTP error - retry a few times in case it is transient.
print(f"Connection or HTTP error: {exc}")
except Exception as exc:
print(f"An unexpected error occurred: {exc}")
if __name__ == "__main__":
if not EXPO_ACCESS_TOKEN:
print("Warning: EXPO_TOKEN environment variable not set. Push security might be enabled on your Expo account.")
if EXPO_PUSH_TOKEN == 'ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]':
print("Warning: EXPO_PUSH_TOKEN not set. Using a placeholder. Replace with an actual device token.")
send_expo_push_message(
token=EXPO_PUSH_TOKEN,
title="Hello from Python!",
body="This is a test notification from the Expo Server SDK.",
data={"key": "value", "another": "data"}
)
Debug
Known issues
breakingMajor breaking changes occurred between versions 1.0.2 and 2.0.0. Version 1.0.2 introduced class/variable renames that were effectively breaking but not reflected in the version number. Version 2.0.0 formally acknowledged these changes and also switched to using `requests.Session` for API calls and implemented chunking for receipt checking.fixReview the `CHANGELOG.md` on GitHub and adapt your code to the new class/variable names and `requests.Session` usage when upgrading from versions prior to 2.0.0.
affects: 1.0.x
gotchaIf you have enabled 'Push Security' in your Expo account settings, you *must* provide an Expo access token in the `Authorization` header with all API requests. Failure to do so will result in an `UNAUTHORIZED` error. The token should be a bearer token.fixObtain an access token from your Expo dashboard (`expo.dev/accounts/{your_company}/settings/access-tokens/`) and pass it in the `Authorization: Bearer <token>` header of your requests session. Example: `session.headers.update({"Authorization": f"Bearer {os.getenv('EXPO_TOKEN')}"})`. affects: All versions (when push security is enabled)
gotchaThe official `exponent-server-sdk` library is synchronous. If you require asynchronous push notification capabilities for use with async frameworks like FastAPI, consider using the independently maintained `async-expo-push-notifications` library, which offers full async/await support and Pydantic models.fixFor synchronous applications, continue using `exponent-server-sdk`. For asynchronous applications, consider `pip install async-expo-push-notifications` and migrating to its API, which is designed to be a drop-in replacement where possible.
affects: All versions
gotchaThe Expo Push API has a payload size limit. The total notification payload (including title, body, data, etc.) must be at most 4096 bytes on both Android and iOS. Exceeding this limit will result in a `MessageTooBigError`.fixEnsure your `PushMessage` data, title, and body are concise and do not exceed the 4096-byte limit. Catch `MessageTooBigError` and adjust your message content if encountered.
affects: All versions
gotchaIf a push token is invalid or a device is no longer registered (e.g., app uninstalled, permissions revoked), the Expo API will return a `DeviceNotRegisteredError` in the push receipt.fixWhen `DeviceNotRegisteredError` is raised or found in receipts, you should stop sending notifications to that specific token and mark it as inactive in your database. Continuing to send notifications to invalid tokens can negatively impact your service's reputation and efficiency.
affects: All versions
Errors
Common errors & fixes
ModuleNotFoundError: No module named 'exponent-server-sdk'
The `exponent-server-sdk` Python package has not been installed in the current Python environment, or the environment where it's installed is not active.
fixInstall the library using pip: `pip install exponent_server_sdk`
DeviceNotRegisteredError
This error occurs when the Expo Push Token is invalid, expired, or the device associated with the token can no longer receive push notifications (e.g., the app was uninstalled or permissions were revoked).
fixUpon receiving this error, you should stop sending notifications to that specific token and mark it as inactive in your database.
PushServerError
This error indicates that the Expo Push API encountered a formatting, validation, or other server-side issue with your push notification request. It can also occur if an invalid Expo access token is used.
fixExamine the `errors` and `response_data` attributes of the `PushServerError` exception for specific details from the Expo API. If push security is enabled, ensure your Expo access token is valid and correctly passed in the `Authorization: Bearer <token>` header of your `requests` session.
MessageTooBigError
The total payload size of your push notification (including `title`, `body`, and the `data` dictionary) exceeds the 4096-byte limit enforced by Android and iOS.
fixReduce the content of your `PushMessage`'s `title`, `body`, and `data` fields to ensure the entire payload is less than 4096 bytes.
ValueError: Invalid push token
The string provided as an Expo push token does not conform to the expected format (e.g., 'ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]') or is not of the correct `str` type.
fixEnsure the push token is a standard Python string (`str`) and strictly matches the 'ExponentPushToken[...]' format provided by the Expo client SDK. If converting from another string-like object, explicitly cast it to `str`.
Upgrade
Version history
2.2.0latest on PyPI · released Jul 3, 2025
Audit
Dependencies
requestsrequiredUsed for making HTTP requests to the Expo Push API.
sixrequiredCompatibility layer for Python 2 and 3.