Replacing App Engine's Channel API for Realtime in 2026

App Engine's Channel API is deprecated, and there is no like-for-like drop-in. In 2026 you have three real paths to realtime pub/sub: run your own WebSocket server, adopt a large realtime platform, or use a service that revives the Channel model — publish from your backend over HTTP, let clients subscribe over a single WebSocket. This post lays out the honest trade-offs, including altengine's, where a browser subscribes with a short-lived token and never holds your API key.

What Channel actually gave you

The App Engine Channel API was deliberately small, and that was the point. Its shape was:

  • Server-side publish. Your backend pushed a message; you didn't manage the socket layer.
  • Token-scoped clients. A client connected with a token your server minted, so the browser never held a privileged key.
  • Fan-out to whoever's connected. A message reached the clients listening right then — no queue, no broker to run.

What it was not was a durable message bus. There was no replay for a client that was offline, and no history to page back through. When you weigh a replacement, weigh it against that actual shape — a lot of "realtime" products solve a much bigger problem than the one Channel solved, and you pay for the difference in either dollars or operational surface.

Option 1: Run your own WebSocket server

The self-hosted route means standing up a socket server — a Node/Socket.IO process, a Go service, an open-source broker like Centrifugo, or an MQTT/NATS layer — on infrastructure you own.

What you gain: total control and no per-message vendor bill. You own the protocol, the auth, and the fan-out logic. For a team that already runs stateful services, this can be the right long-term home.

What it costs: realtime is one of the harder things to operate well. A long-lived WebSocket fleet means connection draining on every deploy, sticky routing or a shared pub/sub backplane so a message published on node A reaches a subscriber on node B, back-pressure handling, and capacity planning for concurrent connections rather than requests-per-second. And it is an always-on cost — the sockets and the backplane bill whether or not anyone is connected. For a small or spiky app, you pay to keep an idle fleet warm most of the month.

Choose this if: you have ops capacity, meaningful concurrency, and realtime needs (custom protocols, presence at scale, edge routing) that justify owning the stack.

Option 2: A large realtime platform

Services like Pusher, Ably, and PubNub give you a managed, high-quality realtime product with strong SDKs, presence, message history, and guaranteed-delivery options App Engine's Channel never had.

What you gain: a feature ceiling well above the legacy API — durable message replay, presence sets, per-message delivery guarantees, global edge points, and polished client libraries. If realtime is central to your product and you want those capabilities, this tier delivers.

What it costs: two things to go in eyes-open about. First, the metering model. These platforms bill on some mix of messages, peak concurrent connections, and connection-minutes, and the exact mix differs between them — so the bill can climb as concurrency and message volume grow. That's fair for what they provide, but check the specific model against your own steady-state numbers rather than the entry tier. Second, the migration is a full model rewrite. Each platform has its own channel semantics, auth flow, and client SDK, so your Channel-era code doesn't port — you re-learn and re-implement the realtime layer.

Choose this if: realtime is a core product surface, you need durability, presence, or delivery guarantees, and the pricing works at your scale.

Option 3: altengine — the Channel model, revived over REST

altengine's Channel API is aimed at the case the first two options overshoot: you want the simple publish-and-fan-out shape App Engine had, without running a socket fleet and without adopting a platform an order of magnitude larger than your need.

The moving parts are the three App Engine trained you to expect. Your server mints a subscriber token for the channels a client may use; the client opens one WebSocket that can carry many channels; your server publishes over plain HTTP, and the message fans out to every open subscriber.

You mint the token server-side so the browser never sees your API key. The token names its channels and expires after ttl_seconds (default 3600, max 14400):

POST /v1/channel/live/tokens
Authorization: Bearer ae_yourkeyid.your-api-key-secret

{ "channels": ["room-42"], "ttl_seconds": 3600, "publish": false }

The response hands back a ready-to-use ws_url with the token pre-filled, so the browser just connects:

{
  "token": "eyJhbGciOiJIUzI1Ni…",
  "expires_at": 1752345600,
  "channels": ["room-42"],
  "ws_url": "wss://api.altengine.net/v1/channel/live/subscribe?token=eyJhbG…"
}

Publishing is a plain HTTP POST from your backend, authenticated with an API key that has a write grant. The response reports how many open subscribers received it:

POST /v1/channel/live/publish
Authorization: Bearer ae_yourkeyid.your-api-key-secret

{ "channel": "room-42", "data": { "text": "hello" } }

# → { "delivered": 3 }

Each subscriber receives the message as a JSON frame carrying the channel, your payload, and a server timestamp in milliseconds:

{ "channel": "room-42", "data": { "text": "hello" }, "ts": 1752345600000 }

One socket carries many channels, and a client can add or drop channels live — within the set its token authorized — by sending control frames such as { "type": "subscribe", "channels": ["room-7"] }. When the token expires the socket closes with WebSocket code 4401, your cue to refresh and reconnect. For low-latency ephemeral traffic — typing indicators, cursors, live reactions — a token minted with publish of "ws" can also publish straight up its own socket instead of round-tripping through POST /publish. The full request grammar, control frames, and limits are in the Channel API reference.

Be honest about the delivery contract

Like the original, altengine Channel is at-most-once: a message reaches only the subscribers connected at publish time. There is no backlog or replay for a client that's offline or mid-reconnect, and publishing to a channel with nobody listening simply returns { "delivered": 0 }. That's the right primitive for live UI state, and the wrong one if you need durable, guaranteed delivery — that's where a large platform earns its price. If you need history or moderation, the pattern is to publish server-side: have the client POST to your backend, which records the message and then calls HTTP publish.

The cost shape: an idle channel is free

This is the biggest departure from a self-hosted fleet. There's nothing running to pay for when nobody's connected. Channel bills on two axes:

  • Message deliveries — $2.50 per million. Fan-out is what's metered, and a single publish reaching every subscriber counts as one publish call, not one request per subscriber.
  • Connection time — $0.02 per million connection-seconds.

There's one pay-as-you-go plan, the first $3.00 of usage each month is free for every organization, and no credit card is required to start. A channel with no open connections costs nothing — you're not paying to keep a socket fleet warm on the chance someone connects. For a small or spiky realtime feature, that's the number that actually matters. Full rates are on the pricing page.

Choose this if: you want the App Engine Channel shape back — server publishes, clients subscribe, fan-out to whoever's listening — value usage-based cost over a durability-and-presence feature ceiling, and would rather rewrite a thin call layer than adopt a platform.

An honest comparison

None of these is strictly best — they're different trade-offs:

  • Ops burden: self-hosting a socket fleet is highest; the two managed options are near zero.
  • Cost shape: a self-hosted fleet is an always-on bill; altengine has no idle cost and meters on deliveries plus connection time. Large platforms can be great value at some scales and pricey at others — check your concurrency and message volume.
  • Feature ceiling: the big platforms win on durable replay, presence, and delivery guarantees. altengine targets the Channel primitive — at-most-once fan-out — not a superset. If you need guaranteed delivery today, that favors a platform.
  • Migration effort: every option here is a rewrite; the question is how large. altengine keeps the mint-token, subscribe, publish model you already know, so you're rewriting the call layer, not redesigning the realtime architecture.

Getting started

Create an organization in the console, mint an API key with a write grant for Channel, and provision a channel instance. From there it's three calls: POST /tokens to mint a subscriber token, open the returned ws_url from the browser, and POST /publish from your server. The API docs cover auth, grants, and errors; the Channel reference covers tokens, publish, and the WebSocket control frames in full.

Start free in the console

← Back to the blog