# finnhub-python is a thin client over one hosted market data API

> The finnhub-python package gives Python programmers a named method for each endpoint of the Finnhub financial data service, from stock candles and fundamentals to ETF holdings, SEC filings and crypto symbols. It is a small, mostly generated wrapper with one runtime dependency, and it hands you back raw dictionaries rather than typed models.

**Finnhub-Stock-API/finnhub-python** — Finnhub Python API Client. Finnhub API provides institutional-grade financial data to investors, fintech startups and investment firms. We support real-time stock price, global fundamentals, global ETFs holdings and alternative data. https://finnhub.io/docs/api

- Repository: https://github.com/Finnhub-Stock-API/finnhub-python
- Website: https://finnhub.io/
- Stars: 1,092 · Forks: 156
- Language: Python
- License: Apache-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/finnhub-stock-api-finnhub-python

## One method per endpoint, and nothing else

finnhub-python is the Python client for the Finnhub API, and the fastest way to understand it is to look at how little it does. You create a client with an API key, and every method on that client corresponds to one endpoint of the service.

The coverage is broad. There are the market data calls, stock_candles for time series, quote for a price, stock_tick and stock_nbbo for tick and bid ask data, and the same for forex and crypto with forex_candles and crypto_candles. There are the fundamentals, company_basic_financials, financials, financials_reported, company_eps_estimates, company_ebit_estimates, company_ebitda_estimates and company_revenue_estimates. There is the reference data, stock_symbols, country, economic_code, crypto_exchanges, forex_exchanges and symbol_lookup.

And there is the part that distinguishes the service from a plain price feed. There are filings and international_filings, press_releases, transcripts and transcripts_list for earnings calls, sec_sentiment_analysis and sec_similarity_index, news_sentiment and stock_social_sentiment, company_esg_score, company_earnings_quality_score, stock_supply_chain, stock_revenue_breakdown, stock_investment_theme, pattern_recognition, support_resistance, technical_indicator and aggregate_indicator. There are ETF endpoints for profile, holdings, sector exposure and country exposure, mutual fund equivalents of the same, an IPO calendar, an earnings calendar, an economic calendar, an FDA calendar, insider transactions, patent data, and a covid19 endpoint that is presumably there for historical reasons.

The audience is the developer building on market data: a fintech product, a research notebook, a backtesting script, a price alerting service. The data itself is the product Finnhub sells, and the API documentation at finnhub.io/docs/api is the reference for what each method actually returns.

## Installing and the first call

The installation is a single pip command:

```bash
pip install finnhub-python
```

The client needs an API key from Finnhub, and the getting started section shows the shape of every call that will follow. The setup is one line, and the rest is method calls on the client object:

```python
import finnhub

# Setup client
finnhub_client = finnhub.Client(api_key="YOUR API KEY")

# Stock candles
res = finnhub_client.stock_candles('AAPL', 'D', 1590988249, 1591852249)
print(res)

#Convert to Pandas Dataframe
import pandas as pd
print(pd.DataFrame(res))
```

The candles example is worth a second look because it shows the data shape. The arguments are a symbol, a resolution of 'D' for daily, and a from and to pair as Unix timestamps. The result is printed directly, and then handed to pandas as a DataFrame. The client is not converting anything for you, the raw response already comes back in a tabular structure, and pandas is how you are expected to work with it.

The second thing the example shows is the dependency situation. pandas is not a dependency of the client, it is something you import yourself if you want to. The package itself installs only requests, and the setup script declares a single requirement, requests at 2.22.0 or newer. The requirements file adds one more line, the future package, but only for Python 2.7 and below.

So the client will not drag a numerical stack into your environment. That matters if you are adding it to a service that already has its own data tooling, and it is the main reason the package is small enough to depend on without thinking about it.

## The method names are the API documentation

Because the readme is essentially a list of one line calls, the fastest way to learn the surface is to scan the list, and a few details stand out.

The first is the keyword clash. The readme notes it explicitly: to use company_news you need _from instead of from, to avoid a conflict with the Python keyword. The same convention shows up everywhere a date range is needed, in filings, press_releases, upgrade_downgrade, stock_dividends, stock_splits, ipo_calendar, earnings_calendar and the rest. A leading underscore for a parameter that shadows a keyword is a slightly ugly workaround, but it is consistent, and consistency is what makes a large generated surface usable.

The second is that many endpoints accept more than one identifier. company_profile can be called with a symbol, with an isin, or with a cusip, and the readme shows all three forms. The same pattern appears for etfs_profile and mutual_fund_profile, which take a ticker or an isin, and for etfs_holdings and mutual_fund_holdings, which take a symbol, an isin or a date, and also accept a skip parameter for paging.

The third is that parameters are positional in the short examples and keyword in the longer ones, and the keyword forms are the ones to copy when you want to be explicit. technical_indicator, for example, is shown with symbol, resolution, _from, to, indicator and an indicator_fields dictionary.

None of this is a framework. There is no session object with retries, no pagination helper, no schema validation, no async variant. The client is a named function per endpoint, and everything else is in the service.

## A generated client, and two version numbers that disagree

The repository layout explains the shape of the code. The package source is in a finnhub directory, and next to it are the pieces of a standard Python distribution: setup.py, setup.cfg, requirements.txt, test-requirements.txt and tox.ini. There is also a.openapi-generator directory and a.openapi-generator-ignore file, which is the clearest signal in the tree. The client is generated from an OpenAPI description of the service, so the method names, the parameters and the argument order follow a machine produced contract rather than a human API design.

That is the right call for a service with this many endpoints. It also explains the surface exactly: if the OpenAPI document says an endpoint takes a symbol, a from and a to, you get three parameters in that order, and the readme is a rendering of the same document.

The cost of generation shows up in the version numbers. The readme states the package version as 2.4.25, while setup.py declares VERSION as 2.4.29. Both are in the repository, and they disagree. The readme also states the API version separately as 1.0.0, which is a third number and refers to the service rather than to the client.

So if you are pinning a version, pin the one pip actually installs rather than the one quoted in the documentation, and check the CHANGELOG, which the readme points at specifically for anyone arriving from version 1 of the client. The migration from version 1 evidently changed the calling convention, and the changelog is the only account of it.

The CI configuration also dates the project. There is a.travis.yml and a.gitlab-ci.yml rather than a GitHub Actions workflow, and the root contains release.sh and git_push.sh, which is the shape of a release run from a developer's machine rather than from a modern pipeline.

## No models, no error handling, no rate limit logic

Three things are absent from the client, and they are the three things you will write yourself.

The first is typed response objects. Every method returns the parsed JSON, so a candle series comes back as a structure you immediately hand to pandas. There is no dataclass for a company profile, no enum for a resolution, and no type hint in the readme that tells you what a given method returns. You learn the shape from the API documentation and from looking at the data.

The second is error handling. The readme examples wrap every call in a print, and there is no documented behaviour for a bad symbol, a missing entitlement, or a request that exceeds what your plan allows. Whether a failed call raises, returns an error payload, or returns None is something you discover by using it, and the client documentation does not say.

The third is rate limiting and retries. This is where a market data client usually earns its keep, because the free tiers of these services are aggressively limited and the honest engineering answer is to back off and retry. The readme says nothing about that, so if you need it you are writing a wrapper, and at that point you are maintaining code that the upstream client does not.

There is also a dependency you should know about. The client calls a hosted service, so your program needs network access, an API key, and an account with Finnhub whose plan covers the endpoints you call. A free key is enough to try the library, but the breadth of the endpoint list is bounded by what your plan includes, and the client will not tell you which endpoints your key can reach.

## Apache 2.0, no tagged releases, and a client you are meant to read

The licence is Apache 2.0, declared in setup.py and shipped in the LICENSE file, which is a permissive licence with an express patent grant and no conditions on commercial use.

The repository is not archived and the last push was on 2026-06-24. There are no GitHub releases, so there is no tag to pin and no release notes page beyond the CHANGELOG.md at the root. For a package that is installed by name from PyPI, that means the version in your lock file is the only handle you have.

The dependency footprint is one package, requests, which is as easy to vendor as a client gets. There is a tox.ini and a test-requirements.txt, so the project does have a test setup, and an examples.py file at the root for the longer examples.

The strategic question is whether you want this client at all. If you are already paying for Finnhub, or you are evaluating it, the client saves you from hand building request URLs and remembering the parameter names for dozens of endpoints, and the price is one dependency and an API key. If your application needs several market data providers behind one interface, with typed models and a normalised error layer, this is the wrong level of abstraction, because the method names are Finnhub's, and the shapes you get back are Finnhub's.

The last thing to check before you commit is the free tier. The client is free, the service is not, and the endpoints you can actually use depend on the plan you sign up for rather than on anything in this repository.

## Conclusion

finnhub-python is worth adopting if you already pay for Finnhub or intend to trial it, because the client saves you from hand building URLs and remembering parameter names for dozens of endpoints, and its surface is small enough to read in one sitting. It is the wrong choice if you want typed response objects, if you need to handle rate limits and retries yourself without writing that layer, or if you would rather talk to several data providers through one abstraction. Before you depend on it, read the CHANGELOG if you are coming from version 1, check which package version you actually get, since the readme quotes 2.4.25 while setup.py declares 2.4.29, and note that the repository was pushed to on 2026-06-24 and publishes no tagged releases.

## FAQ

### How do I install and start using finnhub-python?

Run pip install finnhub-python, then import finnhub and create a client with finnhub.Client(api_key="YOUR API KEY"). Every endpoint is a method on that client, for example finnhub_client.stock_candles('AAPL', 'D', 1590988249, 1591852249), and the raw response can be passed straight into pandas.

### What Python dependencies does finnhub-python need?

Only requests, at 2.22.0 or newer. The requirements file adds the future package but only for Python 2.7 and below, and pandas is not a dependency, it is an optional import in the examples.

### Why do some finnhub-python methods use _from instead of from?

Because from is a Python keyword, so a parameter with that name would be a syntax error. The readme notes this explicitly for company_news, and the same leading underscore convention is used by every endpoint that takes a date range.

### Is the finnhub-python client generated code?

The repository contains an .openapi-generator directory and an .openapi-generator-ignore file, so the client is produced from an OpenAPI description of the Finnhub service. The readme quotes the package version as 2.4.25 while setup.py declares 2.4.29, and the API version is listed separately as 1.0.0.

### Does finnhub-python handle rate limits and errors?

The documentation does not describe any error handling, retry behaviour or rate limit logic, and the examples simply print the result of each call. Anything beyond the raw request has to be written by the application.

## Sources

- [Finnhub-Stock-API/finnhub-python on GitHub](https://github.com/Finnhub-Stock-API/finnhub-python)
- [Issues](https://github.com/Finnhub-Stock-API/finnhub-python/issues)
- [License: Apache-2.0](https://github.com/Finnhub-Stock-API/finnhub-python/blob/master/LICENSE)
- [Project website](https://finnhub.io/)
- [README](https://github.com/Finnhub-Stock-API/finnhub-python/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/finnhub-stock-api-finnhub-python
