Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/georgejohnsonoff-business/spendwiselygeorge/llms.txt

Use this file to discover all available pages before exploring further.

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