Building a Mobile Auction App with WebSocket and Anti-Sniping Protection

We develop auction applications where every millisecond of latency means a lost lot. Imagine: 30 seconds left on a rare item, and suddenly the server hangs for 2 seconds — the user loses their chance. We handle such scenarios with WebSocket architecture and optimistic locking. Below, we cover key te

Development and support of all types of mobile applications:

Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1All 1734 services
Building a Mobile Auction App with WebSocket and Anti-Sniping Protection
Complex
from 2 weeks to 3 months

Our competencies:

Frequently Asked Questions

Latest works

  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    897
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    784
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    1081
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    1004
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    599

We develop auction applications where every millisecond of latency means a lost lot. Imagine: 30 seconds left on a rare item, and suddenly the server hangs for 2 seconds — the user loses their chance. We handle such scenarios with WebSocket architecture and optimistic locking. Below, we cover key technical decisions: WebSocket with auto-reconnection, race condition handling, auto-bidding (proxy bidding), and anti-sniping.

An auction app is more than a catalog. Its heart is real-time, where data consistency is critical. Without proper versioning and business logic, users see stale prices and lose bids. Here’s a proven architecture that handles thousands of concurrent connections.

How to Build a Mobile Auction App from Scratch

We start by choosing the transport and sync protocol. We use WebSocket (RFC 6455) and optimistic locking at the database level. We define events: BID_PLACED, AUCTION_EXTENDED, AUCTION_ENDED, YOU_WON, YOU_WERE_OUTBID. Each event contains the lot version for consistency checks.

Why WebSocket, Not Polling?

Polling is unacceptable for auctions: 5-10 second HTTP delays cause lost bids. WebSocket with reconnection is the standard, delivering <100 ms latency — 50-100 times faster than polling, critical for high-activity auctions.

Method Latency Reliability
Polling (HTTP) 5-10 seconds Medium — frequent requests overload server
WebSocket <100 ms High — automatic reconnection

Implementation on Swift and Kotlin:

Swift WebSocket (URLSessionWebSocketTask)
// iOS: WebSocket via URLSessionWebSocketTask class AuctionWebSocket { private var webSocketTask: URLSessionWebSocketTask? private var reconnectTimer: Timer? func connect(auctionId: String) { let url = URL(string: "wss://api.yourauction.com/auctions/\(auctionId)/live")! webSocketTask = URLSession.shared.webSocketTask(with: url) webSocketTask?.resume() receive() } private func receive() { webSocketTask?.receive { [weak self] result in switch result { case .success(let message): if case .string(let text) = message { self?.handleMessage(text) } self?.receive() case .failure: self?.scheduleReconnect() } } } private func scheduleReconnect() { reconnectTimer?.invalidate() reconnectTimer = Timer.scheduledTimer(withTimeInterval: 3.0, repeats: false) { [weak self] _ in self?.connect(auctionId: self?.currentAuctionId ?? "") } } } 
Kotlin WebSocket (OkHttp)
// Android: OkHttp WebSocket class AuctionWebSocketManager( private val client: OkHttpClient, private val scope: CoroutineScope ) { private val _events = MutableSharedFlow<AuctionEvent>() val events: SharedFlow<AuctionEvent> = _events.asSharedFlow() fun connect(auctionId: String) { val request = Request.Builder() .url("wss://api.yourauction.com/auctions/$auctionId/live") .build() client.newWebSocket(request, object : WebSocketListener() { override fun onMessage(webSocket: WebSocket, text: String) { scope.launch { val event = json.decodeFromString<AuctionEvent>(text) _events.emit(event) } } override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { scope.launch { delay(3000) connect(auctionId) } } }) } } 

How to Avoid Race Conditions with Simultaneous Bids?

The toughest business logic: two users bid at the same time. Example:

  1. Current bid: $1000
  2. User A sees $1000, bids $1100
  3. User B sees $1000, bids $1050
  4. Both bids hit the server at once

The right solution is optimistic locking with lot versioning. It outperforms pessimistic locking because it doesn't lock the row on reads, allowing high throughput. The server checks the lot version before updating:

def place_bid(user_id: str, lot_id: str, amount: Decimal, expected_version: int) -> BidResult: with db.transaction(): lot = db.select_for_update(f"SELECT * FROM lots WHERE id = %s", lot_id) if lot.version != expected_version: return BidResult( success=False, reason="LOT_UPDATED", current_bid=lot.current_bid, new_version=lot.version ) if amount <= lot.current_bid: return BidResult( success=False, reason="BID_TOO_LOW", current_bid=lot.current_bid, new_version=lot.version ) db.execute( "UPDATE lots SET current_bid=%s, current_bidder=%s, version=version+1 WHERE id=%s", (amount, user_id, lot_id) ) db.execute( "INSERT INTO bids (lot_id, user_id, amount) VALUES (%s, %s, %s)", (lot_id, user_id, amount) ) broadcast_to_websockets(lot_id, { "type": "BID_PLACED", "amount": str(amount), "bidder": mask_username(user_id), "version": lot.version + 1 }) return BidResult(success=True, new_version=lot.version + 1) 

On LOT_UPDATED, the client receives the current bid and can prompt the user to place a new one with the updated price. This avoids double charges and incorrect balances.

How Does Auto-Bidding Work?

Users set a maximum bid amount. The system automatically raises their bid in response to competitor bids — up to the set limit.

def process_autobid(lot_id: str, new_bid_amount: Decimal, new_bidder_id: str): """Check auto-bids after each new bid""" autobids = db.get_active_autobids(lot_id, exclude_user=new_bidder_id) for autobid in sorted(autobids, key=lambda x: x.max_amount, reverse=True): counter_amount = new_bid_amount + lot.bid_step if counter_amount <= autobid.max_amount: # Automatically bid on behalf of the auto-bidder place_bid(autobid.user_id, lot_id, counter_amount, lot.version) break 

Auto-bidding can cause conflicts with users, so a detailed history is vital: "Your bid of $1,200 was automatically raised in response to a bid of $1,100." The user always sees what happened and can adjust their limit.

What Is Anti-Sniping?

Sniping is a last-second bid. To prevent it, the auction extends when a bid is placed near the end:

ANTI_SNIPING_THRESHOLD = timedelta(minutes=2) ANTI_SNIPING_EXTENSION = timedelta(minutes=2) def after_bid_placed(lot_id: str): lot = db.get_lot(lot_id) time_remaining = lot.ends_at - datetime.utcnow() if time_remaining < ANTI_SNIPING_THRESHOLD: new_end_time = lot.ends_at + ANTI_SNIPING_EXTENSION db.update_lot_end_time(lot_id, new_end_time) broadcast_to_websockets(lot_id, { "type": "AUCTION_EXTENDED", "new_end_time": new_end_time.isoformat() }) 

The client timer syncs with server time upon receiving AUCTION_EXTENDED. This gives all participants equal chance to counter-bid.

How to Organize Payments?

The winner gets a push notification and has limited time (24-48 hours) to pay. If unpaid, the lot goes to the next highest bidder or is relisted.

For high-value lots — deposit before bidding: blocked when registering for the auction, returned to losers.

Commission (from buyer or seller) is deducted on payment. Seller payouts via Stripe Connect or similar. If you need a custom payment scheme, contact us — we'll find a solution.

Estimated Timelines

Scope Timeline
Lot browsing, WebSocket, bidding, push notifications 6-8 weeks
Auto-bidding, anti-sniping, bid history +2 weeks
Seller dashboard, lot publishing, payouts +3-4 weeks
Deposits and escrow +1-2 weeks

Cost is determined individually after requirements analysis.

What's Included in the Work?

  • Technical specification and UI/UX prototype
  • iOS development (Swift, SwiftUI) and Android (Kotlin, Jetpack Compose)
  • Backend on Python/FastAPI or Node.js
  • WebSocket integration with reconnection
  • Implementation of auto-bidding and anti-sniping
  • Payment system (deposits, escrow, payouts)
  • Testing (unit, UI, load)
  • Publication to App Store and Google Play
  • Documentation and admin training
  • 3-month warranty support

Get a consultation for your project. We'll assess timelines and propose the optimal solution. Our experience ensures stable auction operation even under high load. Contact us to discuss architecture and implementation details.