India’s Trade Statistics: Exports, Imports & Sectoral Composition

Trade
Exports
Imports
A comprehensive interactive analysis of India’s merchandise trade — total flows, trade balance, cereals exports, fertilizer and petroleum imports — with HS code level decomposition.
Author

Pawan

Published

October 29, 2025

Modified

March 15, 2026

Overview

India’s merchandise trade has grown dramatically over the past two decades — exports crossing USD 450 billion in recent years while imports hover around USD 700 billion, producing a persistent and growing trade deficit. This report decomposes the story through HS code level analysis: cereals (HS 10), edible oils (HS 15), fertilizers (HS 31), and petroleum products (HS 27).


Setup

Code
library(data.table)
library(ggplot2)
library(ggiraph)
library(scales)
library(gt)
library(plotly)
library(viridis)
library(readxl)
library(patchwork)
library(dplyr)

my_theme <- theme_minimal(base_size = 13) +
  theme(
    plot.title      = element_text(size = 16, face = "bold", hjust = 0.5, margin = margin(b = 8)),
    plot.subtitle   = element_text(size = 11, hjust = 0.5, color = "#7F8C8D"),
    plot.caption    = element_text(size = 9, color = "#95A5A6", hjust = 0),
    axis.title      = element_text(size = 12, face = "bold"),
    axis.text       = element_text(size = 10),
    axis.text.x     = element_text(angle = 45, vjust = 1, hjust = 1),
    legend.position = "bottom",
    legend.title    = element_blank(),
    panel.grid.major= element_line(color = "#ECF0F1", linewidth = 0.4),
    panel.grid.minor= element_blank(),
    strip.background= element_rect(fill = "#F8F9FA", color = NA),
    strip.text      = element_text(face = "bold", size = 11)
  )

## Data cleaning helper
clean_trade <- function(dt, col) {
  dt[, (col) := as.numeric(gsub("[,]","", get(col)))]
  dt[is.na(get(col)), (col) := 0]
  dt[, `:=`(HSCode2=substr(HSCode,1,2),
             HSCode4=substr(HSCode,1,4),
             HSCode6=substr(HSCode,1,6))]
  dt
}

Section 1 · Data Loading & Aggregation

Code
usd  <- clean_trade(readRDS("/home/pawan/trade/totaltrade/total_exportsusd.rds"),  "Export")
qty  <- clean_trade(readRDS("/home/pawan/trade/totaltrade/total_exportsqty.rds"),  "Export")
musd <- clean_trade(readRDS("/home/pawan/trade/totaltrade/total_importsusd.rds"),  "Import")
mqty <- clean_trade(readRDS("/home/pawan/trade/totaltrade/total_importsqty.rds"),  "Import")

hs2  <- as.data.table(read_excel("/home/pawan/trade/totaltrade/two-digit.xlsx"))
hs2[, HSCode2 := as.integer(HSCode2)]
qty[,  HSCode2 := as.integer(HSCode2)]
mqty[, HSCode2 := as.integer(HSCode2)]
qty  <- merge(qty,  hs2, by="HSCode2", all.x=TRUE)
mqty <- merge(mqty, hs2, by="HSCode2", all.x=TRUE)

## Annual aggregates
exp_usd  <- usd[,  .(Export_USD  = sum(Export,  na.rm=TRUE)/1000), by=Year]
imp_usd  <- musd[, .(Import_USD  = sum(Import,  na.rm=TRUE)/1000), by=Year]
exp_qty  <- qty[,  .(Export_Qty  = sum(Export,  na.rm=TRUE)), by=Year]
imp_qty  <- mqty[, .(Import_Qty  = sum(Import,  na.rm=TRUE)), by=Year]

trade <- Reduce(function(a,b) merge(a,b,by="Year"),
                list(exp_usd, imp_usd, exp_qty, imp_qty))
trade[, `:=`(
  Balance_USD  = Export_USD - Import_USD,
  Volume_USD   = Export_USD + Import_USD,
  Coverage_Pct = round(Export_USD/Import_USD*100, 1),
  Exp_Growth   = round((Export_USD/shift(Export_USD)-1)*100, 1),
  Imp_Growth   = round((Import_USD/shift(Import_USD)-1)*100, 1)
)]

Section 2 · Trade Overview: Exports, Imports & Balance

Code
trade_long <- melt(trade[, .(Year,Export_USD,Import_USD)],
                   id.vars="Year", variable.name="Flow", value.name="Billion_USD")
trade_long[, tt := paste0(Flow,"\nYear: ",Year,"\n$",round(Billion_USD,1),"B")]

p_flows <- ggplot(trade_long, aes(x=Year, y=Billion_USD, color=Flow,
                                   text=tt, group=Flow)) +
  geom_line_interactive(aes(tooltip=tt, data_id=Flow), linewidth=1.3) +
  geom_point(size=2.5, alpha=0.7) +
  geom_ribbon(data=trade, aes(x=Year, ymin=pmin(Export_USD,Import_USD),
                               ymax=pmax(Export_USD,Import_USD),
                               fill=ifelse(Balance_USD<0,"Deficit","Surplus")),
              inherit.aes=FALSE, alpha=0.15) +
  scale_color_manual(values=c("Export_USD"="#27AE60","Import_USD"="#E74C3C"),
                     labels=c("Export_USD"="Exports","Import_USD"="Imports")) +
  scale_fill_manual(values=c("Deficit"="#FADBD8","Surplus"="#D5F5E3")) +
  scale_y_continuous(labels=dollar_format(suffix="B")) +
  my_theme +
  labs(title="India's Merchandise Trade: Exports & Imports",
       subtitle="Shaded area shows trade deficit (red) | Hover for exact values",
       y="USD Billion", x="Year",
       caption="Source: DGCI&S / Ministry of Commerce | Analysis: Pawan")

girafe(ggobj=p_flows, width_svg=12, height_svg=6,
       options=list(
         opts_tooltip(css="background:#fff;color:#333;padding:8px;border-radius:5px;border:1px solid #ddd;"),
         opts_hover(css="stroke-width:2.5;"),
         opts_hover_inv(css="opacity:0.3;"),
         opts_sizing(rescale=TRUE,width=1)))
Code
p_bal <- ggplot(trade, aes(x=Year, y=Balance_USD,
                            fill=Balance_USD>0,
                            text=paste0("Year: ",Year,"\nBalance: $",round(Balance_USD,1),"B"))) +
  geom_col(width=0.75, alpha=0.85) +
  geom_hline(yintercept=0, color="#333", linewidth=0.5) +
  scale_fill_manual(values=c("TRUE"="#27AE60","FALSE"="#E74C3C"),
                    labels=c("TRUE"="Surplus","FALSE"="Deficit")) +
  scale_y_continuous(labels=dollar_format(suffix="B")) +
  my_theme +
  labs(title="India's Trade Balance (USD Billion)",
       subtitle="Red = trade deficit | India has run a persistent goods-trade deficit",
       y="Trade Balance (USD Billion)", x="Year",
       caption="Source: DGCI&S | Analysis: Pawan")

ggplotly(p_bal, tooltip="text") |> layout(hovermode="x unified")

Section 3 · Cereals Export Composition

India is a major global rice exporter and an intermittent wheat exporter. The composition shifts sharply based on domestic supply and export bans/policy.

Code
cer <- qty[HSCode2 == 10]
cer[, Commodity := fcase(
  HSCode4=="1006", "Rice",
  HSCode4=="1001", "Wheat",
  HSCode4=="1005", "Maize",
  HSCode4=="1008", "Millet/Buckwheat",
  default          = "Other Cereals"
)]
cer_agg <- cer[, .(Qty = sum(Export, na.rm=TRUE)), .(Year, Commodity)]
cer_agg[, Total := sum(Qty), by=Year]
cer_agg[, Share := round(Qty/Total*100)]
cer_agg[, tt    := paste0("<b>",Commodity,"</b><br>Year: ",Year,
                           "<br>Qty: ",format(Qty,big.mark=",")," K Tonnes",
                           "<br>Share: ",Share,"%")]

p_cer <- ggplot(cer_agg, aes(x=Year, y=Qty, fill=Commodity)) +
  geom_bar_interactive(aes(tooltip=tt, data_id=Commodity),
                        stat="identity", position="stack", width=0.75, alpha=0.9) +
  geom_text(aes(label=ifelse(Share<7,"",paste0(Share,"%"))),
            position=position_stack(vjust=0.5), size=2.8, color="white", fontface="bold") +
  scale_fill_brewer(palette="Set1") +
  scale_y_continuous(labels=comma_format()) +
  my_theme +
  labs(title="India's Cereal Exports by Commodity",
       subtitle="Rice dominates; wheat exports surge in surplus years",
       y="Quantity (000 Tonnes)", x="Year",
       caption="Source: DGCI&S | HS Chapter 10 | Analysis: Pawan")

girafe(ggobj=p_cer, width_svg=12, height_svg=6,
       options=list(
         opts_tooltip(css="background:#fff;color:#333;padding:8px;border-radius:5px;border:1px solid #ddd;"),
         opts_hover(css="opacity:0.85;stroke-width:1.5;"),
         opts_hover_inv(css="opacity:0.3;"),
         opts_sizing(rescale=TRUE,width=1)))

Section 4 · Fertilizer Imports

Code
fert <- mqty[HSCode2 == 31]
fert[, Commodity := fcase(
  HSCode4=="3102", "Nitrogenous",
  HSCode4=="3104", "Potassic",
  HSCode4=="3105", "DAP & NPK",
  default          = "Other Chemical Fert"
)]
fert_agg <- fert[, .(Qty = sum(Import, na.rm=TRUE)), .(Year, Commodity)]
fert_agg[, Total := sum(Qty), by=Year]
fert_agg[, Share := round(Qty/Total*100)]
fert_agg[, tt    := paste0("<b>",Commodity,"</b><br>Year: ",Year,
                            "<br>Qty: ",format(Qty,big.mark=",")," K Tonnes",
                            "<br>Share: ",Share,"%")]

p_fert <- ggplot(fert_agg, aes(x=Year, y=Qty, fill=Commodity)) +
  geom_bar_interactive(aes(tooltip=tt, data_id=Commodity),
                        stat="identity", position="stack", width=0.75, alpha=0.9) +
  geom_text(aes(label=ifelse(Share<8,"",paste0(Share,"%"))),
            position=position_stack(vjust=0.5), size=2.8, color="white", fontface="bold") +
  scale_fill_viridis_d(option="E") +
  scale_y_continuous(labels=comma_format()) +
  my_theme +
  labs(title="India's Fertilizer Imports (HS Chapter 31)",
       subtitle="Nitrogenous (Urea) and DAP/NPK dominate volumes",
       y="Quantity (000 Tonnes)", x="Year",
       caption="Source: DGCI&S | HS Chapter 31 | Analysis: Pawan")

girafe(ggobj=p_fert, width_svg=12, height_svg=6,
       options=list(
         opts_tooltip(css="background:#fff;color:#333;padding:8px;border-radius:5px;border:1px solid #ddd;"),
         opts_hover(css="opacity:0.85;stroke-width:1.5;"),
         opts_sizing(rescale=TRUE,width=1)))

Section 5 · Edible Oil Imports (HS 15)

Code
oil_imp <- mqty[HSCode2 == 15, .(Qty=sum(Import,na.rm=TRUE)), by=Year]

p_oil <- ggplot(oil_imp, aes(x=Year, y=Qty, group=1,
                               text=paste0("Year: ",Year,"\n",format(Qty,big.mark=",")," K Tonnes"))) +
  geom_line(color="#E67E22", linewidth=1.3) +
  geom_point(color="#E67E22", size=3) +
  geom_area(fill="#E67E22", alpha=0.15) +
  geom_smooth(method="lm", se=TRUE, color="#2C3E50", linetype="dashed", linewidth=0.8) +
  scale_y_continuous(labels=comma_format()) +
  my_theme +
  labs(title="India's Vegetable Fats & Edible Oil Imports",
       subtitle="Upward structural trend; dashed = linear fit",
       y="Quantity (000 Tonnes)", x="Year",
       caption="Source: DGCI&S | HS Chapter 15 | Analysis: Pawan")

ggplotly(p_oil, tooltip="text") |> layout(hovermode="x unified")

Section 6 · Petroleum Imports (HS 27)

Code
petro <- mqty[HSCode2 == 27]
petro[, Commodity := fcase(
  HSCode4=="2701", "Coal",
  HSCode4=="2709", "Crude Petroleum",
  HSCode4=="2710", "Refined Products",
  HSCode4=="2711", "Petroleum Gases",
  default          = "Other Petroleum"
)]
petro_agg <- petro[, .(Qty=sum(Import,na.rm=TRUE)), .(Year, Commodity)]
petro_agg[, Total:=sum(Qty),by=Year]
petro_agg[, Share:=round(Qty/Total*100)]
petro_agg[, tt:=paste0("<b>",Commodity,"</b><br>Year: ",Year,
                        "<br>Qty: ",format(round(Qty/1000),big.mark=",")," MT<br>Share: ",Share,"%")]

p_petro <- ggplot(petro_agg, aes(x=Year, y=Qty/1000, fill=Commodity)) +
  geom_bar_interactive(aes(tooltip=tt, data_id=Commodity),
                        stat="identity", position="stack", width=0.75, alpha=0.9) +
  geom_text(aes(label=ifelse(Share<5,"",paste0(Share,"%"))),
            position=position_stack(vjust=0.5), size=2.8, color="white", fontface="bold") +
  scale_fill_brewer(palette="Set2") +
  scale_y_continuous(labels=comma_format()) +
  my_theme +
  labs(title="India's Petroleum Imports (HS Chapter 27)",
       subtitle="Crude petroleum dominates; coal and gases growing",
       y="Quantity (Million Tonnes)", x="Year",
       caption="Source: DGCI&S | HS Chapter 27 | Analysis: Pawan")

girafe(ggobj=p_petro, width_svg=12, height_svg=6,
       options=list(
         opts_tooltip(css="background:#fff;color:#333;padding:8px;border-radius:5px;border:1px solid #ddd;"),
         opts_hover(css="opacity:0.85;stroke-width:1.5;"),
         opts_sizing(rescale=TRUE,width=1)))

Section 7 · Trade Metrics Summary Table

Code
trade[, .(Year, Export_USD=round(Export_USD,1), Import_USD=round(Import_USD,1),
           Balance_USD=round(Balance_USD,1), Coverage_Pct, Exp_Growth, Imp_Growth)] |>
  gt() |>
  tab_header(title="India's Annual Trade Metrics (USD Billion)") |>
  cols_label(Year="Year", Export_USD="Exports ($B)", Import_USD="Imports ($B)",
             Balance_USD="Balance ($B)", Coverage_Pct="Coverage %",
             Exp_Growth="Export Growth %", Imp_Growth="Import Growth %") |>
  data_color(Balance_USD, colors=scales::col_numeric(c("#E74C3C","#FDFEFE","#27AE60"), domain=NULL)) |>
  data_color(Coverage_Pct, colors=scales::col_numeric(c("#FADBD8","#D5F5E3"), domain=c(50,90))) |>
  opt_stylize(style=3) |>
  tab_source_note("Source: DGCI&S / Ministry of Commerce")
India's Annual Trade Metrics (USD Billion)
Year Exports ($B) Imports ($B) Balance ($B) Coverage % Export Growth % Import Growth %
1997-98 69.6 83.0 -13.4 83.9 NA NA
1998-99 66.4 84.8 -18.3 78.4 -4.5 2.2
1999-00 73.6 99.5 -25.8 74.0 10.8 17.3
2000-01 89.1 101.1 -12.0 88.2 21.0 1.6
2001-02 87.7 102.8 -15.2 85.2 -1.6 1.7
2002-03 105.4 122.8 -17.4 85.8 20.3 19.4
2003-04 127.7 156.3 -28.6 81.7 21.1 27.3
2004-05 167.1 223.0 -56.0 74.9 30.8 42.7
2005-06 206.2 298.3 -92.2 69.1 23.4 33.8
2006-07 252.8 371.5 -118.6 68.1 22.6 24.5
2007-08 326.3 503.3 -177.0 64.8 29.0 35.5
2008-09 370.6 607.4 -236.8 61.0 13.6 20.7
2009-10 357.5 576.7 -219.2 62.0 -3.5 -5.0
2010-11 499.6 739.5 -239.9 67.6 39.8 28.2
2011-12 611.9 978.6 -366.7 62.5 22.5 32.3
2012-13 600.8 981.5 -380.7 61.2 -1.8 0.3
2013-14 628.8 900.4 -271.6 69.8 4.7 -8.3
2014-15 620.7 896.1 -275.4 69.3 -1.3 -0.5
2015-16 524.6 762.0 -237.4 68.8 -15.5 -15.0
2016-17 551.7 768.7 -217.0 71.8 5.2 0.9
2017-18 607.1 931.2 -324.1 65.2 10.0 21.1
2018-19 660.2 1028.2 -368.0 64.2 8.7 10.4
2019-20 626.7 949.4 -322.7 66.0 -5.1 -7.7
2020-21 583.6 788.9 -205.3 74.0 -6.9 -16.9
2021-22 844.0 1226.1 -382.1 68.8 44.6 55.4
2022-23 902.1 1431.9 -529.8 63.0 6.9 16.8
2023-24 874.1 1356.4 -482.3 64.4 -3.1 -5.3
2024-25 504.2 833.6 -329.4 60.5 -42.3 -38.5
Source: DGCI&S / Ministry of Commerce