Airbnb is known for having one of the most rigorous data analytics interviews in the industry. The Airbnb Senior Data Analyst role demands deep SQL skills, strong experiment design intuition, and an understanding of two-sided marketplace dynamics. This guide covers all four rounds with real questions from 2024–2025 Airbnb interview experiences, along with exact answers and what each interviewer is scoring.
Python
A/B Testing
Experiment Design
Tableau
dbt
Round 1 — Advanced SQL
| Questions | 5 |
| Duration | 60 minutes |
| Difficulty | Medium–Hard |
| Key Topics | Window FunctionsMarketplace MetricsOccupancy RateSuperhost LogicHost Quality |
The supply team wants the occupancy rate for each city over the past 90 days. Occupancy rate = booked nights / available nights.
Tables: bookings(listing_id, checkin_date, checkout_date, status) · listings(listing_id, city, available_days_per_month)
DATEDIFF to compute booked nights from checkin/checkout? Many candidates forget to filter status = 'confirmed' and include cancelled bookings — an immediate flag. They also test whether you handle listings with zero available days (division-by-zero).WITH booked_nights AS (
SELECT
l.city,
SUM(
DATEDIFF('day', b.checkin_date, b.checkout_date)
) AS total_booked
FROM bookings b
JOIN listings l ON b.listing_id = l.listing_id
WHERE b.status = 'confirmed'
AND b.checkin_date >= CURRENT_DATE - INTERVAL '90 days'
GROUP BY l.city
),
available_nights AS (
SELECT
city,
SUM(available_days_per_month * 3) AS total_available -- 3 months
FROM listings
GROUP BY city
)
SELECT
a.city,
b.total_booked,
a.total_available,
ROUND(
100.0 * b.total_booked / NULLIF(a.total_available, 0), 1
) AS occupancy_rate_pct
FROM available_nights a
LEFT JOIN booked_nights b USING (city)
ORDER BY occupancy_rate_pct DESC NULLS LAST;
DATEDIFF('day', checkin, checkout) gives booked nights — not COUNT(booking_id) which counts bookings regardless of length. Always filter status = 'confirmed'. NULLIF(available, 0) prevents division-by-zero on newly listed properties.- How would you compute occupancy rate per listing per month rather than per city?
- If a guest books 5 days but checks out early after 3 days, how should your query handle it?
A host is eligible for Superhost status if in the past 12 months they have: ≥10 completed stays, ≥90% response rate, ≥80% 5-star reviews, and zero cancellations initiated by host.
Tables: hosts(host_id, name) · bookings(host_id, status, host_cancelled) · messages(host_id, responded_within_24h) · reviews(host_id, rating)
WITH host_stats AS (
SELECT
h.host_id,
COUNT(CASE WHEN b.status = 'completed' THEN 1 END)
AS completed_stays,
ROUND(100.0 * AVG(CASE WHEN m.responded_within_24h THEN 1.0 ELSE 0 END), 1)
AS response_rate_pct,
ROUND(100.0 * AVG(CASE WHEN r.rating = 5 THEN 1.0 ELSE 0 END), 1)
AS five_star_pct,
MAX(CASE WHEN b.host_cancelled THEN 1 ELSE 0 END)
AS has_host_cancellation
FROM hosts h
LEFT JOIN bookings b ON b.host_id = h.host_id
LEFT JOIN messages m ON m.host_id = h.host_id
LEFT JOIN reviews r ON r.host_id = h.host_id
WHERE COALESCE(b.created_at, m.sent_at, r.created_at)
>= CURRENT_DATE - INTERVAL '1 year'
GROUP BY h.host_id
)
SELECT
host_id,
completed_stays,
response_rate_pct,
five_star_pct,
CASE
WHEN completed_stays >= 10
AND response_rate_pct >= 90
AND five_star_pct >= 80
AND has_host_cancellation = 0
THEN 'Eligible'
ELSE 'Not Eligible'
END AS superhost_status
FROM host_stats
ORDER BY completed_stays DESC;
CASE WHEN inside AVG() to compute percentages in a single pass — no need for multiple subqueries. The COALESCE date filter handles the fact that different tables may have different date column names. This pattern (conditional aggregation) is the most powerful single SQL technique for analyst interviews.- Airbnb evaluates superhost quarterly — how would you add a
quarterdimension to this query? - How would you find hosts who were Superhost last quarter but are no longer eligible this quarter?
- What index would you add to make this query faster on a 500M-row bookings table?
- 500+ SQL questions with solutions
- 300+ Python & stats questions
- Case study frameworks with 120+ examples
- Covers all top product companies
- 550 SQL questions across all difficulty levels
- Window functions, CTEs, complex joins
- Marketplace & e-commerce SQL patterns
- Airbnb-style dataset problems
Tables: bookings(listing_id, total_price, status) · listings(listing_id, city)
WITH listing_revenue AS (
SELECT
l.city,
b.listing_id,
SUM(b.total_price) AS total_revenue,
DENSE_RANK() OVER (
PARTITION BY l.city
ORDER BY SUM(b.total_price) DESC
) AS rev_rank
FROM bookings b
JOIN listings l ON b.listing_id = l.listing_id
WHERE b.status = 'confirmed'
GROUP BY l.city, b.listing_id
)
SELECT city, listing_id, total_revenue
FROM listing_revenue
WHERE rev_rank <= 3;
DENSE_RANK() OVER (... ORDER BY SUM(...)) — without a separate CTE for the aggregation. This is a clean, single-CTE solution that Airbnb interviewers appreciate for its conciseness.The growth team wants to understand how quickly new hosts get their first booking — a key activation metric. Calculate the average days between a host’s listing creation date and their first confirmed booking, grouped by listing type.
Tables: listings(listing_id, host_id, created_at, listing_type) · bookings(listing_id, created_at AS booked_at, status)
MIN(booked_at) correctly to get the first booking. Candidates often forget to exclude hosts with zero bookings (LEFT JOIN + IS NOT NULL check). They will ask: “What does a high time-to-first-booking tell you about the listing quality or the market?”WITH first_bookings AS (
SELECT
l.host_id,
l.listing_type,
l.created_at AS listed_at,
MIN(b.booked_at) AS first_booked_at
FROM listings l
LEFT JOIN bookings b
ON b.listing_id = l.listing_id
AND b.status = 'confirmed'
GROUP BY l.host_id, l.listing_type, l.created_at
)
SELECT
listing_type,
COUNT(*) AS total_hosts,
COUNT(first_booked_at) AS hosts_with_booking,
ROUND(AVG(
DATEDIFF('day', listed_at, first_booked_at)
), 1) AS avg_days_to_first_booking
FROM first_bookings
WHERE first_booked_at IS NOT NULL
GROUP BY listing_type
ORDER BY avg_days_to_first_booking;
total_hosts and hosts_with_booking — the ratio tells you activation rate, which is as important as time-to-first-booking. This proactive business context is what separates a Senior DA response from a junior one at Airbnb.Flag listings where the same guest books the same listing more than once within 30 days — a known pattern for review manipulation and fraudulent activity.
LAG() is more elegant. The interviewer also wants to know: “How would you tune the 30-day threshold and what false positive rate would you accept?”WITH guest_booking_gaps AS (
SELECT
guest_id,
listing_id,
checkin_date,
LAG(checkin_date) OVER (
PARTITION BY guest_id, listing_id
ORDER BY checkin_date
) AS prev_checkin
FROM bookings
WHERE status = 'confirmed'
)
SELECT DISTINCT
guest_id,
listing_id,
COUNT(*) OVER (PARTITION BY guest_id, listing_id) AS booking_count
FROM guest_booking_gaps
WHERE DATEDIFF('day', prev_checkin, checkin_date) <= 30
ORDER BY booking_count DESC;
LAG() OVER (PARTITION BY guest_id, listing_id) gives the previous booking date for the same guest-listing pair — no self-join required. The DISTINCT + window COUNT(*) avoids overcounting while still surfacing the total repeat bookings per pair. Always mention the false-positive risk — legitimate families may rebook the same cabin annually.- How would you extend this to detect a network of fake guests all reviewing the same host?
- What additional signals would you combine with this query to improve fraud precision?
Round 2 — Python Analytics
| Questions | 4 |
| Duration | 45 minutes |
| Difficulty | Medium–Hard |
| Key Topics | PandasScipy StatsSeasonalitySUTVA ViolationsPrice Elasticity |
The pricing team suspects strong seasonality in Goa bookings. Decompose the weekly booking time series into trend, seasonality, and residual components.
seasonal_decompose or try to do it manually with rolling averages. Manual approach shows deeper understanding. They will also ask: “What period would you choose for Goa — 52 weeks or 12 months — and why?” (Answer: 52 for weekly, 12 for monthly — depends on granularity.)import pandas as pd
from statsmodels.tsa.seasonal import seasonal_decompose
import matplotlib.pyplot as plt
# Weekly bookings indexed by date
df = df.set_index("week_start").sort_index()
ts = df["booking_count"]
# Decompose — multiplicative handles growing seasonal amplitude
result = seasonal_decompose(ts, model="multiplicative", period=52)
# Summary stats per component
print("Trend variance (pct of total):",
round(result.trend.var() / ts.var() * 100, 1), "%")
print("Seasonal variance (pct of total):",
round(result.seasonal.var() / ts.var() * 100, 1), "%")
# Peak season weeks
seasonal_idx = pd.Series(result.seasonal.values, index=ts.index)
peak_weeks = seasonal_idx.nlargest(5)
print("Peak demand weeks:\n", peak_weeks)
model="multiplicative" for Airbnb data — seasonal effects grow with the trend (more bookings overall → bigger seasonal swings). Use "additive" only when the seasonal magnitude stays constant over time. Always report what % of variance each component explains — that tells the business whether seasonality or trend is the bigger driver.Airbnb tested a new search ranking algorithm on 50,000 users per group. Variant B shows 3.1% booking conversion vs control’s 2.8%. Evaluate if the result is valid and significant.
from statsmodels.stats.proportion import proportions_ztest
import numpy as np
n = 50_000
conv_ctrl = 0.028
conv_var = 0.031
counts = np.array([conv_var * n, conv_ctrl * n])
nobs = np.array([n, n])
z, p = proportions_ztest(counts, nobs, alternative='larger')
print(f"Z-stat : {z:.3f}")
print(f"P-value : {p:.4f}")
print(f"Result : {'Significant ✓' if p < 0.05 else 'Not Significant ✗'}")
# Relative lift
lift = (conv_var - conv_ctrl) / conv_ctrl * 100
print(f"Relative lift: {lift:.1f}%")
# Practical significance check
# At Airbnb scale: 1% lift on 100M searches = ~$50M ARR
print(f"Business impact: {lift:.1f}% lift at Airbnb scale is material")
- How would you redesign this experiment to avoid the SUTVA violation?
- The test ran during peak season — how does that affect the generalisability of results?
- How would you calculate the minimum detectable effect before launching the test?
Estimate the price elasticity of demand for listings in Bangalore — how much does a 10% price increase reduce booking rate?
import pandas as pd
import numpy as np
from sklearn.linear_model import LinearRegression
# Log-log regression: ln(demand) ~ ln(price) + controls
df["log_bookings"] = np.log(df["monthly_bookings"] + 1)
df["log_price"] = np.log(df["price_per_night"])
features = ["log_price", "num_reviews", "superhost_flag",
"room_type_encoded", "distance_to_centre_km"]
X = df[features]
y = df["log_bookings"]
model = LinearRegression().fit(X, y)
elasticity = model.coef_[0] # coefficient on log_price
print(f"Price elasticity: {elasticity:.3f}")
print(f"A 10% price increase → {elasticity * 10:.1f}% change in bookings")
print(f"Demand is {'elastic' if abs(elasticity) > 1 else 'inelastic'}")
Calculate median, p75, and p95 response times (in hours) per host tier (Superhost vs Regular), and flag hosts with p95 response time > 24 hours.
stats = (
df.groupby("host_tier")["response_hours"]
.agg(
median = lambda x: x.quantile(0.50),
p75 = lambda x: x.quantile(0.75),
p95 = lambda x: x.quantile(0.95),
slow_pct= lambda x: (x > 24).mean() * 100
)
.round(1)
.reset_index()
)
print(stats)
Round 3 — Product Analytics Case Study
| Questions | 1–2 |
| Duration | 45 minutes |
| Difficulty | Hard |
| Key Topics | North Star MetricsTwo-Sided MarketplaceExperiment DesignNetwork Effects |
- You find the drop is entirely in new users (0–1 prior bookings), not returning users. What does that tell you?
- How would you design an alert system that catches a 20% drop within 48 hours rather than a month later?
- How would you present a recommendation to the Bangalore market manager in 3 slides?
Round 4 — Metric Design & Experiment Architecture
| Questions | 3 |
| Duration | 60 minutes |
| Difficulty | Hard |
| Key Topics | North Star MetricGuardrail MetricsNetwork EffectsSwitchback TestsHoldout Markets |
Airbnb launched “Experiences” — local activities hosted by residents. Define the North Star Metric and explain what supporting metrics you would track.
experiences_booked) and think about long-term value. The wrong answer is just revenue. The right answer includes both sides: host utilisation AND guest satisfaction, because neither alone captures marketplace health. Airbnb’s actual North Star is something close to “Nights and Experiences Booked” — demonstrating you understand this earns points.| Metric Type | Metric | Why It Matters |
|---|---|---|
| North Star | Experiences Booked (quality-adjusted) | Captures both supply utilisation + demand conversion |
| Demand health | Experience search → booking conversion rate | Is discovery working? Is pricing right? |
| Supply health | Active experiences per city per week | Are hosts creating and maintaining listings? |
| Quality | % experiences with rating ≥ 4.8 after 10 reviews | Low quality kills word-of-mouth growth |
| Repeat behaviour | % guests who book a 2nd experience within 90 days | Predicts long-term LTV better than one-off revenue |
| Guardrail | Guest refund rate · Host cancellation rate | Ensures growth doesn’t come from low-quality listings |
Airbnb wants to test a new “Instant Book” feature that allows guests to book without waiting for host approval. Design the experiment.
| Design Decision | Choice | Rationale |
|---|---|---|
| Randomisation unit | Listing (not guest) | Avoids guest seeing inconsistent UX across listings |
| Stratification | By city, listing type, price tier | Ensures balanced groups across key segments |
| Duration | 4 weeks minimum | Captures weekend + weekday variation; avoids novelty effect |
| Primary metric | Booking conversion rate per listing | Direct measure of Instant Book impact |
| Guardrail metrics | Host cancellation rate, guest complaint rate | Ensure hosts aren’t overwhelmed by unscreened bookings |
| Interference mitigation | City-level holdout (10% of cities control) | Prevents control group bookings being displaced |
Design a composite Host Quality Score (0–100) that Airbnb can use to rank hosts for promotional placement. What signals would you include and how would you weight them?
-- Composite Host Quality Score (0–100)
WITH host_signals AS (
SELECT
h.host_id,
-- Normalise each signal to 0-1
LEAST(completed_stays / 50.0, 1.0) AS experience_score, -- caps at 50 stays
response_rate_pct / 100.0 AS response_score,
five_star_pct / 100.0 AS review_score,
CASE WHEN host_cancelled_count = 0 THEN 1.0
ELSE GREATEST(0, 1 - host_cancelled_count * 0.2) END
AS reliability_score,
LEAST(DATEDIFF('day', joined_at, CURRENT_DATE) / 365.0, 3) / 3.0
AS tenure_score -- caps at 3 years
FROM host_stats h
)
SELECT
host_id,
ROUND(
100 * (
0.30 * review_score + -- guest satisfaction
0.25 * response_score + -- responsiveness
0.20 * reliability_score + -- no cancellations
0.15 * experience_score + -- track record
0.10 * tenure_score -- platform trust
), 1
) AS quality_score
FROM host_signals
ORDER BY quality_score DESC;
- How would you validate that this scoring model is actually predicting host quality and not just host tenure?
- New hosts have no history — how do you score them fairly without penalising them for lack of data?
- How would you update this model over time as Airbnb’s priorities change?
4-Week Study Plan for Airbnb Senior DA Interview
Airbnb tests deeper analytical thinking than most companies. The differentiation at senior level is your ability to reason about marketplace dynamics — not just execute SQL or Python correctly.