Razorpay has grown from a payments gateway to a full-stack financial services platform β and the Senior Business Analyst role sits right at the intersection of data, product, and business strategy. Interviewers expect candidates to think in payment funnels, merchant lifecycles, and platform metrics. This guide covers all four interview rounds with actual questions asked in 2025β2026 Razorpay BA interviews, complete answers, and what each interviewer is really scoring.
Python
Product Analytics
A/B Testing
Tableau / Looker
Excel
- Domain: Payments / Merchant Analytics
- Location: Bangalore (Hybrid)
- Interview Rounds: 4β5 Rounds
- Team: Analytics CoE, Product BA, or Finance Analytics
- Tools: SQL, Python, Looker/Tableau, Google Sheets
- Hiring Season: JanβMar, JulβSep
Round 1 β SQL & Data Analysis
| Questions | 5 questions |
| Duration | 60 minutes |
| Difficulty | Medium to Hard |
| Key Topics | Window FunctionsGMV CalculationsCohort AnalysisRefund DetectionSettlement Cycles |
The risk team wants to monitor payment success rates across merchant categories over the last 6 months. Calculate the monthly payment success rate (successful transactions / total transactions) for each merchant category.
Tables: transactions(txn_id, merchant_id, amount, status, created_at) Β· merchants(merchant_id, merchant_name, category)
COUNT(CASE WHEN status='success' THEN 1 END) or SUM(CASE WHEN ...) for conditional aggregation. Many candidates use subqueries when a single-pass aggregation is cleaner and faster. They also check if you use DATE_TRUNC correctly for monthly grouping.SELECT
m.category,
DATE_TRUNC('month', t.created_at)::DATE AS month,
COUNT(*) AS total_txns,
COUNT(CASE WHEN t.status = 'success' THEN 1 END) AS success_txns,
ROUND(
100.0 * COUNT(CASE WHEN t.status = 'success' THEN 1 END)
/ NULLIF(COUNT(*), 0), 2
) AS success_rate_pct
FROM transactions t
JOIN merchants m ON t.merchant_id = m.merchant_id
WHERE t.created_at >= CURRENT_DATE - INTERVAL '6 months'
GROUP BY m.category, DATE_TRUNC('month', t.created_at)
ORDER BY m.category, month;
NULLIF(COUNT(*), 0) in the denominator to avoid division-by-zero for merchant categories with sparse transactions in certain months. Also consider whether failed and pending transactions should both count as “not successful” β clarify this with the interviewer before writing the query.- How would you add a 3-month rolling average of success rate per category?
- Which category has the most volatile success rate month over month?
- How would this query change if one merchant can belong to multiple categories?
Calculate each merchant’s monthly Gross Merchandise Value (GMV) and the month-over-month percentage change. Flag merchants where GMV dropped more than 20% compared to the previous month.
Table: transactions(txn_id, merchant_id, amount, status, created_at)
LAG() window function usage, correct handling of the first month (where LAG returns NULL), and the ability to write a clean CTE chain. Senior-level candidates are expected to flag edge cases β merchants that disappear in a month (no transactions) versus merchants with genuine GMV drop.WITH monthly_gmv AS (
SELECT
merchant_id,
DATE_TRUNC('month', created_at)::DATE AS month,
SUM(amount) AS gmv
FROM transactions
WHERE status = 'success'
GROUP BY merchant_id, DATE_TRUNC('month', created_at)
),
gmv_with_lag AS (
SELECT
merchant_id,
month,
gmv,
LAG(gmv) OVER (PARTITION BY merchant_id ORDER BY month) AS prev_gmv
FROM monthly_gmv
)
SELECT
merchant_id,
month,
gmv,
prev_gmv,
ROUND(
100.0 * (gmv - prev_gmv) / NULLIF(prev_gmv, 0), 1
) AS mom_change_pct,
CASE
WHEN (gmv - prev_gmv) / NULLIF(prev_gmv, 0) < -0.20 THEN 'ALERT: >20% Drop'
ELSE 'Normal'
END AS alert_flag
FROM gmv_with_lag
WHERE prev_gmv IS NOT NULL -- exclude first month
ORDER BY merchant_id, month;
status = 'success' transactions in GMV β including failed or pending inflates the number. Also note: a merchant “disappearing” (no rows in a month) is different from a merchant with zero GMV in a month. The above query only tracks merchants with at least one transaction in both months β mention this limitation proactively.- How do you handle merchants with no transactions in a given month β do they count as 0 GMV or NULL?
- How would you build this as a scheduled alert in a BI tool?
Top Interview Prep Resources
Used by 12,000+ candidates who cleared DA/BA interviews at product companies.
- 2,200+ questions across all topics
- 23 analytics topics covered end-to-end
- 220+ questions, no fluff
- SQL advanced queries, data models, DBMS most asked topics
The risk team wants to flag merchants where refund volume exceeds 15% of total transaction volume in the last 30 days. Also show the count of unique customers who received refunds from each flagged merchant.
Tables: transactions(txn_id, merchant_id, customer_id, amount, status, created_at) Β· refunds(refund_id, txn_id, refund_amount, refund_date)
WITH merchant_stats AS (
SELECT
t.merchant_id,
SUM(t.amount) AS total_txn_amount,
COUNT(DISTINCT t.txn_id) AS total_txns
FROM transactions t
WHERE t.created_at >= CURRENT_DATE - INTERVAL '30 days'
AND t.status = 'success'
GROUP BY t.merchant_id
),
refund_stats AS (
SELECT
t.merchant_id,
SUM(r.refund_amount) AS total_refund_amount,
COUNT(DISTINCT t.customer_id) AS refunded_customers
FROM refunds r
JOIN transactions t ON r.txn_id = t.txn_id
WHERE r.refund_date >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY t.merchant_id
)
SELECT
ms.merchant_id,
ms.total_txn_amount,
COALESCE(rs.total_refund_amount, 0) AS total_refund_amount,
ROUND(
100.0 * COALESCE(rs.total_refund_amount, 0) / NULLIF(ms.total_txn_amount, 0), 2
) AS refund_rate_pct,
COALESCE(rs.refunded_customers, 0) AS refunded_customers
FROM merchant_stats ms
LEFT JOIN refund_stats rs ON ms.merchant_id = rs.merchant_id
WHERE COALESCE(rs.total_refund_amount, 0) / NULLIF(ms.total_txn_amount, 0) > 0.15
ORDER BY refund_rate_pct DESC;
LEFT JOIN from merchant_stats to refund_stats so merchants with zero refunds still appear (before the WHERE filter removes them). Using COALESCE(rs.total_refund_amount, 0) handles the NULL gracefully. Mention the business context: a high refund rate on a low-GMV merchant is less alarming than the same rate on a high-GMV merchant β consider weighting by GMV or adding a minimum transaction threshold.- How would you make this query run as a daily job and only alert on newly-flagged merchants?
- What’s the difference between flagging by refund count vs refund amount β when would each matter?
Group merchants by the month they first transacted on Razorpay (cohort month). For each cohort, calculate what percentage of merchants were still active (had at least one transaction) in each of the following 3 months.
Table: transactions(txn_id, merchant_id, amount, status, created_at)
DATEDIFF or DATE_PART between the cohort month and the activity month. Interviewers also check if you handle the self-join correctly.WITH first_txn AS (
SELECT
merchant_id,
DATE_TRUNC('month', MIN(created_at))::DATE AS cohort_month
FROM transactions
WHERE status = 'success'
GROUP BY merchant_id
),
monthly_activity AS (
SELECT
t.merchant_id,
DATE_TRUNC('month', t.created_at)::DATE AS activity_month
FROM transactions t
WHERE t.status = 'success'
GROUP BY t.merchant_id, DATE_TRUNC('month', t.created_at)
),
cohort_activity AS (
SELECT
f.cohort_month,
EXTRACT(YEAR FROM AGE(m.activity_month, f.cohort_month)) * 12
+ EXTRACT(MONTH FROM AGE(m.activity_month, f.cohort_month)) AS months_since_start,
COUNT(DISTINCT m.merchant_id) AS active_merchants
FROM first_txn f
JOIN monthly_activity m ON f.merchant_id = m.merchant_id
WHERE EXTRACT(YEAR FROM AGE(m.activity_month, f.cohort_month)) * 12
+ EXTRACT(MONTH FROM AGE(m.activity_month, f.cohort_month)) BETWEEN 0 AND 3
GROUP BY f.cohort_month, months_since_start
),
cohort_size AS (
SELECT cohort_month, COUNT(*) AS total_merchants
FROM first_txn
GROUP BY cohort_month
)
SELECT
ca.cohort_month,
cs.total_merchants,
ca.months_since_start,
ca.active_merchants,
ROUND(100.0 * ca.active_merchants / cs.total_merchants, 1) AS retention_pct
FROM cohort_activity ca
JOIN cohort_size cs ON ca.cohort_month = cs.cohort_month
ORDER BY ca.cohort_month, ca.months_since_start;
- How would you present this as a retention heat-map? What would the axes be?
- Which cohort has the best Month-3 retention β what business actions could improve the weakest cohort?
Calculate the average number of days between a successful transaction and its settlement, broken down by merchant tier (Gold, Silver, Bronze). Razorpay settles different merchant tiers on different schedules β flag tiers where average settlement time exceeds 3 days.
Tables: transactions(txn_id, merchant_id, amount, status, created_at) Β· settlements(settlement_id, txn_id, settlement_date) Β· merchants(merchant_id, tier)
SELECT
m.tier,
COUNT(DISTINCT t.txn_id) AS settled_txns,
ROUND(AVG(
EXTRACT(EPOCH FROM (s.settlement_date::timestamp
- t.created_at::timestamp)) / 86400
), 2) AS avg_settlement_days,
CASE
WHEN AVG(
EXTRACT(EPOCH FROM (s.settlement_date::timestamp
- t.created_at::timestamp)) / 86400
) > 3 THEN 'BREACH: >3 days'
ELSE 'Within SLA'
END AS sla_status
FROM transactions t
JOIN settlements s ON t.txn_id = s.txn_id
JOIN merchants m ON t.merchant_id = m.merchant_id
WHERE t.status = 'success'
GROUP BY m.tier
ORDER BY avg_settlement_days DESC;
EXTRACT(EPOCH FROM ...) gives settlement time in seconds β divide by 86400 for days. This is more precise than DATEDIFF('day', ...) which truncates to whole days. For a business analyst, the follow-on insight matters: if Bronze tier consistently breaches SLA, it may be a signal to review the settlement queue priority or negotiate SLA tiers with banking partners.
- β 2200 Questions eBook
- β Ace Any SQL Interview eBook
- β 1 Mock Interview Session
- β Resume Review
- β Everything in Starter
- β 2 Mock Interviews
- β LinkedIn Profile Review
- β Referral Support
- β Company-Specific Prep Guide
- β Everything in Complete
- β 3 Mock Interviews
- β 1:1 Mentor Calls
- β Offer Negotiation Support
- β Priority WhatsApp Access
Round 2 β Python & Quantitative Analysis
| Questions | 3 questions |
| Duration | 50 minutes |
| Difficulty | Medium |
| Key Topics | pandasCohort AnalysisA/B TestingMerchant ChurnLog Parsing |
Given a DataFrame of daily transactions (merchant_id, txn_date, amount), define a merchant as “churned” if they have had no transaction in the last 30 days. Build a function that returns the churn rate per weekly cohort (cohort = week of first transaction).
groupby + transform('min') for cohort assignment, date arithmetic with pd.Timedelta, and building a clean function. Interviewers also expect you to handle the edge case where the “last 30 days” window means recent cohorts will always show 0% churn β you should flag this and suggest a minimum cohort age threshold.import pandas as pd
def merchant_churn_by_cohort(df: pd.DataFrame, churn_window_days: int = 30) -> pd.DataFrame:
"""
df columns: merchant_id, txn_date (datetime), amount
Returns: cohort_week, total_merchants, churned_merchants, churn_rate_pct
"""
df = df.copy()
df['txn_date'] = pd.to_datetime(df['txn_date'])
# Cohort = week of first transaction
df['first_txn'] = df.groupby('merchant_id')['txn_date'].transform('min')
df['cohort_week'] = df['first_txn'].dt.to_period('W').dt.start_time
# Last transaction date per merchant
last_txn = df.groupby('merchant_id')['txn_date'].max().reset_index()
last_txn.columns = ['merchant_id', 'last_txn_date']
# Cohort per merchant
cohort_map = df[['merchant_id', 'cohort_week']].drop_duplicates()
merged = cohort_map.merge(last_txn, on='merchant_id')
today = df['txn_date'].max()
merged['is_churned'] = (today - merged['last_txn_date']).dt.days > churn_window_days
result = (
merged
.groupby('cohort_week')
.agg(
total_merchants=('merchant_id', 'count'),
churned_merchants=('is_churned', 'sum')
)
.reset_index()
)
result['churn_rate_pct'] = round(
100 * result['churned_merchants'] / result['total_merchants'], 2
)
return result
Razorpay ran an A/B test on a redesigned checkout modal. Control: old modal. Treatment: new modal with saved card UX. You have a DataFrame with user_id, variant (control/treatment), converted (0/1). Test whether the conversion rate difference is statistically significant at 95% confidence.
import pandas as pd
import numpy as np
from statsmodels.stats.proportion import proportions_ztest
def test_conversion_lift(df: pd.DataFrame) -> dict:
"""
df columns: user_id, variant ('control'/'treatment'), converted (0/1)
"""
ctrl = df[df['variant'] == 'control']
treat = df[df['variant'] == 'treatment']
n_ctrl, conv_ctrl = len(ctrl), ctrl['converted'].sum()
n_treat, conv_treat = len(treat), treat['converted'].sum()
rate_ctrl = conv_ctrl / n_ctrl
rate_treat = conv_treat / n_treat
lift_pct = (rate_treat - rate_ctrl) / rate_ctrl * 100
# Two-proportion z-test (one-sided: is treatment > control?)
count = np.array([conv_treat, conv_ctrl])
nobs = np.array([n_treat, n_ctrl])
z_stat, p_value = proportions_ztest(count, nobs, alternative='larger')
return {
'control_rate': round(rate_ctrl, 4),
'treatment_rate': round(rate_treat, 4),
'lift_pct': round(lift_pct, 2),
'z_statistic': round(z_stat, 3),
'p_value': round(p_value, 4),
'significant_95': p_value < 0.05,
'recommendation': 'Ship' if (p_value < 0.05 and lift_pct > 0) else 'Do not ship'
}
# Usage
# result = test_conversion_lift(df)
# print(result)
- What is the minimum detectable effect at 80% power for this experiment?
- How would you handle multiple A/B tests running simultaneously (interference effects)?
- The p-value is 0.048 β would you ship? What other factors would you consider?
Razorpay fires webhooks to merchants on payment events. You receive a list of JSON log strings, each with merchant_id, event_type, status, and a nested error object with code and description. Find the top 5 failure reasons by error code and the merchants most impacted by each.
import pandas as pd
import json
def analyze_webhook_failures(raw_logs: list[str]) -> pd.DataFrame:
"""
raw_logs: list of JSON strings
Returns: top 5 error codes with count and affected merchant list
"""
records = []
for line in raw_logs:
try:
log = json.loads(line)
if log.get('status') == 'failed' and log.get('error'):
records.append({
'merchant_id': log['merchant_id'],
'event_type': log['event_type'],
'error_code': log['error'].get('code', 'UNKNOWN'),
'error_desc': log['error'].get('description', ''),
})
except (json.JSONDecodeError, KeyError):
continue # skip malformed logs gracefully
df = pd.DataFrame(records)
if df.empty:
return df
summary = (
df.groupby('error_code')
.agg(
failure_count = ('merchant_id', 'count'),
unique_merchants = ('merchant_id', 'nunique'),
top_merchants = ('merchant_id',
lambda x: ', '.join(x.value_counts().head(3).index.tolist())),
description = ('error_desc', 'first')
)
.reset_index()
.sort_values('failure_count', ascending=False)
.head(5)
)
return summary
try/except β production log files are never perfectly clean. The business value here is distinguishing between failures caused by Razorpay infrastructure (error codes like GATEWAY_TIMEOUT) vs merchant-side issues (MERCHANT_URL_UNREACHABLE) β these require very different remediation actions, so include that in your analysis narrative to the interviewer.
Round 3 β Business Analysis & Metrics Design
| Questions | 3 questions |
| Duration | 50 minutes |
| Difficulty | Medium to Hard |
| Key Topics | North Star MetricMetric DecompositionRoot Cause AnalysisRazorpayX |
The interviewer asks you to define and defend a North Star Metric (NSM) for Razorpay. They will push back on your answer to test how well you understand the business model.
Strong answer structure:
- Proposed NSM: Monthly Active Merchants (merchants with β₯1 successful transaction in the month) β captures adoption breadth
- Alternative considered: Total Payment Volume (TPV) β captures revenue intensity but misses merchant health
- Why MAM over TPV: A single large merchant can inflate TPV. MAM is harder to spike artificially, is predictive of long-term revenue, and correlates with upsell opportunities (banking, lending)
- Guardrail metrics: Average GMV per MAM (to catch quality), payment success rate (to catch infra degradation), churn rate of MAM (to catch retention)
- NSM tree: MAM = New merchants activated + Retained merchants β Churned merchants
- How would the NSM change if Razorpay pivoted to focus on enterprise merchants only?
- How would you measure success of RazorpayX (business banking) separately?
The MondayβTuesday payment success rate fell from 94.2% to 91.1%. You are told there were no planned deployments. Walk through your diagnostic process step by step.
Diagnostic steps in order:
- Confirm the metric: Is the drop in all transaction types or specific payment modes (UPI / card / netbanking)?
- Segment by payment method: If only UPI dropped, the issue is upstream of Razorpay (NPCI / bank). If all methods dropped equally, suspect Razorpay infra.
- Segment by merchant: Is the drop concentrated in a few large merchants or spread across all? A concentration in 2β3 merchants suggests merchant-level issue, not platform.
- Check error code distribution: What error codes are driving the new failures? TIMEOUT vs DECLINED vs FRAUD_BLOCKED lead to very different root causes.
- Check volume mix: Did total transaction volume surge on Tuesday? A sudden volume spike from a flash sale can temporarily stress the stack without a code change.
- Correlate with infra metrics: API response time, error logs, queue depth. Escalate to engineering if needed.
RazorpayX is Razorpay’s business banking product β it includes current accounts, vendor payments, payroll, and expense management. Design a metrics framework for the Product team to track the health of RazorpayX.
Three-layer metrics framework:
- North Star: Monthly Active Businesses (MAB) β businesses that made at least one payout via RazorpayX
- Acquisition metrics: New account activations per month Β· Time from sign-up to first payout Β· Drop-off in KYC completion funnel
- Engagement metrics: Average payout volume per MAB Β· Feature adoption rate (payroll / vendor payments / cards) Β· Wallet balance maintained (proxy for trust)
- Retention metrics: 30-day, 90-day payout retention Β· Churn rate (no payout in 60 days) Β· Businesses upgrading to premium tiers
- Revenue metrics: Revenue per MAB (interchange + SaaS fees) Β· Lending conversion from RazorpayX users (cross-sell)
- Guardrails: Failed payout rate Β· Customer support tickets per MAB Β· KYC rejection rate
Round 4 β Case Study & Business Strategy
| Questions | 1 case study |
| Duration | 45 minutes |
| Difficulty | Hard |
| Key Topics | Market SizingGTM StrategyNew Vertical EntryRisk vs Opportunity |
Razorpay’s leadership is considering entering the insurance premium collection vertical β enabling insurance companies (LIC, ICICI Lombard, SBI Life) to collect premiums via Razorpay’s payment infrastructure. You are the Senior BA assigned to evaluate this opportunity. What would you analyse, what is the market opportunity, and what would your GTM recommendation be?
GTM Recommendation (3-phase):
- Phase 1 β Land (Months 1β6): Partner with 2β3 private insurers (HDFC Life, Bajaj Allianz) where BillDesk contracts are expiring. Offer free integration + lower MDR for first 6 months. Focus on health insurance (shorter renewal cycles, digital-native customers).
- Phase 2 β Expand (Months 7β18): Add NACH mandate support for auto-renewals β this is the stickiest product. Launch an insurer analytics dashboard showing premium collection funnel, failed payment patterns, and customer risk scores. Use data as a moat.
- Phase 3 β Lead (18+ months): Cross-sell RazorpayX for insurer payouts (claim settlements). Launch embedded insurance on Razorpay checkout (insurance add-on at checkout = new revenue stream). Lobby for IRDAI sandbox status.
Key metrics to track: Insurer sign-up rate Β· Premium collection TPV Β· Failed renewal rate Β· NACH mandate activation rate Β· Revenue per insurer per month
4-Week Plan to Crack Any Product Company Interview
This plan works for Razorpay, Meta, Google, Amazon β any product-based company. It covers ~950 questions across two eBooks and includes three mock interviews. No shortcuts, no cramming.
Foundation
Build your base β SQL, Python, case study frameworks
Master window functions, CTEs, cohort logic. Revise pandas basics, hypothesis testing. Learn the MECE framework for case studies. Read The Data Monk’s company-specific interview pattern posts to understand what is being asked before you prepare.
Question Bank
2200 Most Asked Analytics Interview Questions eBook
Cover the SQL, Python, and case study sections fully. Every question matters β skip nothing. At the end of Week 2, book your first mock interview: topmate.io/nitin_kamal
SQL Depth
Ace Any SQL Interview eBook β 200+ Questions
Cover all questions without shortcuts. Advanced window functions, optimisation, real company SQL patterns. At the end of Week 3, book your second mock interview: topmate.io/nitin_kamal
Revision
Revise first 5 chapters of 2200 eBook + timed practice
15 min per SQL problem. 20 min per case study. Simulate real interview pressure. Final mock interview at the end of Week 4. Book your final mock β