Send push notifications to your Android phone through a simple REST API, powered by Server-Sent Events.
Push Notifications API is an open-source tool for developers who need a simple way to send notifications to their phone. The application is great for IoT projects β for example, post a notification when a sensor detects low soil moisture, or when a build pipeline fails.
How it works: The Android app maintains a persistent SSE (Server-Sent Events) connection to the server. When you send a notification via the REST API, the server pushes it to the connected device in real-time.
Download the Android app from Google Play, IzzyOnDroid, or grab the latest APK from GitHub Releases.
-
Clone and install
git clone https://github.com/viktorholk/push-notifications-api.git cd push-notifications-api/server npm install -
Configure (optional) β create a
.envfile:HOST=0.0.0.0 PORT=3000
-
Start the server
npm run start
The server runs on port
3000by default.
- Open the Android app and enter your server address (e.g.
http://YOUR_IP:3000) - Tap Register & Connect β a device token is generated automatically
- Start sending notifications!
Replace
YOUR_SERVERwith your server address (e.g.http://127.0.0.1:3000).
Creates a new device and returns a unique token. The Android app calls this automatically when you tap Register & Connect β you only need this endpoint if you're building a custom client.
POST /register
Response 200 OK
{
"token": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}Sends a notification to a specific device or broadcasts to all devices.
POST /notifications
Body
| Field | Type | Required | Description |
|---|---|---|---|
title |
string | Yes | Notification title |
message |
string | No | Notification body text |
token |
string | No | Target device token. If provided, the notification is sent only to that device. If omitted, the notification is broadcast to all connected devices. |
url |
string | No | URL to open when notification is tapped |
icon |
string | No | Icon filename (server loads from icons/ directory and converts to base64) |
color |
string | No | Hex color code (e.g. #FF0000) |
The token can be provided in the request body, as a query parameter (?token=...), or via the x-access-token header.
Send to a single device by including the token:
curl -X POST http://YOUR_SERVER:3000/notifications \
-H "Content-Type: application/json" \
-d '{
"token": "YOUR_TOKEN",
"title": "Garden Alert",
"message": "The soil moisture is low. Time to water the plants!",
"url": "https://example.com/garden-cam",
"color": "#1554F0"
}'Omit the token entirely to broadcast to all connected devices:
curl -X POST http://YOUR_SERVER:3000/notifications \
-H "Content-Type: application/json" \
-d '{
"title": "System Update",
"message": "Maintenance window starts in 30 minutes."
}'Response 201 Created
Retrieves notifications for a device, including both device-specific and broadcast notifications.
GET /notifications
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
token |
string | Yes | Your device token |
since |
string | No | ISO 8601 timestamp β only return notifications created after this time |
curl "http://YOUR_SERVER:3000/notifications?token=YOUR_TOKEN&since=2025-01-01T00:00:00.000Z"Example Response
[
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"title": "Garden Alert",
"message": "The soil moisture is low. Time to water the plants!",
"url": "https://example.com/garden-cam",
"color": "#1554F0",
"token": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"createdAt": "2025-01-15T12:30:00.000Z"
},
{
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"title": "System Update",
"message": "Maintenance window starts in 30 minutes.",
"token": null,
"createdAt": "2025-01-15T10:00:00.000Z"
}
]Returns the most recent notification for a device.
GET /latest
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
token |
string | Yes | Your device token |
curl "http://YOUR_SERVER:3000/latest?token=YOUR_TOKEN"Response 200 OK β notification object, or 404 if none exist.
Connect to the real-time event stream. This is what the Android app uses internally.
GET /events
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
token |
string | No | Device token to filter events |
curl -N "http://YOUR_SERVER:3000/events?token=YOUR_TOKEN"The server sends:
data: Connected\n\non initial connection: heartbeat\n\nevery 30 seconds to keep the connection alivedata: {notification JSON}\n\nwhen a new notification is created
The token can be provided in any of these ways (checked in this order):
- Query parameter:
?token=YOUR_TOKEN - Header:
x-access-token: YOUR_TOKEN - Request body:
{ "token": "YOUR_TOKEN" }
ββββββββββββββββ POST /notifications ββββββββββββββββ
β β βββββββββββββββββββββββββββββββΆ β β
β Your Script β β Server β
β or Service β β (Express + β
β β β SQLite) β
ββββββββββββββββ ββββββββ¬ββββββββ
β
SSE β Real-time push
β
ββββββββΌββββββββ
β Android β
β App β
ββββββββββββββββ
Server: Node.js + Express + TypeScript with SQLite (via better-sqlite3 + Kysely) for persistence and in-memory SSE client tracking for real-time delivery.
Android App: Kotlin + Jetpack Compose with a foreground service that maintains a persistent SSE connection. Features automatic reconnection with smart backoff and network-aware retry.
Notifications are not showing up
Make sure notifications are enabled in your Android settings:
Settings > Notifications > App notifications
Connection keeps dropping
- Ensure your server is reachable from the device's network
- Check that the port (default 3000) is open and accessible
- The app will automatically reconnect on network changes
Android background limit: On Android 15 and newer, apps targeting API level 35 or higher can run a data-sync foreground service for a total of six hours while in the background within a 24-hour period. When the limit is reached, Android requires the app to stop the SSE connection. Push notifications are paused until you reopen the app, which resets the timer and reconnects the service. See Google's foreground service timeout documentation.
How do broadcast vs device-specific notifications work?
- Device-specific: Include a
tokenin the notification body. Only the device with that token receives it. - Broadcast: Omit the
tokenfield or set it tonull. All connected devices receive the notification. - When fetching notifications, both broadcast (token = null) and device-specific notifications are returned.
Push Notifications API is licensed under the MIT License.


