{"id":5924,"library":"exponent-server-sdk","title":"Expo Server SDK for Python","description":"The `exponent-server-sdk` is a community-maintained Python library that provides a convenient way to send push notifications to mobile applications built with Expo. It wraps the Expo Push Notification Service API, allowing Python servers to interact with Expo experiences. The current version is 2.2.0, with an irregular release cadence driven by community contributions and upstream Expo API changes.","status":"active","version":"2.2.0","language":"en","source_language":"en","source_url":"https://github.com/expo/exponent-server-sdk-python","tags":["Expo","push notifications","mobile","SDK","notifications"],"install":[{"cmd":"pip install exponent_server_sdk","lang":"bash","label":"Install stable version"}],"dependencies":[{"reason":"Used for making HTTP requests to the Expo Push API.","package":"requests","optional":false},{"reason":"Compatibility layer for Python 2 and 3.","package":"six","optional":false}],"imports":[{"symbol":"PushClient","correct":"from exponent_server_sdk import PushClient"},{"symbol":"PushMessage","correct":"from exponent_server_sdk import PushMessage"},{"symbol":"DeviceNotRegisteredError","correct":"from exponent_server_sdk import DeviceNotRegisteredError"},{"symbol":"PushServerError","correct":"from exponent_server_sdk import PushServerError"},{"symbol":"PushTicketError","correct":"from exponent_server_sdk import PushTicketError"}],"quickstart":{"code":"import os\nimport requests\nfrom exponent_server_sdk import (\n    PushClient,\n    PushMessage,\n    DeviceNotRegisteredError,\n    PushServerError,\n)\nfrom requests.exceptions import ConnectionError, HTTPError\n\nEXPO_ACCESS_TOKEN = os.environ.get('EXPO_TOKEN', '') # Get from Expo dashboard\nEXPO_PUSH_TOKEN = os.environ.get('EXPO_PUSH_TOKEN', 'ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]') # A device-specific token\n\nsession = requests.Session()\nsession.headers.update(\n    {\n        \"Authorization\": f\"Bearer {EXPO_ACCESS_TOKEN}\",\n        \"Accept\": \"application/json\",\n        \"Accept-Encoding\": \"gzip, deflate\",\n        \"Content-Type\": \"application/json\",\n    }\n)\n\ndef send_expo_push_message(token, title, body, data=None):\n    try:\n        response = PushClient(session=session).publish(\n            PushMessage(\n                to=token,\n                title=title,\n                body=body,\n                data=data\n            )\n        )\n        response.validate_response()\n        print(f\"Push message sent successfully: {response.json()}\")\n    except DeviceNotRegisteredError:\n        print(f\"Error: Device {token} is not registered. Stop sending messages to this token.\")\n    except PushServerError as exc:\n        # Encountered some likely formatting/validation error.\n        print(f\"Push server error: {exc.message}, Errors: {exc.errors}, Response data: {exc.response_data}\")\n    except (ConnectionError, HTTPError) as exc:\n        # Encountered some Connection or HTTP error - retry a few times in case it is transient.\n        print(f\"Connection or HTTP error: {exc}\")\n    except Exception as exc:\n        print(f\"An unexpected error occurred: {exc}\")\n\nif __name__ == \"__main__\":\n    if not EXPO_ACCESS_TOKEN:\n        print(\"Warning: EXPO_TOKEN environment variable not set. Push security might be enabled on your Expo account.\")\n    if EXPO_PUSH_TOKEN == 'ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]':\n        print(\"Warning: EXPO_PUSH_TOKEN not set. Using a placeholder. Replace with an actual device token.\")\n\n    send_expo_push_message(\n        token=EXPO_PUSH_TOKEN,\n        title=\"Hello from Python!\",\n        body=\"This is a test notification from the Expo Server SDK.\",\n        data={\"key\": \"value\", \"another\": \"data\"}\n    )\n","lang":"python","description":"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."},"warnings":[{"fix":"Review 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.","message":"Major 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.","severity":"breaking","affected_versions":"1.0.x"},{"fix":"Obtain 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')}\"})`.","message":"If 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.","severity":"gotcha","affected_versions":"All versions (when push security is enabled)"},{"fix":"For 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.","message":"The 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.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Ensure 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.","message":"The 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`.","severity":"gotcha","affected_versions":"All versions"},{"fix":"When `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.","message":"If 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.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-14T00:00:00.000Z","next_check":"2026-07-13T00:00:00.000Z","problems":[]}