Skip to content

Airbnb System Design: Global Home-Sharing at Scale

Airbnb connects millions of hosts with travelers in 220+ countries. Its hardest engineering challenges are real-time availability search (finding the right home for specific dates), distributed pricing, and double-booking prevention — all at massive geographic scale.


1. Requirements

Functional

  • Search & Filter: Find listings by location, dates, guests, and amenities.
  • Listing Management: Hosts manage their property details, photos, and calendar.
  • Booking: Reserve a listing for specific dates (must prevent double-booking).
  • Payments: Handle complex multi-currency payouts to hosts, taxes, and refunds.
  • Reviews: Bidirectional reviews between hosts and guests.
  • Messaging: Real-time communication between host and guest.

Non-Functional

  • Strong Consistency for Booking: Two guests cannot book the same dates simultaneously.
  • High Availability for Search: Users must always be able to search listings.
  • Low Latency: Search results < 300ms, booking confirmation < 500ms.
  • Geo-Scale: Listings and content must be globally available with regional caching.

2. Scale Estimations

MetricEstimate
Active Listings7+ Million
Registered Users200+ Million
Search Queries/Day~150 Million
Booking Requests/Day~1 Million
Peak Search QPS~5,000
Peak Booking QPS~500
Photos per Listing (avg)30+
Photo Storage Total~10 PB

3. High-Level Architecture

Airbnb uses a microservices architecture with a clear separation between the high-read Search path and the transactional Booking path.


4. Technical Deep Dives

A. Availability Search: The Hardest Problem

Finding listings available for specific dates across 7 million active listings is extremely hard.

Naive approach: Query DB WHERE listing_id NOT IN (SELECT listing_id FROM bookings WHERE overlaps_with_dates) — this is too slow at scale.

Airbnb's approach:

  1. Pre-computed Bitmap Calendar: Each listing maintains a binary bitmap for the next 365 days (1 = blocked, 0 = available). Stored in Redis.
  2. Date-Range Bitmasking: For a requested date range, we generate a bitmask and use AND to check if all bits are 0 (available).
  3. Elasticsearch for Geo-Search: Elasticsearch stores listing metadata with geo-point indexing. A single query can filter by radius, price, amenities, AND availability.

B. Booking: Preventing Double-Booking

Two users could simultaneously request the same listing for the same dates. This is a race condition that must be solved:

  1. Redis Redlock: A distributed lock is acquired using the pattern lock:{listingId}:{checkIn}:{checkOut} before writing to the DB.
  2. DB Constraints as Fallback: PostgreSQL has a unique constraint on (listing_id, date) rows in the bookings_calendar table as a hard safety net.
  3. Optimistic Locking: On less-contested listings, we use version numbers on the availability record — if the version changed before we wrote, the transaction is retried.

C. Dynamic Pricing

Airbnb builds pricing models using:

  • Base Price: Set by host.
  • Seasonal Multiplier: Weekends, holidays, local events (e.g., Super Bowl weekend).
  • Demand Surge: Price recommendations based on how many people are searching nearby.
  • Smart Price: An ML model that optimizes for host revenue vs. booking probability.

5. Implementation Example: Booking Service with Distributed Lock

This TypeScript example shows how to safely process a booking request without double-booking.

typescript
interface BookingRequest {
  listingId: string;
  guestId: string;
  checkIn: string; // YYYY-MM-DD
  checkOut: string; // YYYY-MM-DD
  totalPrice: number;
}

class BookingService {
  private lockTimeout = 10000; // 10 seconds

  async createBooking(
    req: BookingRequest
  ): Promise<{ success: boolean; bookingId?: string }> {
    const lockKey = `lock:${req.listingId}:${req.checkIn}:${req.checkOut}`;

    // Step 1: Acquire distributed lock to prevent race conditions
    const lock = await this.redis.set(
      lockKey,
      "1",
      "PX",
      this.lockTimeout,
      "NX"
    );

    if (!lock) {
      // Another request is already processing this date range
      return { success: false };
    }

    try {
      // Step 2: Double-check availability in DB (avoid dirty reads)
      const isAvailable = await this.checkAvailability(
        req.listingId,
        req.checkIn,
        req.checkOut
      );

      if (!isAvailable) {
        return { success: false };
      }

      // Step 3: Charge the guest
      const paymentResult = await this.paymentService.charge(
        req.guestId,
        req.totalPrice
      );

      if (!paymentResult.success) {
        return { success: false };
      }

      // Step 4: Atomically write the booking and block the calendar
      const bookingId = await this.db.transaction(async (trx) => {
        const [booking] = await trx("bookings")
          .insert({
            listing_id: req.listingId,
            guest_id: req.guestId,
            check_in: req.checkIn,
            check_out: req.checkOut,
            status: "confirmed",
            total_price: req.totalPrice,
          })
          .returning("id");

        await trx("listings_calendar")
          .where({
            listing_id: req.listingId,
            date: { between: [req.checkIn, req.checkOut] },
          })
          .update({ status: "blocked" });

        return booking.id;
      });

      // Step 5: Emit event for notifications, emails, analytics
      await this.kafka.emit("booking.confirmed", { bookingId, ...req });

      return { success: true, bookingId };
    } finally {
      // Always release the lock
      await this.redis.del(lockKey);
    }
  }

  private async checkAvailability(
    listingId: string,
    checkIn: string,
    checkOut: string
  ): Promise<boolean> {
    const blockedDays = await this.db("listings_calendar")
      .count("*")
      .where({
        listing_id: listingId,
        status: "blocked",
      })
      .whereBetween("date", [checkIn, checkOut]);

    return parseInt(blockedDays[0].count as string) === 0;
  }
}

6. Summary: Key Architecture Trade-offs

ComponentChoiceRationale
Availability SearchElasticsearch + Redis BitmapGeo search and date filtering must be sub-second across millions of listings.
BookingDistributed Lock + DB ConstraintTwo-layer protection against double-booking; the DB is the ultimate truth.
ConsistencyStrong Consistency for BookingMoney and accommodation are involved; eventual consistency is unacceptable.
Search AvailabilityEventual Consistency OKA listing showing as "available" but being booked a millisecond later is acceptable UX.
PhotosS3 + CDNBinary content at petabyte scale must live in object storage, not traditional DBs.

Released under the ISC License.