While AMFI ( Association of Mutual Funds in India) provides the foundational NAV data, this guide focuses on the engineering required to build high-performance retrieval systems.
Each Mutual Fund scheme is identified by a unique, standardized scheme code; by programmatically targeting this identifier, we can retrieve real-time data points including the latest NAV (Net Asset Value), the most recent valuation date, and the official scheme nomenclature.These are the Python libraries required to be installed
pip install requests pandas matplotlib openpyxl
Here is the code to get latest NAV of a Scheme.
import requests
def get_latest_nav(scheme_code):
url = f"https://api.mfapi.in/mf/{scheme_code}/latest"
response = requests.get(url,timeout=10)
data = response.json()
latest_nav = data['data'][0]['nav']
date = data['data'][0]['date']
fund_name = data['meta']['scheme_name']
return fund_name, latest_nav, date
# Example: Quant Small Cap Fund (Direct Plan)
name, nav, date = get_latest_nav("120847")
print(f"Fund: {name} | Current NAV: {nav} | Date: {date}")Fund: quant ELSS Tax Saver Fund - Growth Option - Direct Plan | Current NAV: 414.31430 | Date: 09-01-2026
/latest — Appended to the URL to fetch only the most recent data point, reducing payload size.
data['data'][0]['date'] — Specifically extracts the timestamp of the last updated NAV from the API response.
return fund_name, latest_nav, date — Returns a tuple containing all three critical data points for further processing.
print(f"... Date: {date}") — Uses Python f-strings to display the formatted output in the console.
This Part I script is intentionally minimal to focus on the core API concept. It does not include error handling — if the scheme code is wrong or the network is unavailable, it will crash with an unhelpful error.
Part II introduces try/except blocks and
Part IV adds full Exponential Backoff retry logic
for production-ready reliability.
Jump to Part II →
# Input scheme list
input_schemes = [
{"schemeCode": 120251, "schemeName": "ICICI Prudential Equity & Debt Fund Direct Growth"},
{"schemeCode": 118955, "schemeName": "HDFC Flexi Cap Direct Plan Growth"},
{"schemeCode": 120334, "schemeName": "ICICI Prudential Multi Asset Fund Direct Growth"}
]
For production environments, this input list can be dynamically retrieved from databases such as SQLite or MySQL. Furthermore, the processed outputs can be archived back into storage for historical analysis and reporting.
Below is the complete, integrated implementation.
import requests
from datetime import datetime
def fetch_latest_navs(scheme_list):
"""
Retrieves the most recent NAV and corresponding date for a list of funds.
"""
base_url = "https://api.mfapi.in/mf/"
final_report = []
print(f"{'SCHEME NAME':<65} | {'NAV':<10} | {'DATE':<12}")
print("=" * 92)
for scheme in scheme_list:
code = scheme['schemeCode']
name = scheme['schemeName']
try:
# Request data from MFAPI
response = requests.get(f"{base_url}{code}/latest", timeout=10)
response.raise_for_status()
data = response.json()
if "data" in data and len(data["data"]) > 0:
# API data is usually in reverse chronological order.
latest = data["data"][0]
nav = latest["nav"]
date = latest["date"]
final_report.append({
"name": name,
"code": code,
"nav": nav,
"date": date
})
# Truncated name for table alignment
short_name = (name[:62] + '..') if len(name) > 65 else name
print(f"{short_name:<65} | {nav:<10} | {date:<12}")
else:
print(f"Data missing for: {name}")
except Exception as e:
print(f"Could not retrieve {code}: {e}")
return final_report
if __name__ == "__main__":
# Input scheme list
input_schemes = [
{"schemeCode": 120251, "schemeName": "ICICI Prudential Equity & Debt Fund Direct Growth"},
{"schemeCode": 118955, "schemeName": "HDFC Flexi Cap Direct Plan Growth"},
{"schemeCode": 120334, "schemeName": "ICICI Prudential Multi Asset Fund Direct Growth"}
]
print(f"Querying MFAPI for latest records...\n")
results = fetch_latest_navs(input_schemes)
Querying MFAPI for latest records...
SCHEME NAME | NAV | DATE
=================================================================================
ICICI Prudential Equity & Debt Fund Direct Growth | 453.09000 | 09-01-2026
HDFC Flexi Cap Direct Plan Growth | 2264.30200 | 09-01-2026
ICICI Prudential Multi Asset Fund Direct Growth | 898.67790 | 09-01-2026
import requests
import matplotlib.pyplot as plt
from datetime import datetime
def fetch_and_plot_nav(scheme_code):
"""
Fetches historical NAV data for a given scheme code from mfapi.in
and plots the variation over time.
"""
api_url = f"https://api.mfapi.in/mf/{scheme_code}"
print(f"Fetching data for Scheme Code: {scheme_code}...")
try:
response = requests.get(api_url, timeout=10)
response.raise_for_status()
data = response.json()
if data.get("status") != "SUCCESS":
print("Error: Could not find data for this scheme code.")
return
scheme_name = data['meta']['scheme_name']
nav_data = data['data']
# Extract dates and NAV values
# We take the last 60 entries for a clear 2-month view
subset = nav_data[:60]
# Parse dates and convert NAVs to floats
dates = [datetime.strptime(item['date'], '%d-%m-%Y') for item in subset]
nav_values = [float(item['nav']) for item in subset]
# Sort by date
plot_data = sorted(zip(dates, nav_values))
dates, nav_values = zip(*plot_data)
# Plotting
plt.figure(figsize=(10, 6))
plt.plot(dates, nav_values, marker='o', linestyle='-',
color='b', markersize=4)
plt.title(f"NAV Variation: {scheme_name}", fontsize=12)
plt.xlabel("Date")
plt.ylabel("Net Asset Value (NAV)")
plt.grid(True, linestyle='--', alpha=0.7)
plt.xticks(rotation=45)
plt.tight_layout()
print("Displaying graph...")
plt.show()
except requests.exceptions.RequestException as e:
print(f"Network error: {e}")
except Exception as e:
print(f"An unexpected error occurred: {e}")
if __name__ == "__main__":
print("--- Mutual Fund NAV Tracker ---")
user_code = input("Enter the Mutual Fund Scheme Code: ").strip()
if user_code.isdigit():
fetch_and_plot_nav(user_code)
else:
print("Please enter a valid numeric scheme code.")
Once the current Net Asset Value (NAV) is retrieved for each scheme, you can determine your total investment value by multiplying the NAV by the number of units held. This provides a real-time snapshot of your portfolio's worth:
Integrating this logic allows for automated tracking of gains, losses, and overall asset allocation.
Keeping track of multiple Mutual Fund investments can be tedious if done manually. This Python script automates the process by connecting directly to the Open Source Mutual Fund API (mfapi.in) to fetch real-time Net Asset Values (NAV) and calculate your total portfolio valuation instantly.
The script features Exponential Backoff logic. If the API is busy, the script intelligently waits and retries (1s, 2s, 4s, etc.), ensuring your data is fetched even under unstable network conditions.
By mapping your specific schemeCodes and units, the code performs automated multiplication and aggregation to provide a "Grand Total" of your net worth across various fund houses.
requests library to pull live JSON data from the AMFI-linked API endpoints.import requests
import time
from datetime import datetime
def fetch_nav_with_retry(scheme_code):
"""
Fetches the latest NAV for a given scheme code from the AMFI API.
Implements exponential backoff for reliability.
"""
api_url = f"https://api.mfapi.in/mf/{scheme_code}"
retries = 5
delay = 1
for i in range(retries):
try:
response = requests.get(api_url, timeout=10)
response.raise_for_status()
data = response.json()
# The API returns a list of NAVs, we take the most recent one (index 0)
if "data" in data and len(data["data"]) > 0:
latest_nav_data = data["data"][0]
return {
"nav": float(latest_nav_data["nav"]),
"date": latest_nav_data["date"]
}
return None
except (requests.RequestException, ValueError, KeyError):
if i < retries - 1:
time.sleep(delay)
delay *= 2 # Exponential backoff: 1s, 2s, 4s, 8s, 16s
else:
return None
def calculate_portfolio():
# User Input Data , expand this list based
input_schemes = [
{"schemeCode": 120251,
"schemeName": "ICICI Prudential Equity & Debt Fund Direct Growth",
"Units": 100.02},
{"schemeCode": 118955,
"schemeName": "HDFC Flexi Cap Direct Plan Growth",
"Units": 250.589},
{"schemeCode": 120334,
"schemeName": "ICICI Prudential Multi Asset Fund Direct Growth",
"Units": 200.567}
]
results = []
total_portfolio_value = 0.0
print("Fetching real-time data from AMFI...")
for scheme in input_schemes:
nav_info = fetch_nav_with_retry(scheme["schemeCode"])
if nav_info:
current_value = nav_info["nav"] * scheme["Units"]
total_portfolio_value += current_value
results.append({
"Date": nav_info["date"],
"Fund Name": scheme["schemeName"],
"NAV": nav_info["nav"],
"Units": scheme["Units"],
"Investment": round(current_value, 2)
})
else:
print(f"Error: Could not fetch data for {scheme['schemeName']}")
# --- OUTPUT ---
print("\n" + "="*80)
print(f" TOTAL INVESTMENT VALUE: ₹{total_portfolio_value:,.2f}")
print("="*80)
# Header
print(f"{'Date':<12} | {'Fund Name':<50} | {'NAV':<8} | {'Units':<10} | {'Investment':<12}")
print("-" * 105)
# Rows
for r in results:
print(f"{r['Date']:<12} | {r['Fund Name'][:50]:<50} | {r['NAV']:<8.2f} | {r['Units']:<10.3f} | ₹{r['Investment']:<12,.2f}")
if __name__ == "__main__":
calculate_portfolio()
import requests
import time
import pandas as pd
from datetime import datetime
from pathlib import Path
def fetch_nav_with_retry(scheme_code):
"""
Fetches the latest NAV for a given scheme code from the AMFI API.
Implements exponential backoff for reliability.
"""
api_url = f"https://api.mfapi.in/mf/{scheme_code}"
retries = 5
delay = 1
for i in range(retries):
try:
response = requests.get(api_url, timeout=10)
response.raise_for_status()
data = response.json()
# The API returns a list of NAVs, we take the most recent one (index 0)
if "data" in data and len(data["data"]) > 0:
latest_nav_data = data["data"][0]
return {
"nav": float(latest_nav_data["nav"]),
"date": latest_nav_data["date"]
}
return None
except (requests.RequestException, ValueError, KeyError):
if i < retries - 1:
time.sleep(delay)
delay *= 2 # Exponential backoff
else:
return None
def calculate_portfolio():
# --- LOAD DATA FROM FILE ---
try:
# Update filename here if needed. Using pd.read_csv for your uploaded file.
#df = pd.read_csv('input_schemes.xlsx - Sheet1.csv')
# Looks for the file in the same folder as your script
file_path = Path(__file__).parent / 'input_schemes.xlsx'
df = pd.read_excel(file_path, sheet_name='Sheet1')
# Convert dataframe to a list of dictionaries to maintain the loop logic
input_schemes = df.to_dict('records')
except Exception as e:
print(f"Error reading the file: {e}")
return
results = []
total_portfolio_value = 0.0
print(f"Fetching real-time data for {len(input_schemes)} funds from AMFI...")
for scheme in input_schemes:
# Note: Ensure keys match the column names in your CSV exactly
nav_info = fetch_nav_with_retry(scheme["schemeCode"])
if nav_info:
current_value = nav_info["nav"] * scheme["Units"]
total_portfolio_value += current_value
results.append({
"Date": nav_info["date"],
"Fund Name": scheme["schemeName"],
"NAV": nav_info["nav"],
"Units": scheme["Units"],
"Investment": round(current_value, 2)
})
else:
print(f"Error: Could not fetch data for {scheme['schemeName']}")
# --- OUTPUT ---
print("\n" + "="*85)
print(f" TOTAL PORTFOLIO VALUE: ₹{total_portfolio_value:,.2f}")
print("="*85)
# Header
print(f"{'Date':<12} | {'Fund Name':<40} | {'NAV':<8} | {'Units':<10} | {'Value (₹)':<12}")
print("-" * 95)
# Rows
for r in results:
print(f"{r['Date']:<12} | {r['Fund Name'][:40]:<40} | {r['NAV']:<8.2f} | {r['Units']:<10.3f} | ₹{r['Investment']:<12,.2f}")
if __name__ == "__main__":
calculate_portfolio()
Access raw master data for all mutual fund schemes including scheme codes, types, and classification categories via the official AMFI portal.
Curious how the stock prices are fetched in the background? Check out the Backend Data Logic for this monitor.
View Backend Fetching LogicReady to simplify your code? Learn how to use the mftool wrapper to fetch AMFI data even faster without manual API handling.
Instead of running the script manually each day, you can schedule it to execute automatically every evening after AMFI updates NAV data (usually by 9:00 PM IST).
Open Task Scheduler and create a new Basic Task with these settings:
# In the Task Scheduler action, set:
Program/script : python
Arguments : C:\your_folder\portfolio_tracker.py
Trigger : Daily at 21:15 (9:15 PM IST)
Or run this once in Command Prompt to register the task automatically:
:: Run this in Command Prompt (as Administrator)
schtasks /create /tn "MF NAV Tracker" /tr "python C:\your_folder\portfolio_tracker.py" /sc daily /st 21:15
Open your crontab with crontab -e and add this line.
It runs the script every weekday (Mon–Fri) at 9:15 PM IST.
# crontab -e
15 21 * * 1-5 /usr/bin/python3 /home/youruser/portfolio_tracker.py >> /home/youruser/nav_log.txt 2>&1
The >> nav_log.txt 2>&1 part saves all output and errors
to a log file so you can review what ran each night.
schedule library (cross-platform)
If you prefer a pure Python approach that works on any OS,
install the schedule library and wrap your function:
pip install schedule
import schedule
import time
def run_tracker():
# Call the function from Part IV or Part V
calculate_portfolio()
# Schedule to run every weekday at 9:15 PM
schedule.every().monday.at("21:15").do(run_tracker)
schedule.every().tuesday.at("21:15").do(run_tracker)
schedule.every().wednesday.at("21:15").do(run_tracker)
schedule.every().thursday.at("21:15").do(run_tracker)
schedule.every().friday.at("21:15").do(run_tracker)
# Keep the script alive to wait for the scheduled time
while True:
schedule.run_pending()
time.sleep(60)
Keep this script running in the background (or inside a
screen / tmux session on Linux) and it will
call calculate_portfolio() automatically each weekday evening.
Author
🎥 Join me live on YouTubePassionate about coding and teaching, I publish practical tutorials on PHP, Python, JavaScript, SQL, and web development. My goal is to make learning simple, engaging, and project‑oriented with real examples and source code.