How to Use Advanced Growth Filters in the Screener API
Some screener filters are simple: send a minimum market cap, a country, or a sector. Growth filters are different. They let you ask a more specific question: which metric should grow, over what horizon, measured how, and by how much?
Once you understand the pattern, you can build surprisingly precise screens with just a few lines of JSON. This article focuses on incomeStatementGrowth because it is the clearest way to learn the structure. Once that clicks, the same logic carries over to share count, balance sheet, and cash flow growth filters.
For the endpoint contract, request envelope, and generated Postman collection, see the API v1 screener reference. If you want simpler request examples before this article, start with the starter-screen screener examples.
1.0 The growth filter family
The advanced screener supports four growth-rule filters. Each one takes a rules array, and each rule specifies a metric, a time horizon, a value type, a currency basis, and a threshold.
| Filter | API field | Max rules | What it screens |
|---|---|---|---|
| Income Statement Growth | incomeStatementGrowth | 5 | Revenue, gross profit, EBITDA, EBIT, operating income, net income, EPS, and other income-statement metrics. |
| Share Count Growth | shareCountGrowth | 5 | Basic or diluted share count, useful for finding companies that have reduced, held flat, or grown their share count over time. |
| Balance Sheet Growth | balanceSheetGrowth | 5 | Assets, debt, equity, book value per share, and other balance-sheet measures. |
| Cash Flow Growth | cashFlowGrowth | 5 | Operating cash flow, free cash flow, CapEx, dividends, and per-share cash-flow measures. |
2.0 How Income Statement Growth rules work
Each rule inside incomeStatementGrowth combines into a single logical statement, such as: revenue grew at least 10% per year over the past five years, measured in reported currency. Here is what each field controls.
| Field | Example value | What it controls |
|---|---|---|
| metric | revenue | The income-statement metric you want to evaluate. |
| horizon_years | 1, 2, 3, 5, 7, 8, 10 | How far back to look. Use /api/v1/screener/options/ to confirm the current allowed values. |
| value_type | cagr, multiple | CAGR gives you annualized growth rate. Multiple gives you total growth over the horizon, so 2.0 means doubled. |
| currency_basis | reported, usd | Use reported to evaluate each company in its own currency. Use USD to normalize across currencies when comparing companies from different countries. |
| min / max | 0.10, 2.0 | The threshold. For CAGR, 0.10 means 10% annualized. For a multiple, 2.0 means the metric has at least doubled. |
You can include up to 5 rules in a single filter. Companies that do not have enough history for the chosen horizon are excluded automatically.
3.0 Find companies with sustained revenue growth
The goal
A clean first pass for companies that have been genuinely expanding, not just companies that had one great year.
Why this filter works
Revenue CAGR over five years is one of the most reliable ways to find real demand growth. The five-year window smooths out a lucky quarter and forces you to look at a fuller business cycle.
Where to take it next
This is a starting filter, not a final answer. Once you have results, layer in profitability or balance-sheet screens to separate businesses that are growing healthily from ones growing at any cost.
Rule breakdown
| Metric | API metric | Payload setting | What it does |
|---|---|---|---|
| Revenue | revenue | revenue, 5-year CAGR, reported currency, min: 0.1 | Keeps companies where Revenue CAGR over 5 years, measured in reported currency, is at least 0.1. |
Request body
This request keeps companies where reported revenue CAGR is at least 10% over five years. Send this body to /api/v1/screener/advanced/.
{
"filters": {
"incomeStatementGrowth": {
"rules": [
{
"metric": "revenue",
"horizon_years": 5,
"value_type": "cagr",
"currency_basis": "reported",
"min": 0.1
}
]
}
},
"sort": {},
"page": {
"limit": 100,
"offset": 0
},
"fields": [
"full_ticker",
"companyName",
"marketCap_usd"
],
"result_format": "rows"
}Code
/api/v1/screener/advanced/
curl -X POST "https://www.dfin.pro/api/v1/screener/advanced/" \
-H "Authorization: Bearer ${DFIN_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"filters":{"incomeStatementGrowth":{"rules":[{"metric":"revenue","horizon_years":5,"value_type":"cagr","currency_basis":"reported","min":0.1}]}},"sort":{},"page":{"limit":100,"offset":0},"fields":["full_ticker","companyName","marketCap_usd"],"result_format":"rows"}'
import os, requests
url = "https://www.dfin.pro/api/v1/screener/advanced/"
headers = {"Authorization": f"Bearer {os.environ['DFIN_API_KEY']}"}
payload = {'filters': {'incomeStatementGrowth': {'rules': [{'metric': 'revenue',
'horizon_years': 5,
'value_type': 'cagr',
'currency_basis': 'reported',
'min': 0.1}]}},
'sort': {},
'page': {'limit': 100, 'offset': 0},
'fields': ['full_ticker', 'companyName', 'marketCap_usd'],
'result_format': 'rows'}
response = requests.post(url, headers=headers, json=payload, timeout=30)
response.raise_for_status()
print(response.json())
4.0 Find companies where EBITDA has doubled
The goal
Surface businesses where earnings power has expanded dramatically over a shorter window.
Why this filter works
Sometimes you care less about the annualized rate and more about the total change. That is where multiple is more useful than cagr: a multiple of 2.0 simply means the metric is at least twice what it was at the start of the horizon. Concrete, easy to reason about.
This example also uses currency_basis: usd, which normalizes EBITDA into USD before applying the threshold. That is useful when your screen includes companies that report in different currencies and you want a like-for-like comparison.
Where to take it next
Companies that pass this screen have grown fast, but they are not necessarily profitable or well-capitalized. Add a net income or leverage filter to narrow toward companies where the growth looks durable.
Rule breakdown
| Metric | API metric | Payload setting | What it does |
|---|---|---|---|
| EBITDA | ebitda | ebitda, 3-year growth multiple, USD, min: 2.0 | Keeps companies where EBITDA growth multiple over 3 years, measured in USD, is at least 2.0. |
Request body
This request keeps companies where USD EBITDA growth multiple is at least 2.0 over three years. Send this body to /api/v1/screener/advanced/.
{
"filters": {
"incomeStatementGrowth": {
"rules": [
{
"metric": "ebitda",
"horizon_years": 3,
"value_type": "multiple",
"currency_basis": "usd",
"min": 2.0
}
]
}
},
"sort": {},
"page": {
"limit": 100,
"offset": 0
},
"fields": [
"full_ticker",
"companyName",
"marketCap_usd"
],
"result_format": "rows"
}Code
/api/v1/screener/advanced/
curl -X POST "https://www.dfin.pro/api/v1/screener/advanced/" \
-H "Authorization: Bearer ${DFIN_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"filters":{"incomeStatementGrowth":{"rules":[{"metric":"ebitda","horizon_years":3,"value_type":"multiple","currency_basis":"usd","min":2.0}]}},"sort":{},"page":{"limit":100,"offset":0},"fields":["full_ticker","companyName","marketCap_usd"],"result_format":"rows"}'
import os, requests
url = "https://www.dfin.pro/api/v1/screener/advanced/"
headers = {"Authorization": f"Bearer {os.environ['DFIN_API_KEY']}"}
payload = {'filters': {'incomeStatementGrowth': {'rules': [{'metric': 'ebitda',
'horizon_years': 3,
'value_type': 'multiple',
'currency_basis': 'usd',
'min': 2.0}]}},
'sort': {},
'page': {'limit': 100, 'offset': 0},
'fields': ['full_ticker', 'companyName', 'marketCap_usd'],
'result_format': 'rows'}
response = requests.post(url, headers=headers, json=payload, timeout=30)
response.raise_for_status()
print(response.json())
5.0 Find steadier growers, not extreme outliers
The goal
A growth screen that filters out the most explosive names as well as the slow ones, useful when you want a research list you can actually underwrite.
Why this filter works
This is where the max threshold earns its place. A minimum of 8% revenue CAGR keeps the slow growers out; a maximum of 40% keeps out the hypergrowth names that are harder to analyze and often harder to value. The second rule, requiring at least 5% net income CAGR, asks whether some of that top-line growth is actually reaching the bottom line.
Where to take it next
This kind of bounded universe is a useful foundation for valuation work. Add sector and size filters if you want to compare companies within a tighter peer group.
Rule breakdown
| Metric | API metric | Payload setting | What it does |
|---|---|---|---|
| Revenue | revenue | revenue, 5-year CAGR, reported currency, min: 0.08, max: 0.4 | Keeps companies where Revenue CAGR over 5 years, measured in reported currency, is between 0.08 and 0.4. |
| Net Income | netIncome | netIncome, 5-year CAGR, reported currency, min: 0.05 | Keeps companies where Net Income CAGR over 5 years, measured in reported currency, is at least 0.05. |
Request body
This request combines two Income Statement Growth rules: revenue CAGR must be between 8% and 40%, and net income CAGR must be at least 5%. Send this body to /api/v1/screener/advanced/.
{
"filters": {
"incomeStatementGrowth": {
"rules": [
{
"metric": "revenue",
"horizon_years": 5,
"value_type": "cagr",
"currency_basis": "reported",
"min": 0.08,
"max": 0.4
},
{
"metric": "netIncome",
"horizon_years": 5,
"value_type": "cagr",
"currency_basis": "reported",
"min": 0.05
}
]
}
},
"sort": {},
"page": {
"limit": 100,
"offset": 0
},
"fields": [
"full_ticker",
"companyName",
"marketCap_usd"
],
"result_format": "rows"
}Code
/api/v1/screener/advanced/
curl -X POST "https://www.dfin.pro/api/v1/screener/advanced/" \
-H "Authorization: Bearer ${DFIN_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"filters":{"incomeStatementGrowth":{"rules":[{"metric":"revenue","horizon_years":5,"value_type":"cagr","currency_basis":"reported","min":0.08,"max":0.4},{"metric":"netIncome","horizon_years":5,"value_type":"cagr","currency_basis":"reported","min":0.05}]}},"sort":{},"page":{"limit":100,"offset":0},"fields":["full_ticker","companyName","marketCap_usd"],"result_format":"rows"}'
import os, requests
url = "https://www.dfin.pro/api/v1/screener/advanced/"
headers = {"Authorization": f"Bearer {os.environ['DFIN_API_KEY']}"}
payload = {'filters': {'incomeStatementGrowth': {'rules': [{'metric': 'revenue',
'horizon_years': 5,
'value_type': 'cagr',
'currency_basis': 'reported',
'min': 0.08,
'max': 0.4},
{'metric': 'netIncome',
'horizon_years': 5,
'value_type': 'cagr',
'currency_basis': 'reported',
'min': 0.05}]}},
'sort': {},
'page': {'limit': 100, 'offset': 0},
'fields': ['full_ticker', 'companyName', 'marketCap_usd'],
'result_format': 'rows'}
response = requests.post(url, headers=headers, json=payload, timeout=30)
response.raise_for_status()
print(response.json())
6.0 How to modify these examples
The best approach is to copy one example, run it as-is, then change one rule field at a time and watch how the result set shifts. Small changes, fast feedback.
Change the metric
Replace metric with any other value from /api/v1/screener/options/, such as operatingIncome, netIncome, or epsDiluted. Keep the rest of the rule the same at first, then adjust the horizon or threshold once you see what changes.
Return only tickers
Set result_format to tickers when you just need the symbols. It keeps the response compact and works well when the output feeds into another step.
Request more columns
When using result_format: rows, add to the fields array: sector, market cap, valuation ratios, recent returns. More context in the output means less looking things up afterward.
Narrow the universe
Combine a growth filter with ordinary filters like country, gics_sector, or marketCap_usd. This is almost always the right move when you want a practical research list rather than a global screen.
Check the current contract
Before building a new rule from scratch, call /api/v1/screener/options/ and look at the growth filter metadata. It is the authoritative source for current metrics, allowed horizons, value types, and currency options, and it is much faster than guessing and debugging.