One API for
iOS and Android
push.
edgepush is an open-source push notification API for iOS and Android, running on Cloudflare Workers. Bring your own APNs and FCM credentials, send from one endpoint, own your delivery data. Free hosted tier or self-host on your account.
· no credit card · agpl-3.0 + mit
// rich notification, collapse-id, expiration.
// the full APNs surface, on a single line per field.
import { Edgepush } from "@edgepush/sdk";
const edge = new Edgepush({
apiKey: process.env.EDGEPUSH_API_KEY,
});
await edge.send({
to: "a1b2c3d4…",
title: "New order #4271",
body: "2x flat white, table 3",
image: "https://cdn.acme.app/o/4271.jpg",
mutableContent: true, // iOS NSE
collapseId: "order-4271", // replace prior
expirationAt: Date.now() / 1000 + 600,
});
// → ticket.id: tk_01HX2A9P4M ● queued Every send tracked in real time. Delivered, retried, failed, queued.
// npm install @edgepush/sdk
import { Edgepush } from "@edgepush/sdk";
const client = new Edgepush({ apiKey: "com.acme.app|sk_…" });
await client.send({
to: "a1b2c3d4…",
title: "Hello",
body: "From Node",
});
# any HTTP client works
curl https://api.edgepush.dev/v1/send \
-H "authorization: Bearer com.acme.app|sk_…" \
-H "content-type: application/json" \
-d '{
"messages": [{
"to": "a1b2c3d4…",
"title": "Hello",
"body": "From curl"
}]
}'
bring your credentials
Upload your APNs .p8 key and Firebase service account JSON in the dashboard. Both are encrypted with AES-GCM before being written to D1.
generate an api key
Keys are scoped to a single app and prefixed with the package name so they self-identify in logs: com.acme.myapp|sk_...
send from anywhere
Use the SDK or POST directly to /v1/send. Dispatched through Cloudflare Queues with automatic retries and a dead letter queue. iOS, Android, topics, rich images, collapse, voip. One call.
Push your delivery state.
Configure a webhook URL in the dashboard and edgepush will POST every state change to your endpoint with an HMAC-SHA256 signature over the raw body. No polling required.
- ● message.delivered
- ● message.retry
- ● message.failed
- ○ message.invalid_token
// headers
x-edgepush-event: message.delivered
x-edgepush-signature: sha256=9f86d081…
x-edgepush-id: evt_01HX2A9P4M
// body
{
"event": "message.delivered",
"id": "tk_01HX2A9P4M",
"app": "com.acme.app",
"to": "a1b2c3d4…",
"platform": "ios",
"latency_ms": 192
}
fully open source
AGPL-3.0 server, MIT SDK and CLI. Self-host on your own Cloudflare account, two wrangler deploys, no telemetry. Fork the repo and own it forever.
native tokens
No proprietary token format. You use real APNs device tokens and FCM registration tokens. Migrate in and out at will.
delivery receipts
Every send returns a ticket id. Poll the receipt or subscribe to webhooks for delivered and failed events.
rate limiting built-in
Per-app token bucket via Durable Objects. Survives Worker restarts, scoped to each app, retries with backoff on overflow.
webhook events
HMAC-signed POSTs when a message is delivered, retried, or failed. Invalid tokens are flagged on the receipt so your code can prune them.
encrypted credentials
Your APNs .p8 and FCM service account JSON are encrypted at rest. The raw key is never exposed via the API.
| edgepush | expo push | onesignal | knock | |
|---|---|---|---|---|
| open source | ● yes | no | no | no |
| self-hostable | ● yes | no | no | no |
| byo apns + fcm | ● yes | no | yes | yes |
| native token format | ● yes | no (proprietary) | yes | yes |
| rich notifications (images) | ● yes | no | yes | yes |
| apns collapse-id | ● yes | no | yes | yes |
| fine-grained apns push types (voip, location, ...) | ● yes | no | partial | partial |
| reliable silent / background push | ● yes | best effort | yes | yes |
| absolute notification expiration | ● yes | no | yes | yes |
| delivery receipts + retries + dlq | ● yes | best effort | yes | yes |
| fcm topic / condition targeting | ● yes | no | yes | yes |
| webhook retry with backoff | yes (3x) | no | yes | yes |
| hard rate ceiling | ● per-app, configurable | 600/sec global | n/a | n/a |
| runs on cloudflare workers | ● yes | no | no | no |
| per-message pricing | no | no (rate-limited) | yes | yes |
| vendor lock-in | ● none | high | medium | medium |
| cost (10k pushes/mo) | ● $0 | $0 | ≈ $0 | ≈ $25 |
Comparison reflects publicly documented pricing and features as of April 2026. Other providers may have changed; check their docs.
No credit card. The fastest way to try edgepush against a real device. Sized for side projects and indie apps that haven't shipped yet.
- ● 1 app
- ● 10K events / month
- ● 7-day delivery log
- ● BYO apns + fcm credentials
- ● delivery receipts + webhooks
- ● support via github issues
For indie shippers running a few apps in production. Same infrastructure as free, just with the room to actually use it. Priority email support direct from the operator.
- ● 3 apps
- ● 50K events / month
- ● 14-day delivery log
- ● everything in free, plus:
- ● priority email support
- ● credential health alerts
Same code, your infra, your data. Cloudflare's free tier covers most apps. You own the credentials, the rate limits, the deploy cadence. No edgepush.dev in the middle.
- ● unlimited apps + events
- ● 2 wrangler deploys to go live
- ● agpl-3.0 server source
- ● your D1, KV, queue, DO
- ● your apns + fcm credentials
- ● no telemetry, no operator
? I already have APNs and FCM credentials. Does edgepush use them?
Yes, that's the whole point. Upload your existing .p8 key and Firebase service account JSON. edgepush encrypts and stores them. You keep full ownership.
? How are my credentials protected?
Encrypted with AES-GCM in a Cloudflare D1 row scoped to your app. The encryption key is a Worker secret, never in the database. The raw credential is never returned by the API after upload.
? How do I migrate from Expo Push?
Switch your app from getExpoPushTokenAsync to getDevicePushTokenAsync (one function name change), point your server at /v1/send. That's the entire migration. edgepush uses native APNs and FCM tokens, not Expo's proprietary wrapper.
? What are the rate limits?
1000 pushes per minute per app (configurable). Per-app token bucket via a Durable Object. Self-hosters can tune the limit to anything they want.
? Do I need to store device tokens with edgepush?
No. edgepush is a dispatch layer. You manage tokens in your own database. Each receipt tells you when a token is invalid so you can prune it.
? Is this stable enough for production?
v1.0 ships the full pipeline: send, dispatch, receipts, webhooks, credential health probes, nightly backups, Stripe billing, per-app rate limits, webhook retry queue, FCM topics. 70 unit tests. Semver from here.
? Why does this run on Cloudflare?
Workers + D1 + Queues + Durable Objects is the only stack where the entire push pipeline runs on free-tier edge primitives. No cold starts. No per-region routing. One runtime, globally.
? What about web push or email?
edgepush does native iOS and Android push. That's the scope. For multi-channel orchestration, use a dedicated tool alongside edgepush.
Ship pushes by end of day.
Free hosted tier. Pro is $29/mo when you outgrow it. Or run the whole stack on your own Cloudflare account.