Skip to main content

Overview

The Holdings API manages your mutual fund portfolio by storing scheme codes and unit quantities. Holdings are persisted in a local JSON file and used by the Portfolio API for valuation calculations.

Get Holdings

/api/holdings
GET
Retrieves all stored mutual fund holdings

Example Request

curl http://localhost:8000/api/holdings

Response

Returns an array of holding objects:
scheme_code
string
AMFI scheme code identifying the mutual fund
units
number
Number of units held in this fund

Example Response

[
  {
    "scheme_code": "120503",
    "units": 150.5
  },
  {
    "scheme_code": "119551",
    "units": 75.25
  },
  {
    "scheme_code": "118989",
    "units": 200.0
  }
]

Empty Holdings

When no holdings are configured or file doesn’t exist:
[]

Set Holdings

This endpoint replaces all existing holdings. It does not merge or append.
/api/holdings
POST
Replaces all holdings with the provided list

Request Body

Array of holding objects:
scheme_code
string
required
AMFI scheme code for the mutual fund
units
number
required
Number of units held (supports decimals)

Example Request

curl -X POST http://localhost:8000/api/holdings \
  -H "Content-Type: application/json" \
  -d '[
    {
      "scheme_code": "120503",
      "units": 150.5
    },
    {
      "scheme_code": "119551",
      "units": 75.25
    },
    {
      "scheme_code": "118989",
      "units": 200.0
    }
  ]'

Response

status
string
Returns "updated" when holdings are successfully saved
{
  "status": "updated"
}

Data Storage

File Location

Holdings are stored in:
./holdings.json

File Format

[
  {
    "scheme_code": "120503",
    "units": 150.5
  },
  {
    "scheme_code": "119551",
    "units": 75.25
  }
]

File Initialization

  • If holdings.json doesn’t exist, GET returns []
  • POST creates the file automatically
  • Invalid JSON in existing file returns []

Data Validation

Pydantic Model

The API uses this Pydantic model for validation:
class Holding(BaseModel):
    scheme_code: str
    units: float

Validation Rules

scheme_code
string
required
Must be a non-empty string
units
float
required
Must be a valid number (integer or decimal)

Invalid Request Examples

Missing required field:
[
  {
    "scheme_code": "120503"
    // Missing "units"
  }
]
Response: 422 Unprocessable Entity Invalid data type:
[
  {
    "scheme_code": "120503",
    "units": "not-a-number"
  }
]
Response: 422 Unprocessable Entity

Common Use Cases

Initial Setup

Add your first holdings:
curl -X POST http://localhost:8000/api/holdings \
  -H "Content-Type: application/json" \
  -d '[
    {"scheme_code": "120503", "units": 100.0},
    {"scheme_code": "119551", "units": 50.0}
  ]'

Add New Fund

  1. Get current holdings
  2. Add new fund to array
  3. POST complete updated array
const current = await fetch('http://localhost:8000/api/holdings').then(r => r.json());
current.push({ scheme_code: "118989", units: 75.5 });

await fetch('http://localhost:8000/api/holdings', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(current)
});

Update Units

  1. Get current holdings
  2. Modify units for specific fund
  3. POST updated array
const holdings = await fetch('http://localhost:8000/api/holdings').then(r => r.json());
const fund = holdings.find(h => h.scheme_code === "120503");
if (fund) {
  fund.units = 200.5; // Update units
}

await fetch('http://localhost:8000/api/holdings', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(holdings)
});

Remove Fund

  1. Get current holdings
  2. Filter out unwanted fund
  3. POST filtered array
let holdings = await fetch('http://localhost:8000/api/holdings').then(r => r.json());
holdings = holdings.filter(h => h.scheme_code !== "119551");

await fetch('http://localhost:8000/api/holdings', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(holdings)
});

Clear All Holdings

curl -X POST http://localhost:8000/api/holdings \
  -H "Content-Type: application/json" \
  -d '[]'

Scheme Codes

What is a Scheme Code?

AMFI (Association of Mutual Funds in India) assigns unique codes to each mutual fund scheme:
  • 120503: Axis Bluechip Fund - Direct Plan - Growth
  • 119551: SBI Small Cap Fund - Direct Plan - Growth
  • 118989: HDFC Mid-Cap Opportunities Fund - Direct Plan - Growth

Finding Scheme Codes

Use MFApi to search:
# Search by fund name
curl "https://api.mfapi.in/mf/search?q=axis%20bluechip"
Example response:
[
  {
    "schemeCode": "120503",
    "schemeName": "Axis Bluechip Fund - Direct Plan - Growth"
  }
]
Visit MFApi documentation for complete scheme listings.

Integration with Portfolio API

Holdings are used by /api/portfolio to calculate values:
1

Store Holdings

POST holdings via /api/holdings
2

Calculate Portfolio

GET /api/portfolio reads holdings and fetches NAV data
3

Display Value

Portfolio response includes current value for each holding

Example Flow

// 1. Set holdings
await fetch('http://localhost:8000/api/holdings', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify([
    { scheme_code: "120503", units: 150.5 },
    { scheme_code: "119551", units: 75.25 }
  ])
});

// 2. Get portfolio value
const portfolio = await fetch('http://localhost:8000/api/portfolio')
  .then(r => r.json());

console.log(`Total Value: ₹${portfolio.total_value}`);
// Output: Total Value: ₹16715.29

Data Persistence

File Operations

Load (GET):
if os.path.exists(HOLDINGS_FILE):
    with open(HOLDINGS_FILE, "r") as f:
        return json.load(f)
return []
Save (POST):
with open(HOLDINGS_FILE, "w") as f:
    json.dump(holdings, f)

Error Handling

  • File not found: Returns empty array
  • Invalid JSON: Returns empty array
  • Write failure: Raises exception (500 error)
No backup is created. Consider implementing versioning for production use.

Best Practices

Decimal Precision

Mutual fund units often have high precision:
{
  "scheme_code": "120503",
  "units": 150.523456  // Up to 6 decimal places
}

Scheme Code Format

Always use string type for scheme codes:
// ✅ Correct
{"scheme_code": "120503", "units": 100}

// ❌ Wrong (number type)
{"scheme_code": 120503, "units": 100}

Validation Before POST

Validate holdings before saving:
function validateHolding(holding) {
  return (
    typeof holding.scheme_code === 'string' &&
    holding.scheme_code.length > 0 &&
    typeof holding.units === 'number' &&
    holding.units >= 0
  );
}

const holdings = [...];
if (holdings.every(validateHolding)) {
  // Safe to POST
}

Error Responses

ScenarioStatusResponse
Invalid JSON format422Validation error details
Missing required field422Validation error details
Wrong data type422Validation error details
File write failure500Server error

Example Validation Error

{
  "detail": [
    {
      "loc": ["body", 0, "units"],
      "msg": "field required",
      "type": "value_error.missing"
    }
  ]
}

Next Steps

View Portfolio

Calculate current value of your holdings

API Overview

Learn about API architecture and response formats

Build docs developers (and LLMs) love