Building an API for Nepal's Stock Market (Because It Didn't Have One)
· Bhaskar Neupane

Building an API for Nepal's Stock Market (Because It Didn't Have One)

How I reverse-engineered NEPSE's authentication flows, scraped historical price data, and built a Python library that Nepali investors actually use.

pythonnepalfinanceautomationweb-scrapingopen-source

Building an API for Nepal’s Stock Market (Because It Didn’t Have One)

When I started trying to do quantitative analysis on Nepal’s stock market (NEPSE) in 2021, I assumed I’d find a public API. Every major exchange has one. NSE India has one. BSE has one.

NEPSE did not have one.

What NEPSE had was a website — built on aging infrastructure — that required manual login, session cookies, and CSRF tokens on every request. No API. No data export. Just a web UI designed for humans clicking buttons.

So I built the API myself.

The Problem in Detail

If you wanted historical prices for a Nepali stock, your options were:

  1. Go to the NEPSE website. Click through multiple pages. Download a CSV. Repeat for every stock, every date range.
  2. Log into Meroshare (the portfolio app) manually to check your IPO applications.
  3. Use the TMS (Trading Management System) for trade execution — again, manual.

None of these were programmable. For anyone trying to backtest a strategy, run a screener, or just track their portfolio automatically, this was a wall.

Reverse-Engineering the Auth Flow

The hardest part wasn’t scraping the data — it was getting authenticated.

NEPSE’s login flow:

  1. Load the login page — receive a CSRF token in the HTML
  2. POST credentials + CSRF token
  3. Receive a session cookie
  4. All subsequent requests must include the session cookie AND a fresh CSRF token (re-fetched each time)

The kicker: the CSRF token changes on every page load, not just on login. So you can’t just cache it.

class TmsClient:
    def __init__(self, username: str, password: str):
        self.session = requests.Session()
        self.session.headers.update({
            'User-Agent': 'Mozilla/5.0 ...',
        })
        self._login(username, password)

    def _get_csrf_token(self, url: str) -> str:
        response = self.session.get(url)
        soup = BeautifulSoup(response.text, 'html.parser')
        token = soup.find('input', {'name': '_token'})
        if not token:
            raise AuthError("Could not find CSRF token")
        return token['value']

    def _login(self, username: str, password: str):
        csrf = self._get_csrf_token(TMS_LOGIN_URL)
        self.session.post(TMS_LOGIN_URL, data={
            'username': username,
            'password': password,
            '_token': csrf,
        })

Once that was working, the rest was parsing HTML tables and transforming them into clean DataFrames.

What NepseTools Does

The library ended up with three main modules:

1. TMS Client — historical price data, OHLCV, sector data, floorsheet

from nepse_tools import TmsClient

client = TmsClient(username="...", password="...")
df = client.get_historical_prices("NABIL", days=365)
print(df.head())
#            date   open   high    low  close  volume
# 2022-01-01  723.0  748.0  720.0  741.0   45231

2. Meroshare Client — portfolio tracking, IPO application status, dividend history

from nepse_tools import MeroshareClient

ms = MeroshareClient(dp_id="...", username="...", password="...")
portfolio = ms.get_portfolio()
ipos = ms.get_ipo_applications()

3. Technical Indicators — RSI, MACD, Bollinger Bands, built on top of the price data

from nepse_tools.indicators import rsi, macd

prices = client.get_historical_prices("NLIC", days=200)
prices['rsi'] = rsi(prices['close'], period=14)
prices['macd'], prices['signal'] = macd(prices['close'])

What I Learned

Session management is underrated. Most web scraping tutorials show you how to make a single authenticated request. Long-running scrapers need to handle session expiry, token refresh, and retry logic. I implemented an exponential backoff with jitter because NEPSE’s servers occasionally just… don’t respond.

Rate limiting is a social contract. I’m scraping a government-adjacent system. Going too fast isn’t just rude — it could get the library’s IP range blocked, making it useless for everyone. I added configurable delays and recommended defaults in the docs.

Nepali developers need better tools. The response to this library was larger than I expected. People were using it for portfolio trackers, automated IPO applications, and market screeners. A country’s financial markets shouldn’t be inaccessible to programmers just because the exchange hasn’t prioritized an API.

Status

The library is open source on GitHub. It works as of the time of writing, but NEPSE has been gradually modernizing their infrastructure — future site changes may break things. PRs welcome.

If you’re a Nepali developer building anything in this space, reach out. I’d love to see what people are building on top of it.

— Bhaskar