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
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.


