Python client for AMFI India — NAV, TER, Fund Performance, Tracking Error, Risk Parameters and more
Project description
amfipy
Python client for AMFI India — the Association of Mutual Funds in India.
Provides clean, typed access to NAV data, TER (Total Expense Ratio), Fund Performance, Tracking Error, Risk Parameters, New Fund Offers, AMFI publications, and more. Both sync and async interfaces are included.
Features
- Sync + Async —
AMFIClientandAsyncAMFIClientwith identical APIs - Typed & documented — full docstrings, type hints throughout
- Polars support — optional
as_df=Truereturns a polars DataFrame (pip install amfipy[polars]) - Raw bytes default — Excel/file downloads return
bytes; save wherever you want - Historical ranges —
fetch_range()on every module for batch multi-period pulls - No heavy deps — only
httpxrequired
Installation
pip install amfipy
With polars DataFrame support:
pip install amfipy[polars]
Quick Start
from amfipy import AMFIClient
client = AMFIClient()
# Latest NAV for all funds
nav = client.nav.latest()
# TER Excel for March 2026 (all funds)
excel_bytes = client.ter.download_excel(month="03-2026")
open("ter_march_2026.xlsx", "wb").write(excel_bytes)
# Fund performance — Open Ended Equity, all AMCs
perf = client.fund_performance.fetch(category=1, sub_category=1)
# Tracking error for all funds on 31-Mar-2026
error = client.tracking.error(date="31-mar-2026")
Async
import asyncio
from amfipy import AsyncAMFIClient
async def main():
client = AsyncAMFIClient()
nav = await client.nav.latest()
excel = await client.ter.download_excel(month="03-2026")
asyncio.run(main())
Module Reference
client.nav — NAV Data
# Latest NAV — all funds
nav = client.nav.latest()
# → {"data": [{type, categories: [...]}], "count": N}
# Latest NAV — single AMC (62 = 360 ONE Mutual Fund)
nav = client.nav.latest(mf_id=62, fund_type="Open Ended")
# Category-level summary
cat = client.nav.latest_by_category(mf_id="all")
# Scheme list for an AMC (needed for history calls)
schemes = client.nav.schemes(mf_id=85)
# → [{"nav_id": "154043", "nav_name": "Abakkus Flexi Cap Fund - Direct - Growth", "MF_ID": "85"}, ...]
# All scheme NAVs for a specific date
all_navs = client.nav.all_navs_for_date(date="2026-03-31")
# Historical NAV for a scheme over a period (use nav_id from schemes() as sd_id)
hist = client.nav.history(sd_id=154043, from_date="2026-01-01", to_date="2026-03-31")
# → {"mf_name": "...", "scheme_name": "...", "date_range": "...", "nav_groups": [...]}
hist_df = client.nav.history(sd_id=154043, from_date="2026-01-01", to_date="2026-03-31", as_df=True)
# → polars DataFrame with columns: mf_name, scheme_name, nav_name, date, nav, ...
# High/Low NAV (period_type: "Month"|"Annual", nav_type: "high"|"Low"|"Both")
hl = client.nav.high_low(sd_id=154043, from_date="2026-01-01", to_date="2026-03-31")
hl = client.nav.high_low(sd_id=154043, from_date="2026-01-01", to_date="2026-03-31", period_type="Annual", nav_type="high")
# Compare NAV on two dates
cmp = client.nav.compare(sd_id=154043, date1="2026-01-01", date2="2026-03-31")
# Download full NAV flat file (all schemes, one date) — returns bytes
nav_file = client.nav.download_file(date="31-Mar-2026")
open("navs.txt", "wb").write(nav_file)
# Batch historical fetch across multiple ranges
ranges = [("2026-01-01","2026-01-31"), ("2026-02-01","2026-02-28")]
results = client.nav.fetch_range(sd_id=154043, months=ranges)
Fund type values: "" (all), "Open Ended", "Close Ended", "Interval Fund"
client.ter — TER for MF Schemes
# List available months for a financial year
months = client.ter.months(year="2025-2026")
# → [{"MonthYear": "March-2026", "MonthNumber": "03-2026"}, ...]
# Fetch TER data as JSON
data = client.ter.fetch(month="03-2026")
data = client.ter.fetch(month="03-2026", mf_id=62, category="Equity Scheme", fund_type="Open Ended")
data = client.ter.fetch(month="03-2026", as_df=True) # polars DataFrame
# Fetch latest month automatically
data = client.ter.fetch(year="2025-2026") # picks months()[0]
# Batch fetch across months
results = client.ter.fetch_range(months=["03-2026", "02-2026", "01-2026"])
# → {"03-2026": <data>, "02-2026": <data>, "01-2026": <data>}
# Download Excel (raw bytes)
excel = client.ter.download_excel(month="03-2026")
excel = client.ter.download_excel(month="03-2026", category="Equity Scheme")
open("ter.xlsx", "wb").write(excel)
# Sub-category lookup
subcats = client.ter.sub_categories(fund_type="Open Ended", category="Equity Scheme")
Category values: "-1" (All), "Equity Scheme", "Debt Scheme", "Hybrid Scheme", "Other Scheme", "Solution Oriented Scheme"
Fund type values: "-1" (All), "Open Ended", "Close Ended", "Interval Fund"
client.sif_ter — TER for SIF Schemes
Same interface as ter, but uses SIF endpoints and sif_id instead of mf_id:
months = client.sif_ter.months(year="2025-2026")
data = client.sif_ter.fetch(month="03-2026", sif_id="All")
excel = client.sif_ter.download_excel(month="03-2026")
client.fund_performance — Fund Performance
# Get all filter options (maturity types, categories, fund list)
filters = client.fund_performance.filters()
# → {"maturityTypeList": [...], "investmentTypeList": [...], "mutualFundList": [...]}
# Get sub-categories for Equity (category=1)
subcats = client.fund_performance.sub_categories(category=1)
# Fetch performance data
perf = client.fund_performance.fetch(
maturity_type=1, # 1=Open Ended, 2=Close Ended
category=1, # 1=Equity, 2=Debt, 3=Hybrid, 4=Solution Oriented, 5=Other
sub_category=1, # from sub_categories()
mf_id=0, # 0=All, or fund ID from filters()["mutualFundList"]
report_date="07-May-2026", # "DD-Mon-YYYY", defaults to last business day
)
Note: Excel export is client-side only in the AMFI website; no server download endpoint exists.
client.tracking — Tracking Error & Difference
# Tracking Error — date format: "DD-mon-YYYY" (lowercase month)
error = client.tracking.error(date="31-mar-2026")
error = client.tracking.error(date="31-mar-2026", mf_id=62)
error = client.tracking.error(date="31-mar-2026", as_df=True)
# Batch tracking error
errors = client.tracking.error_range(dates=["31-mar-2026", "28-feb-2026"])
# Tracking Difference — date format: "01-Mon-YYYY" (title-case month, always day=01)
diff = client.tracking.difference(month="01-Apr-2026")
diff = client.tracking.difference(month="01-Apr-2026", mf_id="all")
# Batch tracking difference
diffs = client.tracking.difference_range(months=["01-Apr-2026", "01-Mar-2026"])
client.risk_parameters — Risk Parameters
# date format: "01-Mon-YYYY" (always day=01)
# category_id: numeric (e.g. 17 = Mid Cap Fund)
risk = client.risk_parameters.fetch(date="01-Mar-2026", category_id=17)
risk = client.risk_parameters.fetch(date="01-Mar-2026", category_id=17, as_df=True)
# Batch
results = client.risk_parameters.fetch_range(
dates=["01-Mar-2026", "01-Feb-2026"],
category_id=17,
)
client.nfo — New Fund Offers
# Grouped by AMC
nfos = client.nfo.fetch()
# → {"NewFundOffer": [{"MutualFund": "...", "items": [{Scheme_Id, SchemeName, MF_Id}]}], "type": "summary"}
# Flat list
items = client.nfo.flat()
# → [{"Scheme_Id": 14475, "MutualFund": "...", "SchemeName": "...", "MF_Id": 53}, ...]
# As polars DataFrame
df = client.nfo.fetch(as_df=True)
client.publications — AMFI Monthly, Quarterly & Commission Disclosure
# Monthly reports — grouped by financial year
monthly = client.publications.monthly()
# Flat list of all monthly entries
flat = client.publications.monthly_flat()
# → [{"Title": "March 2026", "month": "03", "year": "2026",
# "pdf_url": "...", "excel_url": "...", "financial_year": "..."}, ...]
# Quarterly issues — grouped by financial year
quarterly = client.publications.quarterly()
flat_q = client.publications.quarterly_flat()
# → [{"issue_no": "Issue IV", "period": "(Jan - Mar 2026)", "pdf_url": "...", "excel_url": "..."}, ...]
# Commission disclosure — annual PDF list
commission = client.publications.commission()
# → [{"Period": "FY 2024 - 2025", "URL": "https://portal.amfiindia.com/spages/ACDVol-2024-2025.pdf"}, ...]
# Download any file (PDF or XLS) — returns bytes
xls_bytes = client.publications.download_file(flat[0]["excel_url"])
pdf_bytes = client.publications.download_file(commission[0]["URL"])
open("report.xls", "wb").write(xls_bytes)
client.cdmdf — CDMDF NAV
# Historical CDMDF NAV — date format: "DD-mon-YYYY" (lowercase month)
cdmdf = client.cdmdf.history(date="31-mar-2026")
cdmdf = client.cdmdf.history(date="31-mar-2026", as_df=True)
# Batch
results = client.cdmdf.history_range(dates=["31-mar-2026", "28-feb-2026"])
Note: Latest CDMDF NAV is server-rendered on the AMFI page — no JSON API exists for it.
client.aum — AUM (Assets Under Management)
Five sub-sections covering all AMFI AUM pages.
# ── Average AUM ──────────────────────────────────────────────────────────────
# List financial years (returns id + financial_year label)
fys = client.aum.financial_years()
# → [{"id": 1, "financial_year": "April 2025 - March 2026"}, ...]
# List quarters within a financial year
periods = client.aum.periods(fy_id=1)
# → {"financial_year": "...", "periods": [{"id": 1, "period": "January - March 2026"}, ...]}
# Fund-wise Average AUM for a quarter
data = client.aum.average_aum_fundwise(fy_id=1, period_id=1)
# → [{"Sr_No": "1", "MutualFundName": "...", "averageAUM": {...}}, ...]
# Scheme-wise Average AUM (str_type: "Categorywise" | "Typewise", mf_id: 0=All)
data = client.aum.average_aum_schemewise(fy_id=1, period_id=1, str_type="Categorywise", mf_id=62)
# Excel downloads (raw bytes)
excel = client.aum.average_aum_fundwise_excel(fy_id=1, period_id=1)
excel = client.aum.average_aum_schemewise_excel(fy_id=1, period_id=1, str_type="Typewise")
# ── AUM–AAUM Disclosure ──────────────────────────────────────────────────────
# Quarterly disclosure by fund category — fyId format: "2025-26", "2024-25"
disc_cat = client.aum.disclosure_by_category(fy_id="2024-25")
# → [{"Period": "(Oct - Dec 2024)", "pdfURL": "...", "excelURL": "..."}, ...]
disc_geo = client.aum.disclosure_by_geography(fy_id="2024-25")
# ── Age-wise / Folio Data ────────────────────────────────────────────────────
# Month format: "March-2026" (MonthName-YYYY)
af = client.aum.agewise_folio(month="March-2026")
# → {"data": {"investorClassification": [...], "ageWiseAUM": [...]}, "Month": "March-2026"}
af_df = client.aum.agewise_folio(month="March-2026", as_df=True) # flattens ageWiseAUM
excel = client.aum.agewise_folio_excel(month="March-2026")
# ── Classified Average AUM ───────────────────────────────────────────────────
# Date format: "01-mon-yyyy" (lowercase month, always day=01)
# e.g. "01-apr-2026", "01-mar-2026"
# List available months
dates = client.aum.classified_dates()
# → [{"date": "April-2026"}, {"date": "March-2026"}, ...]
# State-wise (mf_id: 0=All)
sw = client.aum.statewise(date="01-apr-2026", mf_id=0)
# → [{"State": "Andaman and Nicobar Islands", "LiquidSchemes": 0.86, ...}, ...]
# Scheme-category-wise
sc = client.aum.scheme_catwise(date="01-apr-2026", mf_id=0)
# Excel
excel = client.aum.statewise_excel(date="01-apr-2026")
excel = client.aum.scheme_catwise_excel(date="01-apr-2026")
# ── Bifurcation of AAUM ──────────────────────────────────────────────────────
# Date format: "DD-Mon-YYYY" e.g. "31-Mar-2026"
bif = client.aum.bifurcation(date="31-Mar-2026")
# → [{"Month_Date": "31-Mar-2026", "TotalAAUMunderDirectPlan": 3904766.42,
# "AAUMunderRegisteredAdvisers": 537599.97, "AAUMunderPMS": 89942.62,
# "AAUMunderDIYclients": 3277223.84}]
# Batch
results = client.aum.bifurcation_range(dates=["31-Mar-2026", "28-Feb-2026"])
# Excel
excel = client.aum.bifurcation_excel(date="31-Mar-2026")
Common Patterns
Polars DataFrames
Any method with as_df=True returns a polars.DataFrame. Requires pip install amfipy[polars].
import polars as pl
from amfipy import AMFIClient
client = AMFIClient()
df = client.nav.history(sd_id=154043, from_date="2026-01-01", to_date="2026-03-31", as_df=True)
print(df.head())
Async batch fetch
import asyncio
from amfipy import AsyncAMFIClient
async def fetch_all():
client = AsyncAMFIClient()
ter, nav, nfo = await asyncio.gather(
client.ter.download_excel(month="03-2026"),
client.nav.all_navs_for_date(date="2026-03-31"),
client.nfo.flat(),
)
return ter, nav, nfo
asyncio.run(fetch_all())
Custom httpx settings (proxy, SSL, timeout)
All clients accept **httpx_kwargs forwarded to httpx.Client / httpx.AsyncClient:
from amfipy import AMFIClient
client = AMFIClient(
proxies={"https://": "http://myproxy:8080"},
verify=False,
timeout=120,
)
Financial Year Reference
AMFI uses financial years in YYYY-YYYY format (e.g. "2025-2026" = April 2025 – March 2026).
# Available months for the current year
months = client.ter.months(year="2025-2026")
# [{"MonthYear": "March-2026", "MonthNumber": "03-2026"}, ...]
AMC IDs (partial list)
| ID | AMC |
|---|---|
| 62 | 360 ONE Mutual Fund |
| 85 | Abakkus Mutual Fund |
| 3 | Aditya Birla Sun Life Mutual Fund |
| 0 | All (use "all" for NAV, "All" for TER) |
Use client.fund_performance.filters() to get the full list with IDs.
client.research — Categorisation of Stocks
Data published bi-annually (Jan–Jun, Jul–Dec) from 2017 onwards. Served via the Next.js RSC page — no standalone JSON API exists.
# Dict keyed by year: {year: [{period, pdfUrl, excelUrl}]}
data = client.research.categorisation_of_stocks()
# data["2024"] → [{"period": "Jul - Dec", "pdfUrl": "...", "excelUrl": "..."}, ...]
# Flat list sorted by year
flat = client.research.categorisation_of_stocks_flat()
# → [{"year": "2017", "period": "Jul - Dec", "pdfUrl": "...", "excelUrl": "..."}, ...]
# polars DataFrame (year, period, pdfUrl, excelUrl columns)
df = client.research.categorisation_of_stocks(as_df=True)
# Download a file (PDF or Excel)
pdf = client.research.download_categorisation_file(flat[-1]["pdfUrl"])
open("categ_latest.pdf", "wb").write(pdf)
client.other_data — Investor Complaints, Directors, Dividends & More
# ── Investor Complaints ──────────────────────────────────────────────────────
# Monthly report (Part 1-4: A, B, C, D)
# month format: "MonthName-YYYY" (e.g. "March-2026")
complaints = client.other_data.investor_complaints_monthly(
month="March-2026",
mf_id=62, # 0 = All AMCs
part=1, # 1=Part A, 2=Part B, 3=Part C, 4=Part D
)
complaints_df = client.other_data.investor_complaints_monthly(
month="March-2026", mf_id=62, part=1, as_df=True
)
# Yearly report — financial year string "YYYY-YYYY", covers up to FY 2021
yearly = client.other_data.investor_complaints_yearly(year="2019-2020", mf_id=62)
# ── AMC Directors & Trustees ─────────────────────────────────────────────────
directors = client.other_data.amc_directors(mf_id=62) # 0 = All AMCs
trustees = client.other_data.trustees(mf_id=62)
directors_df = client.other_data.amc_directors(mf_id=0, as_df=True)
# ── Group Companies ──────────────────────────────────────────────────────────
# One page (default size=10)
page = client.other_data.group_companies(page=1, size=10)
# → {"data": [...], "totalCount": 599}
# All records (auto-paginate)
all_cos = client.other_data.group_companies_all() # list
all_df = client.other_data.group_companies_all(as_df=True) # polars DataFrame
# ── Scheme Dividends ─────────────────────────────────────────────────────────
# Step 1: get scheme list for an AMC (required to obtain sd_id)
schemes = client.other_data.populate_schemes(mf_id=62)
# → [{"scheme_id": "13771", "scheme_name": "..."}, ...]
# Step 2: fetch dividend history
divs = client.other_data.scheme_dividends(
mf_id=62,
sd_id=schemes[0]["scheme_id"],
year=2026,
)
divs_df = client.other_data.scheme_dividends(
mf_id=62, sd_id=schemes[0]["scheme_id"], year=2026, as_df=True
)
# ── AMC Account Disclosures ──────────────────────────────────────────────────
# Returns list of dicts with external URL references (not raw financials)
ann_links = client.other_data.accounts_annual(mf_id=62)
half_links = client.other_data.accounts_half_yearly(mf_id=62)
# ── Scheme Data & Details ─────────────────────────────────────────────────────
# Step 1: get scheme list for an AMC
schemes = client.other_data.populate_schemes(mf_id=62)
sd_id = schemes[0]["scheme_id"]
# NAV details for all plan/option variants (Direct Growth, Regular IDCW, etc.)
nav_rows = client.other_data.scheme_data(mf_id=62, sd_id=sd_id)
# → [{"Scheme_NAV_Name": "...", "ISIN_Div_Payout_ISIN_Growth": "...",
# "Net_Asset_Value": "13.32", "Date": "2026-05-08T...", ...}, ...]
# Full scheme profile
info = client.other_data.scheme_details(mf_id=62, sd_id=sd_id)
# → {"Scheme_Name": "...", "Scheme_Objective": "...", "SchemeType_Desc": "Open Ended",
# "SchemeCat_Desc": "Hybrid Scheme - ...", "Scheme_min_amt": 1000,
# "Launch_Date": "...", "Scheme_load": "...", "AMC_Website": "..."}
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file amfipy-0.2.0.tar.gz.
File metadata
- Download URL: amfipy-0.2.0.tar.gz
- Upload date:
- Size: 27.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7b32ccd10c5a679b4968c9a879cbcf4f35a6761d17d9fecbb5a8d31e636abba8
|
|
| MD5 |
9e6b71ee1abec2d0edbfa8c296b76330
|
|
| BLAKE2b-256 |
17863a9b85389bba51b0dfb92f7a680bb50576c38ba85be3b3413fc344fda686
|
File details
Details for the file amfipy-0.2.0-py3-none-any.whl.
File metadata
- Download URL: amfipy-0.2.0-py3-none-any.whl
- Upload date:
- Size: 33.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9391661d3980bba3aa3ee474b6810979b0f39823a1539196921c2cdbeb6bff04
|
|
| MD5 |
8c33ff682b42b7cf599e746578f2db68
|
|
| BLAKE2b-256 |
fa67d6c44635ecf933b37cef19fa455b87837637712dd6e483367535a00e5e1b
|