Data AnalyticsPortfolio projects with Indian public data

What Indians pay for on UPI: a merchant category code project

PK
Pankit Kumar
Sr. Data Scientist at Parexel (a Goldman Sachs–backed company) · 27 September 2026 · 5 min read
In this article (7 sections)

Related programme: the Data Analytics with Generative AI course (3–4 months; ₹90,000 (inclusive of GST) · EMI from ₹4,000/month).

The short answer

NPCI publishes monthly UPI merchant payments split by merchant category code (MCC), from groceries and fast food to fuel stations and pharmacies. That lets you answer a question most people only guess at: which kinds of merchants account for UPI's merchant volume and value, and how do their typical payment sizes differ? This project builds a monthly MCC table, groups the codes into readable segments, measures concentration, and reconciles the MCC totals against NPCI's person-to-merchant (P2M) figures.

Two tables on one page

Both come from NPCI's UPI Ecosystem Statistics, which has a month selector and a Download control.

Merchant Category Classification has these columns: Type, MCC, Description, Volume (in Mn), Value (in Cr.). "Type" groups the rows into bands such as "High Transacting Categories" and "Medium Transacting…". The Type label appears only on the first row of each band in the on-screen table, and the rows beneath it are blank. The descriptions are standard MCC names, for example 5411 "Groceries and supermarkets", 5812 "Eating places and restaurants" and 5541 "Service stations (with or without ancillary services)".

P2P and P2M Transactions has a two-level header. Month, then Total, P2P and P2M, each split into Volume (In Mn.) and Value (Cr).

Check whether the downloaded files keep the same layout as the screen. Look at the file's header before you parse it.

Why a product or strategy team would care

Merchant acquiring, credit-on-UPI, loyalty and small-business lending products all start from "where is the money actually moving?". A clean MCC mix with ticket sizes is the kind of market-sizing input a strategy analyst prepares in a first month on the job. It also trains a habit analysts need: checking that category rows add up to the headline figure before building anything on them.

Cleaning steps for this source

  1. 1Fill the Type band downward. Carry the last non-blank Type onto the blank rows below it.
  2. 2Keep MCC as text. Codes are four digits, and storing them as integers invites accidents. Keep leading zeros if any appear.
  3. 3Strip digit grouping from volume and value.
  4. 4Flatten the two-row header of the P2P/P2M table into names like p2m_volume_mn.
  5. 5Check whether the MCC table is complete. If it lists only the top categories in each band, its total will fall short of P2M volume. That shortfall is a finding to report, not an error to hide.

Synthetic example: mix, tickets and concentration

All numbers below are synthetic. The MCC codes and descriptions are real standard codes, but the volumes and values are invented.

python
import pandas as pd
import numpy as np

mcc = pd.DataFrame({
    "Type": ["High Transacting Categories", None, None, None, "Medium Transacting Categories", None, None],
    "MCC": ["5411", "5814", "5812", "5541", "5912", "5462", "4121"],
    "Description": ["Groceries and supermarkets", "Fast food restaurants", "Eating places and restaurants",
                    "Service stations (with or without ancillary services)", "Drug stores and pharmacies",
                    "Bakeries", "Taxicabs and limousines"],
    "Volume (in Mn)": ["3,000.00", "1,200.00", "1,000.00", "600.00", "250.00", "400.00", "150.00"],
    "Value (in Cr.)": ["70,000.00", "15,000.00", "18,000.00", "36,000.00", "11,000.00", "6,000.00", "4,500.00"],
})
mcc["Type"] = mcc["Type"].ffill()
for col, new in [("Volume (in Mn)", "volume_mn"), ("Value (in Cr.)", "value_cr")]:
    mcc[new] = pd.to_numeric(mcc[col].str.replace(",", "", regex=False))

segment = {"5411": "Groceries", "5814": "Food service", "5812": "Food service", "5541": "Fuel",
           "5912": "Health", "5462": "Food retail", "4121": "Transport"}
mcc["segment"] = mcc["MCC"].map(segment)
assert mcc["segment"].notna().all(), "an MCC has no segment mapping"

seg = mcc.groupby("segment")[["volume_mn", "value_cr"]].sum()
seg["vol_share"] = (seg["volume_mn"] / seg["volume_mn"].sum()).round(3)
seg["avg_ticket_rs"] = (10 * seg["value_cr"] / seg["volume_mn"]).round(0)
print(seg.sort_values("volume_mn", ascending=False))

# Herfindahl-Hirschman index on volume shares across MCCs (0 to 10,000)
shares = mcc["volume_mn"] / mcc["volume_mn"].sum() * 100
print("HHI across listed MCCs:", round(float((shares ** 2).sum())))

# Reconciliation against a synthetic P2M headline
p2m_volume_mn = 7200.0
coverage = mcc["volume_mn"].sum() / p2m_volume_mn
print(f"listed MCCs cover {coverage:.1%} of P2M volume")

The assertion on segment mapping is deliberate. If NPCI adds a new code next month, you want the run to stop rather than silently drop that code's volume.

The analysis plan

  • Mix by segment: volume and value shares by your segment mapping, for the latest month and for the same month a year earlier.
  • Ticket sizes: average rupees per payment by segment. A pairing like fuel and groceries is a useful contrast, because the two differ in how often and how much people pay.
  • Concentration: HHI across the listed MCCs by month. Is merchant volume spreading across more categories or piling into a few?
  • Reconciliation: the listed MCC total as a share of P2M volume and value, from the P2P/P2M table. Publish this number prominently.
  • P2M share over time: P2M ÷ total volume by month, as the backdrop to the category story.

What you hand in

ArtefactSpecification
Treemap or sorted barSegments by volume share, labelled with average ticket
Line chartP2M share of total UPI volume by month
TableTop 15 MCCs: code, description, volume, value, average ticket
Mapping filemcc_segments.csv, reviewed by hand, with a column for your reasoning
READMESource, tab names, months, reconciliation coverage, segment rules, limits

The limits section should say that an MCC is how a merchant is classified, which may not match what a customer actually bought. A "grocery" merchant can sell almost anything.

Questions you should rehearse

  1. 1Why store MCC as a string?
  2. 2The MCC rows cover less than 100% of P2M. What could explain the gap?
  3. 3What does HHI tell a business reader, and how would you explain it without the formula?
  4. 4How would you decide whether a mixed-category merchant belongs in "food" or "retail"?
  5. 5If a credit-on-UPI product team asked for a target segment, what would you recommend from this, and what would you want to check first?

This pairs naturally with the ticket size trend project, which explains the headline side of the same story. Segment mapping, reconciliation and clear business charts are all part of our Data Analyst programme. Ask a counsellor on the contact page for current batches and fees.

PK
Pankit Kumar
Lead Instructor, NeuraPath Academy

Pankit Kumar has 10 years in Data Science & AI, building and shipping production systems in regulated pharma and clinical environments. He is a freelance trainer at Boston Institute of Analytics, AnalytixLabs and Scaler, and has taught this material to thousands of working professionals.

This article is part of our Data Analytics with Generative AI programme — 3–4 months. The full analyst stack — Excel, SQL, Power BI and Python pipelines — then a generative-AI layer you can prove is right.

Explore Data Analytics with Generative AI
Counselling is free · no obligation

Not sure which programme fits?

Tell us your background and we will map it to the right entry point — including saying so when a cheaper programme is the better fit. A counsellor replies within one working day.