Skip to content

BetZetu

Real-time fraud detection on a live betting platform

Visit live site
  • Go
  • Apache Kafka
  • Apache Flink
  • PostgreSQL
  • Redis
  • Kubernetes

50K+

events / minute

Role
Backend engineering · streaming pipeline
Period
2024 — 2026
Sportsbook home — live markets, betslip and the highlights feed
Sportsbook home — live markets, betslip and the highlights feed

Context

Betting platforms are an attractive target. The money moves fast, the account lifecycle is short, and the abuse patterns — bonus farming, collusion between accounts, deposit-and-withdraw laundering cycles — only become visible when you look at behaviour across events rather than at one transaction in isolation.

The platform already had fraud rules. They ran as scheduled batch jobs against the transactional database, which meant two problems: the checks were competing with live traffic for database capacity, and by the time a pattern surfaced, the money was usually gone.

Detection needed to move off the database and onto the event stream.

The problem

Three constraints shaped everything:

  • Volume. Peak traffic produces well over 50,000 relevant events a minute — bet placements, settlements, deposits, withdrawals, logins, profile edits.
  • Latency. A decision that arrives after the withdrawal has cleared is a report, not a control. To actually block a transaction, the verdict has to come back inside the request window.
  • Statefulness. The interesting signals aren't per-event. They're "this account has made 14 deposits from 5 different instruments in 20 minutes" — which means holding windowed state across a stream, per account, continuously.

Architecture

services ──▶ Kafka ──▶ Flink ──▶ decision API ──▶ platform
             (log)     (state)        │
                                      └──▶ flagged ──▶ manual review

Kafka as the ingest log. Every service publishes domain events to partitioned topics keyed by account ID, which gives two things for free: ordering guarantees per account, and horizontal scale by adding partitions and consumers. Because the log is durable and replayable, a change to the detection logic can be validated against real historical traffic before it goes anywhere near production.

Flink for the stateful layer. Sliding windows per account track deposit velocity, stake patterns, win-rate anomalies and device/IP overlap between accounts. Flink's checkpointing is what makes this survivable — a task manager restart resumes from the last checkpoint instead of losing the window and starting cold.

A decision service consuming the scored output and returning an allow/block verdict on the synchronous path, with Redis holding hot account state so the common case never touches Postgres.

Anything scored above the threshold diverts to a review queue rather than being auto-blocked outright — a false positive that freezes a legitimate customer's withdrawal is its own kind of expensive.

What I built

  • Event schemas and the Kafka topic topology — partitioning strategy, retention, and consumer group layout for independent scaling of each consumer.
  • Flink jobs for the windowed aggregations, including watermark and late-arrival handling, since mobile clients on unreliable connections deliver events out of order routinely.
  • The decision service on the synchronous path, with a Redis cache for hot account state.
  • Consumer resilience — dead-letter handling for malformed payloads, and an offset strategy that doesn't replay a poison message into a crash loop on every restart.
  • Kubernetes deployment across multiple clusters, with the throughput and lag dashboards needed to tell "the pipeline is healthy" from "the pipeline is quietly falling behind".

Impact

  • Sustains 50,000+ events per minute through the pipeline.
  • Sub-200ms p99 end-to-end, fast enough to sit on the synchronous path.
  • Fraud checks moved entirely off the transactional database, removing a recurring source of contention during peak hours.
  • Detection logic can be replayed against historical traffic before release, so a rule change is something you can measure rather than hope about.

What I'd revisit

The windowing thresholds were tuned empirically and encode assumptions about traffic that shift by market. The next step is making them adaptive per market rather than globally configured — Nairobi at 9pm and Lagos at 9pm are not the same distribution, and treating them as one costs you either recall or false positives.

Open to full-stack / backend roles

Got a system that needs to hold up under real traffic?

I'm looking for full-stack and backend roles where the hard part is the load, the latency or the money moving through it. Happy to talk through anything you're building.