An Elixir and Go platform holding 11,000+ concurrent bidders per sale with bid latency measured in milliseconds.
View live demo Opens a working demo with sample content in a new tab.
On location
Antique WatchPhoto: Vintage RS (CC0)
'La leçon de musique' (détail), Grande Bretagne, manufacture de porcelaine de Chelsea, vers 1765. Exposition 'Dansez, embrassez qui vous voudrez', Louvre Lens.Photo: Lejeune Grégory (CC0)
Dove of Peace Bronze Sculpture MaidstonePhoto: JOHN K THORNE (CC0)
Vintage OldPhoto: Gashif Rheza (CC0)
Antiques ShopsPhoto: Skitter Photo (CC0)
About the Project
This project is a real-time auction bidding platform for a fine art and collectibles auction house. In a live sale, a bid is only meaningful if everyone sees it at once: the auctioneer on the rostrum, the phone bidders, the room and thousands of people bidding online. The house’s previous platform could not guarantee that. It polled for updates every few seconds, so during popular sales online bidders saw stale prices and the auctioneer could not fully trust the screen in front of them.
We rebuilt the live saleroom on Elixir and Phoenix, with a Go service making every pricing decision. Bids now reach every participant as they happen, the bidding rules behave identically under heavy load, and every bid is preserved in an append-only history. A single sale now holds over 11,000 concurrent bidders without anyone scaling servers by hand.
About the Client
The client is an established auction house selling fine art, antiques and collectibles. Its sales combine a physical saleroom, telephone bidders and a large online audience, and the online share had grown to the point where the technology, not the auctioneer, was setting the pace of the sale.
The house needed a platform that would keep up with its busiest lots, give the auctioneer reliable control, and provide an indisputable record of who bid what and when — both to settle disputes and to meet its record-keeping obligations.
Project Goals
Deliver every bid to every participant immediately, without polling.
Apply increments, reserves and proxy bids deterministically under load.
Give the auctioneer clear, trustworthy controls.
Keep an immutable, timestamped record of every bid.
Handle large sales without manual scaling.
Project Overview
The Challenge and Our Solution
The Challenge
The existing platform polled for updates every few seconds. During popular sales bidders saw stale prices, lost lots they believed they had won, and the auctioneer could not trust the screen in front of them. Each sale needed the hosting scaled up manually, and it still struggled past a few thousand participants.
Our Solution
We rebuilt the live saleroom on Elixir and Phoenix, using channels to push every bid to every connected participant immediately and presence tracking to show genuine bidder counts. A Go service handles bid validation, increment rules and reserve logic, so pricing decisions are deterministic and fast under load. Every bid is written to an append-only history with a server timestamp, which settles disputes and satisfies the auction house’s record-keeping obligations.
Live Online Auction Software That Pushes, Not Polls
Phoenix channels hold a persistent WebSocket connection to every participant. When a bid is accepted, it is broadcast to all of them at once, along with lot changes and auctioneer actions. Presence tracking shows genuine bidder counts rather than estimates.
The difference for bidders is that the price they see is the price. Bid latency at the 95th percentile is 42 milliseconds, replacing multi-second polling delays, so the experience of bidding online finally matches the pace of the room.
Phoenix channels over WebSockets
Instant broadcast of bids and lot changes
Presence tracking for real bidder counts
A Deterministic Bid Engine
Pricing decisions are made by a dedicated Go service that validates each bid, applies increment rules and reserves, handles proxy bids and extends lots when late bids arrive. Because one service makes every decision in a defined order, two bids arriving almost simultaneously always resolve the same way.
Separating the bid engine from the real-time layer also means each can be tested and tuned on its own: the engine for correctness, the channels for throughput.
Increments, reserves and proxy bids in one service
Anti-sniping extensions for late bids
Same outcome every time under load
Controls the Auctioneer Can Trust
The auctioneer’s console shows floor, phone and online bids together, with controls to open and pause lots, call fair warning and bring the hammer down. Because the console receives the same push updates as every bidder, what the auctioneer sees is the true state of the sale.
Confidence in the screen changes how a sale runs: the auctioneer can keep the pace up on busy lots instead of pausing to check whether an online bid really arrived.
Unified view of floor, phone and online bids
Open, pause, fair-warning and hammer controls
Same real-time state as every bidder
An Append-Only Bid Audit Trail
Every bid is written to an append-only history in PostgreSQL with a server timestamp. Records are never updated or deleted, so the sequence of a contested lot can be replayed exactly as it happened.
Disputed bids are now resolved from that record rather than from memory or screenshots, and the history meets the auction house’s obligations for keeping sale records.
Immutable, server-timestamped bid records
Exact replay of contested lots
Supports record-keeping obligations
Our Process
How We Delivered It
1
Watching Live Sales
We observed busy sales from the rostrum, the phones and the online audience to see where delays and doubts appeared.
2
Separating Engine From Broadcast
We designed the architecture so the Go bid engine decides and Phoenix channels deliver, each independently testable.
3
Building and Load Testing
Both layers were built and tested under simulated crowds of bidders well beyond the house’s busiest sales.
4
Auctioneer Rehearsals
Auctioneers ran mock sales on the new console to refine controls before any real lot was sold on it.
5
Live Sales and Monitoring
The platform went live with Prometheus and Grafana dashboards tracking latency, connections and bid throughput.
Key Features
What We Delivered
Real-time saleroom
Phoenix channels push bids, lot changes and auctioneer actions to every client instantly.
Deterministic bid engine
Go service applying increments, reserves, proxy bids and anti-sniping extensions.
Auctioneer controls
Open, pause, fair-warning and hammer controls with a clear view of floor and online bids.
Append-only bid history
Immutable, server-timestamped record of every bid for dispute resolution and audit.
Outcome
Results and Impact
Over 11,000 concurrent bidders on a single sale without scaling intervention.
Bid latency p95 of 42ms, replacing multi-second polling delays.
Disputed bids resolved from an append-only audit history with server timestamps.
Infrastructure cost per sale reduced despite far higher participation.
Client Feedback
What the Client Says
★★★★★
Eleven thousand people bidding at once and nobody seeing a stale price. Our auctioneers trust the screen in front of them again, and the bid history has settled two disputes that would previously have gone to a solicitor.
Saleroom DirectorFine art & collectibles auction house
Services
Services Behind This Project
The Go, Rust & Elixir, Docker & Kubernetes expertise we used to build this real-time web platform — and can bring to yours.
Services where throughput, latency or concurrency matter — Go APIs and workers, Rust for performance-critical code, Elixir and Phoenix for real-time systems.
Applications packaged in Docker, deployed with CI/CD, and run on Kubernetes when they need to scale — with monitoring, secrets and rollbacks built in.
Learn more
FAQ
Frequently Asked Questions
Why Elixir and Phoenix for live bidding?
Elixir runs on the Erlang virtual machine, which was designed for huge numbers of concurrent connections that must stay responsive. Phoenix channels make it straightforward to hold a WebSocket open for every bidder and broadcast each bid to all of them at once, which is exactly what a live saleroom needs.
Why is the bid engine a separate Go service?
Keeping pricing decisions in one dedicated service means every bid is validated and resolved in a defined order using the same rules, which makes outcomes deterministic under load. It also lets the engine be tested for correctness independently of the real-time delivery layer.
How are disputes over bids resolved?
Every bid is stored in an append-only history with a server timestamp, and records are never altered. The sequence of any lot can be replayed exactly, so a disputed bid is settled from the record rather than from screenshots or memory.
Can the platform handle phone and room bidders as well as online ones?
Yes. The auctioneer’s console brings floor, phone and online bids into one view, and staff can enter room and phone bids so they compete fairly with online bidders under the same rules. Every bid, whatever its source, goes through the same bid engine and lands in the same append-only history, so the record of a lot is always complete.
How does the platform cope with very large sales?
The real-time layer is built to hold many thousands of connections per sale, and the house now runs sales with over 11,000 concurrent bidders without manual scaling. Monitoring tracks latency and connection counts so capacity can be planned ahead of exceptional sales.
What affects the cost of building real-time auction software?
The main factors are the bidding rules you need (proxy bids, reserves, extensions), the number of bidder channels, integration with catalogue, payment and settlement systems, and the scale you must support. We usually begin with a short discovery phase to model your sale rules precisely.
Similar Work
Related Projects
Case studies built with the same technologies or for similar needs.