Skip to main content

Overview

SpendWisely George integrates with MFapi.in to provide:
  • Real-time NAV (Net Asset Value) for Indian mutual funds
  • Portfolio valuation based on user holdings
  • Scheme metadata including fund names and details
MFapi.in is a free, public API maintained by the community. It aggregates data from AMFI (Association of Mutual Funds in India).

API Endpoint

Get Scheme Details

GET https://api.mfapi.in/mf/{scheme_code}
Parameters:
  • scheme_code (required) - AMFI scheme code (e.g., 119551 for HDFC Balanced Advantage Fund)
Response:
{
  "meta": {
    "scheme_type": "Hybrid Scheme - Balanced Advantage",
    "scheme_category": "Hybrid Scheme",
    "scheme_code": 119551,
    "scheme_name": "HDFC Balanced Advantage Fund - Direct Plan - Growth Option"
  },
  "data": [
    {
      "date": "15-03-2024",
      "nav": "425.789"
    },
    {
      "date": "14-03-2024",
      "nav": "424.123"
    }
  ],
  "status": "SUCCESS"
}

Implementation

Portfolio Calculation

The backend fetches NAV for each holding and calculates total portfolio value:
@app.get("/api/portfolio")
def get_portfolio():
    total_value = 0
    portfolio = []
    
    holdings = load_holdings()  # From holdings.json
    
    for holding in holdings:
        code = holding["scheme_code"]
        units = float(holding["units"])
        
        try:
            # Fetch latest NAV from MFapi.in
            resp = requests.get(f"https://api.mfapi.in/mf/{code}")
            data = resp.json()
            
            if data and "data" in data and len(data["data"]) > 0:
                nav = float(data["data"][0]["nav"])  # Latest NAV
                fund_name = data["meta"]["scheme_name"]
                value = units * nav
                
                portfolio.append({
                    "scheme_code": code,
                    "scheme_name": fund_name,
                    "units": units,
                    "nav": nav,
                    "current_value": round(value, 2)
                })
                total_value += value
        except Exception as e:
            print(f"Error fetching {code}: {e}")
            
    return {
        "portfolio": portfolio,
        "total_value": round(total_value, 2)
    }
Implementation: server.py:201-234

Holdings Management

Users configure their mutual fund holdings via the settings panel.

Data Structure

Holdings are stored in holdings.json:
[
  {
    "scheme_code": "119551",
    "units": 12.456
  },
  {
    "scheme_code": "120503",
    "units": 8.923
  }
]

API Endpoints

Get Holdings

GET /api/holdings
Response:
[
  {
    "scheme_code": "119551",
    "units": 12.456
  }
]
Implementation: server.py:136-138

Update Holdings

POST /api/holdings
Content-Type: application/json

[
  {
    "scheme_code": "119551",
    "units": 15.789
  },
  {
    "scheme_code": "120503",
    "units": 10.234
  }
]
Response:
{
  "status": "updated"
}
Implementation: server.py:140-144

Finding Scheme Codes

Search by Fund Name

MFapi.in provides a search endpoint:
GET https://api.mfapi.in/mf/search?q={query}
Example:
curl "https://api.mfapi.in/mf/search?q=HDFC%20Balanced"
Response:
[
  {
    "schemeCode": 119551,
    "schemeName": "HDFC Balanced Advantage Fund - Direct Plan - Growth"
  },
  {
    "schemeCode": 100541,
    "schemeName": "HDFC Balanced Advantage Fund - Regular Plan - Growth"
  }
]
This endpoint is not implemented in the current George version but can be added for better UX.

Common Scheme Codes

Popular mutual funds:
Fund NameScheme Code
HDFC Balanced Advantage Fund - Direct Growth119551
SBI Bluechip Fund - Direct Growth120503
Axis Midcap Fund - Direct Growth120830
Parag Parikh Flexi Cap Fund - Direct Growth122639
ICICI Prudential Bluechip Fund - Direct Growth120716

Frontend Integration

Fetch Portfolio

async function fetchPortfolio() {
  try {
    const res = await fetch('/api/portfolio');
    const data = await res.json();
    
    if (data.total_value) {
      appData.portfolioValue = data.total_value;
      renderUI(); // Update "Investments" card
    }
  } catch (e) {
    console.error("Portfolio Fetch Error", e);
  }
}
Implementation: index.html:558-571

Add Holdings

let currentHoldings = [];

function addHolding() {
  const code = document.getElementById('mfCode').value;
  const units = parseFloat(document.getElementById('mfUnits').value);
  
  if (code && units) {
    currentHoldings.push({ scheme_code: code, units: units });
    renderHoldings();
    
    // Clear inputs
    document.getElementById('mfCode').value = '';
    document.getElementById('mfUnits').value = '';
  }
}

function renderHoldings() {
  const list = document.getElementById('mfList');
  list.innerHTML = currentHoldings.map((h, i) =>
    `<div class="flex justify-between text-xs bg-white p-2 rounded border">
      <span class="font-mono">${h.scheme_code}</span>
      <span>${h.units} units</span>
      <button onclick="removeHolding(${i})" class="text-red-500">x</button>
    </div>`
  ).join('');
}
Implementation: index.html:641-662

Save Holdings

async function saveHoldings() {
  await fetch('/api/holdings', {
    method: 'POST',
    body: JSON.stringify(currentHoldings),
    headers: { 'Content-Type': 'application/json' }
  });
  
  alert("Holdings Saved!");
  fetchPortfolio(); // Refresh portfolio value
}
Implementation: index.html:664-671

UI Components

Settings Panel

Users manage holdings in the settings drawer:
<div class="p-4 bg-green-50 rounded-2xl border border-green-100">
  <h3 class="font-bold text-green-900 mb-2">Mutual Fund Holdings</h3>
  
  <!-- Display current holdings -->
  <div id="mfList" class="space-y-2 mb-2"></div>
  
  <!-- Add new holding -->
  <div class="flex gap-2">
    <input type="text" id="mfCode" placeholder="Scheme Code"
      class="flex-1 p-3 bg-white rounded-xl text-sm">
    <input type="number" id="mfUnits" placeholder="Units"
      class="w-20 p-3 bg-white rounded-xl text-sm">
  </div>
  
  <button onclick="addHolding()" class="w-full mt-2 bg-green-600 text-white p-3 rounded-xl font-bold text-sm">
    Add Holding
  </button>
  
  <button onclick="saveHoldings()" class="w-full mt-2 bg-slate-900 text-white p-3 rounded-xl font-bold text-sm">
    Save & Update
  </button>
</div>
Implementation: index.html:249-265

Portfolio Display

Total portfolio value is shown on the main dashboard:
<div class="card p-4 bg-white">
  <p class="text-[10px] font-bold text-slate-400 uppercase tracking-widest mb-1">
    Investments
  </p>
  <h3 id="uiPortfolio" class="text-xl font-black text-slate-800">₹0</h3>
  <p class="text-[10px] text-green-500 font-bold mt-1">MFapi.in Linked</p>
</div>
Implementation: index.html:151-156

Data Caching

MFapi.in NAV data is updated once daily (after market close). Implement caching to avoid redundant API calls.
Recommended caching strategy:
import time
from functools import lru_cache

@lru_cache(maxsize=100)
def fetch_nav_cached(scheme_code: str, cache_key: str):
    """Cache NAV for 24 hours using date as cache key"""
    resp = requests.get(f"https://api.mfapi.in/mf/{scheme_code}")
    return resp.json()

@app.get("/api/portfolio")
def get_portfolio():
    cache_key = time.strftime("%Y-%m-%d")  # Daily cache
    
    for holding in holdings:
        data = fetch_nav_cached(holding["scheme_code"], cache_key)
        # Process data...

Rate Limits

MFapi.in does not enforce strict rate limits but recommends:
  • Max 100 requests/minute per IP
  • Avoid excessive polling - NAV updates once daily
  • Implement caching - Cache responses for 24 hours
The API is community-maintained and free. Please use responsibly to keep it available for everyone.

Error Handling

Invalid Scheme Code

try:
    resp = requests.get(f"https://api.mfapi.in/mf/{code}")
    data = resp.json()
    
    if not data or "data" not in data or len(data["data"]) == 0:
        print(f"No data found for scheme {code}")
        continue
        
    nav = float(data["data"][0]["nav"])
except Exception as e:
    print(f"Error fetching {code}: {e}")
    # Skip this holding and continue
Implementation: server.py:208-229

Network Errors

import requests
from requests.exceptions import Timeout, ConnectionError

try:
    resp = requests.get(
        f"https://api.mfapi.in/mf/{code}",
        timeout=5  # 5 second timeout
    )
    resp.raise_for_status()  # Raise for 4xx/5xx
except Timeout:
    print("MFapi.in request timeout")
except ConnectionError:
    print("Network error connecting to MFapi.in")
except Exception as e:
    print(f"Unexpected error: {e}")

Alternative APIs

If MFapi.in is unavailable, consider:
APIFeaturesCost
RapidAPI - Mutual Funds IndiaNAV, historical dataFreemium
AMFI DirectOfficial AMFI dataFree (manual parsing)
NSE/BSE APIsMarket data for ETFsRegistration required

Best Practices

1. Validate Scheme Codes

function addHolding() {
  const code = document.getElementById('mfCode').value;
  
  // Validate format (AMFI codes are 6 digits)
  if (!/^\d{6}$/.test(code)) {
    alert("Scheme code must be 6 digits");
    return;
  }
  
  // Validate against API before saving
  fetch(`https://api.mfapi.in/mf/${code}`)
    .then(res => res.json())
    .then(data => {
      if (data.status === "SUCCESS") {
        currentHoldings.push({ scheme_code: code, units: units });
      } else {
        alert("Invalid scheme code");
      }
    });
}

2. Handle Stale Data

from datetime import datetime

data = resp.json()
latest = data["data"][0]
date_str = latest["date"]  # "15-03-2024"
date_obj = datetime.strptime(date_str, "%d-%m-%Y")

if (datetime.now() - date_obj).days > 5:
    print(f"Warning: NAV data is {(datetime.now() - date_obj).days} days old")

3. Display Metadata

// Show fund name, not just code
portfolio.forEach(holding => {
  console.log(`${holding.scheme_name}: ₹${holding.current_value}`);
});

Troubleshooting

”Error fetching scheme” Message

  1. Verify Scheme Code: Check code is 6 digits and valid
  2. Test API Directly: curl https://api.mfapi.in/mf/119551
  3. Check Network: Ensure server has internet access

Portfolio Not Updating

  1. Check Holdings Saved: Verify holdings.json exists and has data
  2. Refresh Page: Click “REFRESH” button to trigger portfolio fetch
  3. Check Logs: Look for errors in server console

Incorrect Valuation

  1. Verify Units: Ensure decimal precision is preserved (e.g., 12.456, not 12)
  2. Check NAV Date: Latest NAV might be from previous trading day
  3. Recalculate Manually: units * NAV = current value

Build docs developers (and LLMs) love