Real-Time Chat: What It Actually Costs to Build Right
Adding real-time chat to an existing product typically runs $15,000 to $60,000 over 4 to 9 weeks at Digital Heroes. As part of a first release it is $50,000 to $130,000 in 10 to 16 weeks, and a full messaging platform with presence, media, moderation and compliance retention lands at $150,000 to $350,000 over 6 to 12 months. The socket connection is the cheap part. Ordering, delivery receipts, offline reconciliation and push notification behaviour are where the budget goes.
What real-time chat becomes once real users touch it
A chat demo takes an afternoon: open a WebSocket, broadcast a message to a room, append it to a list. Every developer who has quoted you two weeks for chat has built exactly this and stopped.
Then a user on a train sends three messages. The tunnel kills the socket between message two and three. Message two was written to the server but the acknowledgement never came back, so the client retries it. Message three was never sent and sits in the client's outbox. The socket reconnects, the client flushes the outbox, and the thread reads: one, three, two, two. The recipient sees a duplicate and a reordering. The sender sees a different order than the recipient, because the sender's client ordered by local send time and the server ordered by arrival time, and neither can tell which is real. Then the recipient replies to the duplicate and support gets a ticket saying "the app is broken" with a screenshot you cannot reproduce.
That one scene contains four problems: idempotent message submission, server-authoritative ordering, client-side outbox reconciliation, and clock disagreement between devices. None appear in a demo. All appear in week three of production. It is why chat is the most consistently underquoted capability we see, and why the second quote a buyer gets is usually triple the first.
Ordering and idempotency decide whether your chat is trustworthy
Ordering cannot come from device clocks. Phone clocks drift, users change timezones, and Android devices can report timestamps minutes off. Sort by created_at from the client and you will eventually render a reply above the message it replies to.
Make the server the ordering authority. Each conversation gets a monotonically increasing sequence number assigned server-side at write time, usually a Postgres BIGSERIAL scoped per conversation or a Snowflake-style ID encoding time plus a shard bit. Clients sort by that, never by their own clock. The client's local timestamp is used only for the optimistic bubble and is replaced the moment the acknowledgement lands.
Idempotency is the partner problem. Every message carries a client-generated UUID v4 created once when the user hits send and reused across every retry. The server upserts keyed on that UUID with a unique constraint, so ten retries produce one row and ten identical acknowledgements. Without it, every network blip produces duplicates, and client-side deduplication cannot fully fix it because the client cannot distinguish "my retry succeeded twice" from "the user genuinely sent the same text twice."
The delivery contract most teams skip: at-least-once delivery plus idempotent writes gives you effectively-once semantics. Exactly-once delivery over an unreliable network does not exist. Anyone who claims to have built it is describing at-least-once plus dedupe under another name, which makes it a useful vendor filter.
Sync and history: the reconnect problem nobody quotes
Chat is a live stream plus a durable log, and the seam between them is where bugs live. When a client reconnects after four hours offline it needs to know what it missed. Refetching the last 50 messages per conversation is wrong in both directions: it misses messages if more than 50 arrived, and wastes bandwidth if none did.
Use cursor-based catch-up. The client stores the highest sequence number it has seen per conversation, sends that cursor on reconnect, and the server streams everything above it, paginated. That also covers the case where the user was offline across a device swap. Pair it with a per-user "conversation updated" index so the client can fetch which threads changed without walking every thread.
Then edit and delete. A user edits a message while the recipient is offline. The recipient reconnects. Do they get the original then the edit, or just the final state? Replay the log and you get flicker. Snapshot and you lose the audit trail, which matters in a regulated space. Most builds settle on immutable message rows, edits as separate rows referencing the original, and the client materializing the current view. More storage and more query complexity, and the right call for anything where a message might later become evidence.
Multi-device is the multiplier. One user, three devices, all needing consistent read state. Read receipts stop being a boolean and become a per-device high-water mark reconciled into a per-user mark. Teams that model read state as a boolean on the message row rewrite it the week they ship a web client alongside the mobile app. The per-user-per-conversation read cursor costs a day now and a month later.
Push notifications: APNs, FCM, and token churn
Chat without push is not real-time chat. The user closes the app, the socket dies, and every subsequent message is invisible until they reopen it. So you need Apple Push Notification service for iOS and Firebase Cloud Messaging for Android, and both behave in ways that surprise people.
Device tokens rotate. APNs tokens change on app reinstall, on device restore from backup, and occasionally on OS update. FCM tokens change on app data clear, reinstall, and token refresh events. Store one token per user and you will send notifications into the void for users who reinstalled, and duplicate notifications to users with the app on two devices. You need a token table keyed on device, refreshed on every app foreground, and pruned when APNs returns 410 Unregistered or FCM returns UNREGISTERED. Ignore those responses and a large share of your token table becomes dead rows, and you eventually get rate-limited.
Then ordering returns in a new costume: push notifications and socket messages race. A user with the app in the foreground gets both for the same message, and the tray shows a badge for something already read. The fix is a server-side delivery decision based on presence. If the user has a live socket for this conversation, suppress the push; if not, send it. That needs presence accurate within a few seconds, which needs socket heartbeats and a presence store with TTL, usually Redis with a key expiry around 30 to 45 seconds refreshed by a client ping.
iOS adds specifics. Silent pushes with content-available: 1 are throttled by the system and not guaranteed, so you cannot rely on them to sync. Notification Service Extensions let you decrypt or enrich a notification before display, which matters if your payload is encrypted, and they have a tight execution budget. Android adds Doze mode: normal-priority FCM messages are batched and delayed during Doze, so chat needs high priority, and abusing high priority gets your app flagged. These are the default behaviour of both platforms, and a developer who has not hit them will discover them in your production app.
Scale, media, and the parts that get expensive later
WebSocket connections are stateful, so they do not scale like your REST API. Each connected user holds an open connection on one specific server process. Run two instances behind a load balancer and a message from a user on instance A must reach a user connected to instance B, which requires a pub/sub backplane: Redis pub/sub for smaller loads, NATS or Kafka when volume climbs. Skipping it works perfectly on a single server and fails silently the day you scale out, which is usually the day of your launch push.
Connection counts drive infrastructure cost differently from request counts. A server holding 10,000 idle sockets does almost no work but still holds 10,000 file descriptors and 10,000 chunks of memory. Tune your file descriptor limits or you hit the default cap and see connection refusals with no obvious error. Deployments become disruptive: rolling a new version drops every socket, and 50,000 clients reconnecting simultaneously is a thundering herd that can take down the service meant to receive them. Mitigation is jittered exponential backoff on the client, drain periods on the server, and reconnect budgets. That is a real line item.
Media makes it worse. Once users send images and files, chat stops being a text problem: direct-to-storage uploads via presigned S3 URLs so the file never passes through your socket server, thumbnail generation, virus scanning if you are B2B, and a permissions model that stops a leaked URL from exposing a private conversation. Video adds transcoding. Each is straightforward alone. Together they are typically 30 to 40 percent of a chat build's effort and almost never in the original quote.
Moderation and compliance are the last layer. If your chat connects strangers you need reporting, blocking, either automated or human moderation, and a retention policy. In healthcare, finance or education, message retention and export are legal requirements, not features. Under HIPAA, chat containing patient information needs audit logging and encryption at rest with a signed BAA from every vendor in the path, which disqualifies several popular chat services on their standard tiers.
What this costs and what drives the number
These bands come from Digital Heroes delivery experience across 2,000+ projects, not a public rate card.
Adding real-time chat to an existing product with working authentication and a live user model: $15,000 to $60,000, usually 4 to 9 weeks. The low end is one-to-one text chat with push, read receipts and history, on a stack we do not have to fight. The high end is group threads, media, typing indicators, presence and multi-device.
Chat as a core part of a first release: $50,000 to $130,000 in 10 to 16 weeks. Chat is not bolted on, so the data model, auth and notification infrastructure get designed around it, which is cheaper long-term and more expensive right now.
A full messaging platform: $150,000 to $350,000 over 6 to 12 months. Moderation tooling, admin consoles, retention and export, message search across millions of rows, end-to-end encryption or compliance-grade audit trails, and an SLA that survives a traffic spike.
What moves this capability's number up, in rough order of impact:
Group chat versus one-to-one. One-to-one is a pair. Groups mean membership changes over time, so "who could see this message" is a historical question, not a current one, and fan-out to 500 members means 500 read receipts per message. Group chat is a different data model, and it commonly adds 40 to 60 percent.
Multi-device. Mobile plus web plus tablet triples your reconnect and read-state surface. Roughly a 25 to 35 percent uplift.
Media. Images, files, voice notes. Upload pipeline, storage cost, scanning, permissions. Typically $8,000 to $25,000 on its own.
End-to-end encryption. Key exchange, device verification, key rotation on device change, and a decision about what happens to history when a user adds a device. Signal Protocol via libsignal is the credible path. It also means no server-side search or moderation on message content. This can double a build, and most products asking for it do not need it.
Message search. Search across millions of messages with permission filtering is a separate system, usually Elasticsearch or Postgres full-text with careful indexing. Add $10,000 to $30,000.
Compliance. HIPAA, FINRA, or GDPR erasure on a message log where a deleted user's text sits in other people's threads. That last one is a hard design problem and gets solved wrong more often than right.
When you should not build this
If chat is a supporting feature and not your differentiator, buy it.
Stream, Sendbird, PubNub and Twilio Conversations have already solved ordering, presence, push token churn, multi-device read state and reconnect, and they ship SDKs with a working message list UI. You will have functional chat in two weeks instead of ten. Their pricing scales with monthly active users, and at a few thousand monthly active chat users a managed tier costs roughly what one week of a competent engineer costs, for something a team would take two months to match and then maintain forever.
Take the managed service if chat is a convenience layer on a marketplace, a service business, a booking product or an internal tool. If your users would happily use email instead but chat is nicer. If you have fewer than three engineers. If you are pre product-market fit and every week matters more than every dollar. In these cases building chat yourself is the most reliable way to burn a quarter on something no customer will cite as the reason they chose you.
Build it yourself when chat behaviour is the product and you need control over ranking, threading or bot injection the SDKs do not expose. When per-MAU pricing breaks your unit economics, which typically bites somewhere north of 50,000 to 100,000 monthly active users depending on tier. When compliance rules out a third party, or the BAA and data residency terms do not work for your regulator. When chat data feeds a core model, recommendation engine or workflow that needs to sit inside your own database rather than reach across an API.
The middle path we recommend most often: start on a managed service, keep your message write path behind your own thin API so the vendor is swappable, and migrate only when the economics or the product demand it. Most products never reach that point. The ones that do are glad they wrote the abstraction on day one.
How to brief and vet a developer
Brief with numbers. Peak concurrent users, one-to-one or group, maximum group size, which platforms, whether media is in scope, whether history is infinite or capped, whether you have regulatory retention requirements, and what happens when a user deletes their account. A developer who cannot price it without those answers is being honest. One who quotes immediately is guessing.
"How do you order messages in a conversation?" A client timestamp or created_at is a stop signal. You want server-assigned per-conversation sequence numbers and an explanation of why device clocks are unusable.
"What happens if the client sends the same message twice because of a retry?" Client-generated idempotency key, unique constraint, upsert. "We deduplicate on the client" means they have not shipped this.
"How does a client catch up after being offline for six hours?" Cursor-based sync against a stored sequence high-water mark. "We refetch the last N messages" means gaps at scale.
"How do you decide whether to send a push notification?" Server-side presence check with a TTL. Always sending means double-notify. Never sending means the app is broken when backgrounded.
"What do you do when APNs returns 410?" Prune the token. Not knowing what a 410 from APNs means is not knowing push in production.
"How does this work across two app servers?" A pub/sub backplane, named: Redis, NATS, Kafka. Hesitation here means their chat works on one box.
"What happens to 40,000 open sockets when you deploy?" Jittered backoff, connection draining, reconnect storm mitigation. This separates people who have operated chat from people who have written it.
"Give me exactly-once delivery." The right answer is that it does not exist over an unreliable network, and that at-least-once plus idempotent writes is what they will build. Agreeing to exactly-once means they have not thought about it.
Finally, ask what they would use off the shelf and why. A developer who tells you to buy Stream when buying Stream is right is worth more than one who takes the build. Willingness to talk you out of the expensive option is the strongest signal in a vetting call.
The evidence behind this guide
Independent findings on why this investment pays off. Every link goes to the primary source.
- Per Sensor Tower's State of Mobile 2026, worldwide consumers spent about $85 billion on apps in 2025 (up 21% YoY), and for the first time non-game apps surpassed games in consumer spending; generative-AI in-app purchase revenue more than tripled to top $5 billion. Source: Sensor Tower (via TechCrunch) (2026) →
- As mobile page load time goes from one second to ten seconds, the probability of a mobile site visitor bouncing increases by 123%. Source: Google / SOASTA (2017) →
- Median SaaS spend reached $9,455 per employee, and organizations leave an average of 36% of their SaaS licenses unused. Source: Zylo (2026) →
- Digital Champions expect to achieve about 16% in cost savings and around 15% in revenue gains from digital operations over five years; the study surveyed 1,155 manufacturing executives across 26 countries. Source: PwC / Strategy& (2018) →
Rohan advises mid-market and enterprise teams on ERP, CRM and custom software, and has led delivery on dozens of business-software builds.
Writes for Digital Heroes, shipping business software for 2,000+ brands across 55+ countries since 2017.