DFin API v1
Search filings and run the DFin equity screener through stable JSON endpoints. Runtime endpoint paths, request constraints, rate limits, and key choices are rendered from the same code that serves the API.
1.0 Overview
| Base URL | https://www.dfin.pro |
|---|---|
| API version | v1 |
| Runtime base path | /api/v1/ |
| Docs alternate | Markdown / LLM version |
| Postman collection | Download collection |
2.0 Global Rules
2.1 Authentication
Send the API key in the HTTP authorization header.
$env:DFIN_API_KEY = "paste-your-api-key-here"
export DFIN_API_KEY="paste-your-api-key-here"
2.2 Request Format
POST endpoints accept JSON request bodies with Content-Type: application/json. GET endpoints do not require a JSON body.
2.3 Rate Limits
| Window | Starter tier limit | Notes |
|---|---|---|
| Per minute | 30 requests per minute | Authenticated requests that later fail validation can still count. |
| Rolling 24 hours | 1,000 requests per rolling 24 hours | Missing or invalid API keys fail before throttling. |
2.4 Screener Controls
Screener POST endpoints share pagination, sorting, field selection, and result_format behavior. Pagination defaults to {"limit":100,"offset":0}, page.limit must be between 1 and 1000, and result_format can be rows, tickers.
2.5 Common Errors
| Status | Meaning | Typical fix |
|---|---|---|
| 400 | Request validation failed. | Check parameter names, types, allowed values, and screener mode. |
| 401 | Missing, malformed, inactive, or invalid API key. | Set DFIN_API_KEY and use the generated Authorization header shown in the examples. |
| 429 | Rate limit exceeded. | Back off and retry after the throttle window resets. |
| 502 | Filing search backend did not return a usable result. | Retry later or narrow the search request. |
3.0 Endpoint Summary
| Area | Method | Path | Purpose |
|---|---|---|---|
| Filing Search | POST | /api/v1/filings/search/ |
Search structured company filings. |
| Screener | GET | /api/v1/screener/options/ |
Return compact request-building metadata for the current screener API contract. |
| Screener | POST | /api/v1/screener/basic/ |
Run the simple screener filter set. |
| Screener | POST | /api/v1/screener/advanced/ |
Run the advanced screener contract. |
4.0 Filing Search
Filing search endpoints query company filings with one or more natural-language queries and optional structured filters.
4.1 Search filings
Search structured company filings.
4.1.1 Parameters
| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
queries |
array of string | Yes | - | max length 12, max items 12, item max length 300 Submit one or more natural-language filing search queries. |
name |
string | No | - | max length 120 Optional company name. |
ticker |
string | No | - | max length 40 Optional ticker. |
fiscal_year |
integer | No | - | min 1900, max 2200 Fiscal year filter. |
fiscal_period |
string | No | - | choices: Q1, Q2, Q3, Q4, H1, H2, FY Fiscal period filter. |
filing_type |
string | No | - | max length 30 Optional filing type. |
results |
integer | No | 5 |
min 1, max 25 Number of filing-search results to request. |
rerank |
boolean | No | false |
Defaults to false for the time being. Reranking functionality will be enabled in the future. |
searchtype |
string | No | "hybrid" |
choices: hybrid, vector, keyword Filing-search strategy. |
4.1.2 Response
The v1 response returns the current filing-search result payload from the filing search backend. Future response normalization will happen behind this endpoint without changing the request shape unless a new API version is introduced.
curl -X POST "https://www.dfin.pro/api/v1/filings/search/" \
-H "Authorization: Bearer ${DFIN_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"queries":["revenue growth","margin expansion"],"ticker":"AAPL.US","fiscal_year":2024,"filing_type":"10-K","results":5,"rerank":false,"searchtype":"hybrid"}'
import os, requests
url = "https://www.dfin.pro/api/v1/filings/search/"
headers = {"Authorization": f"Bearer {os.environ['DFIN_API_KEY']}"}
payload = {'queries': ['revenue growth', 'margin expansion'],
'ticker': 'AAPL.US',
'fiscal_year': 2024,
'filing_type': '10-K',
'results': 5,
'rerank': False,
'searchtype': 'hybrid'}
response = requests.post(url, headers=headers, json=payload, timeout=30)
response.raise_for_status()
print(response.json())
5.0 Screener
The screener endpoints expose the DFin equity screener through a contract-driven API. Use the options endpoint as the source of truth for current public filter, sort, field, shared-default, and accepted option metadata.
For step-by-step walkthroughs, start with the API v1 examples library, including the starter-screen screener API examples and the advanced growth filter examples.
5.1 Get screener options
Return compact request-building metadata for the current screener API contract.
The options response is compact for API and AI clients: modes.basic and modes.advanced list allowed filter keys directly, sort.fields lists sortable field names for the existing sort.field request property, and filter_type_defaults carries shared operators and rule limits. Growth-rule currency bases are derived from each metric's value_fields[value_type] keys.
curl -X GET "https://www.dfin.pro/api/v1/screener/options/" \
-H "Authorization: Bearer ${DFIN_API_KEY}"
import os, requests
url = "https://www.dfin.pro/api/v1/screener/options/"
headers = {"Authorization": f"Bearer {os.environ['DFIN_API_KEY']}"}
response = requests.get(url, headers=headers, timeout=30)
response.raise_for_status()
print(response.json())
Example response
{
"result_formats": [
"rows",
"tickers"
],
"page": {
"default_limit": 100,
"default_offset": 0,
"max_limit": 1000
},
"sort": {
"fields": [
"marketCap_usd",
"return_1y"
]
},
"modes": {
"basic": [
"country",
"sector",
"marketCap_usd",
"..."
],
"advanced": [
"country",
"incomeStatementGrowth",
"marketCap_usd",
"..."
]
},
"filter_type_defaults": {
"range": {
"operators": [
"min",
"max"
]
},
"growth_rule_group": {
"horizons": [
1,
2,
3,
5,
7,
8,
10
],
"value_types": [
"cagr",
"multiple"
],
"value_type_formats": {
"cagr": "percent",
"multiple": "number"
},
"missing_data_policy": "exclude",
"max_rules": 5
}
},
"filters": {
"country": {
"label": "Country",
"input_type": "multi_select",
"operators": [
"include",
"exclude"
],
"default_operator": "include",
"options_source": "countries"
}
},
"option_values": {
"countries": [
"Canada",
"United States"
]
},
"fields": [
"companyName",
"full_ticker",
"marketCap_usd"
]
}
5.2 Request envelope
Both screener POST endpoints accept the same top-level envelope. The difference is which filter keys are allowed.
5.2.1 Fields
| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
filters |
object | No | {} |
Filter payload keyed by screener filter name. |
sort |
object | No | {} |
Supports the screener sort object, including field and descending direction. |
page |
object | No | {} |
Supports limit and offset. The public maximum limit is code-defined. Maximum limit is 1000. |
fields |
array of string | No | - | item max length 120 Requested output fields for rows responses. |
result_format |
string | No | "rows" |
choices: rows, tickers Rows returns row objects. Tickers returns a flat ticker list and ignores custom fields. |
5.2.2 Building screener requests
The options endpoint is the contract an agent should inspect before composing a screen. It tells you which filters exist, which endpoint can accept each filter, which values can be submitted, which output fields can be requested, and which sort keys are valid.
| Step | What to do |
|---|---|
| Fetch options | Call GET /api/v1/screener/options/ first. Treat that response as the current source of valid filters, modes, option values, sort fields, output fields, and shared defaults. |
| Choose endpoint | Use POST /api/v1/screener/basic/ only when every selected filter is listed in options.modes.basic. Use POST /api/v1/screener/advanced/ when any selected filter is advanced-only. |
| Build filters | For each selected filter, inspect options.filters[filterName].input_type and use the matching payload grammar. When a definition omits common operators or limits, inherit them from options.filter_type_defaults[input_type]. |
| Select output | For rows responses, choose field names from options.fields. For compact ticker-only output, set result_format to tickers and omit custom fields. |
| Sort and page | Choose sort.field from options.sort.fields, submit sort as an object with field and desc, and keep page.limit between 1 and the documented maximum. |
5.2.3 Filter payload grammar
Each entry in filters is keyed by a filter name from options.filters. Use that filter's input_type to choose the payload shape. When a filter definition omits common operators or limits, inherit them from options.filter_type_defaults[input_type]; values shown directly on the filter definition are more specific and take precedence.
| Input type | Payload shape | Notes | Example |
|---|---|---|---|
range |
{"filterName":{"min":number,"max":number}} |
Use min, max, or both to set numeric thresholds. The available range keys come from filter_type_defaults.range. | {"marketCap_usd":{"min":10000000000}} |
multi_select |
{"filterName":{"operator":"include","values":["accepted option"]}} |
Use values from option_values or from an inline options list. If the user does not ask to exclude values, use the filter default_operator. | {"country":{"operator":"include","values":["United States"]}} |
text |
{"description_keywords":{"fts":"search terms"}} |
Use this for full-text searches against company business descriptions. | {"description_keywords":{"fts":"cloud software"}} |
metric_rule_group |
{"filterName":{"missing_policy":"strict","rules":[...]}} |
Use range rules for thresholds and compare rules when the screen needs TTM, a fiscal year, or a trailing fiscal-year average. | {"returnOnInvestedCapital":{"rules":[{"kind":"range","operand":{"kind":"average","years":3,"anchor":{"scope":"fy","offset":0}},"min":0.15}]}} |
growth_rule_group |
{"filterName":{"rules":[{"metric":"revenue","horizon_years":5,"value_type":"cagr","currency_basis":"reported","min":0.10}]}} |
Choose the metric, horizon, value type, currency basis, and threshold. Valid currency bases come from metric.value_fields[value_type]. | {"incomeStatementGrowth":{"rules":[{"metric":"revenue","horizon_years":5,"value_type":"cagr","currency_basis":"reported","min":0.10}]}} |
volatility_rule_group |
{"volatility":{"rules":[{"kind":"range","field":"daily_return_vol_1y_annualized","max":0.40}]}} |
Use member fields from filters.volatility.members. Range rules test one member; compare rules compare two members. | {"volatility":{"rules":[{"kind":"compare","left_field":"daily_return_vol_1y_annualized","operator":"lt","right_field":"daily_return_vol_5y_annualized"}]}} |
field_compare |
{"volatility_compare":{"left_field":"field","operator":"lt","right_field":"field"}} |
Legacy/simple field comparison form for volatility fields. The canonical volatility rule group is preferred for new advanced requests. | {"volatility_compare":{"left_field":"daily_return_vol_1y_annualized","operator":"lt","right_field":"daily_return_vol_5y_annualized"}} |
5.2.4 Using option values
For filters with options_source, submit values from the matching list in options.option_values. These are accepted request values, not browser display labels. For filters with inline options, submit one of those listed values. For growth metrics, derive valid currency_basis values from the keys under metric.value_fields[value_type]; for example, if a metric has value_fields.cagr.reported and value_fields.cagr.usd, both reported and usd are valid for CAGR.
5.2.5 Units and thresholds
| Topic | Rule |
|---|---|
| Percentages and ratios | Use decimal values: 0.10 means 10%, 0.5 means 50%, and 1.0 means 100%. |
| USD amounts | Fields ending in _usd, such as marketCap_usd, use raw U.S. dollars, not millions or billions. |
| CAGR | Growth value_type cagr uses decimal annualized rates, so min 0.10 means at least 10% annualized growth. |
| Growth multiple | Growth value_type multiple uses numeric multiples, so min 2.0 means the metric at least doubled over the horizon. |
| TTM | TTM means trailing twelve months and is used by simple shorthand fields such as dividendYieldTTM. |
| FY offsets | In metric-rule operands, FY offset 0 means the latest eligible fiscal year. Larger offsets move backward one fiscal year at a time. |
5.2.6 Request snippets
These snippets show only the relevant part of the request body. Put filter examples under the top-level filters object when sending a screener POST request.
| Case | When to use it | JSON |
|---|---|---|
| Multi-select filter | Country, sector, industry, currency, and database coverage filters use an operator plus accepted values. | |
| Range filter | Range filters accept min, max, or both. | |
| Text filter | Business-description search uses the fts key. | |
| Metric rule group | Historical metric rules can target TTM, one fiscal year, or a trailing fiscal-year average. | |
| Growth rule group | Growth filters combine metric, horizon, value type, currency basis, and min/max thresholds. | |
| Volatility rule group | Volatility rules can filter one member field or compare two member fields. | |
| Sort and fields | Sort with a supported field and request row fields from options.fields. | |
5.3 Run a basic screener
The basic endpoint accepts business profile filters, database coverage, market and valuation ranges, returns, volatility ranges, common TTM shorthand ranges, and net income.
curl -X POST "https://www.dfin.pro/api/v1/screener/basic/" \
-H "Authorization: Bearer ${DFIN_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"filters":{"country":{"operator":"include","values":["United States"]},"sector":{"operator":"include","values":["Information Technology"]},"marketCap_usd":{"min":1000000000},"return_1y":{"min":0.1}},"sort":{"field":"marketCap_usd","desc":true},"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/basic/"
headers = {"Authorization": f"Bearer {os.environ['DFIN_API_KEY']}"}
payload = {'filters': {'country': {'operator': 'include', 'values': ['United States']},
'sector': {'operator': 'include', 'values': ['Information Technology']},
'marketCap_usd': {'min': 1000000000},
'return_1y': {'min': 0.1}},
'sort': {'field': 'marketCap_usd', 'desc': True},
'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())
Example response
{
"total": 1,
"limit": 100,
"offset": 0,
"loaded_fields": [
"full_ticker",
"companyName",
"marketCap_usd"
],
"temporary_columns": [],
"rows": [
{
"full_ticker": "AAPL.US",
"companyName": "Apple",
"marketCap_usd": 3000000000000
}
]
}
5.4 Run an advanced screener
The advanced endpoint accepts every basic filter plus the canonical advanced screener contract, including metric rule groups, growth/CAGR/multiple filters, composite metric member filters, and canonical volatility rule groups.
curl -X POST "https://www.dfin.pro/api/v1/screener/advanced/" \
-H "Authorization: Bearer ${DFIN_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"filters":{"country":{"operator":"include","values":["United States"]},"incomeStatementGrowth":{"rules":[{"metric":"revenue","horizon_years":5,"value_type":"cagr","currency_basis":"reported","min":0.1}]}},"result_format":"tickers","page":{"limit":100,"offset":0}}'
import os, requests
url = "https://www.dfin.pro/api/v1/screener/advanced/"
headers = {"Authorization": f"Bearer {os.environ['DFIN_API_KEY']}"}
payload = {'filters': {'country': {'operator': 'include', 'values': ['United States']},
'incomeStatementGrowth': {'rules': [{'metric': 'revenue',
'horizon_years': 5,
'value_type': 'cagr',
'currency_basis': 'reported',
'min': 0.1}]}},
'result_format': 'tickers',
'page': {'limit': 100, 'offset': 0}}
response = requests.post(url, headers=headers, json=payload, timeout=30)
response.raise_for_status()
print(response.json())
Example response
{
"total": 2,
"limit": 100,
"offset": 0,
"tickers": [
"AAPL.US",
"MSFT.US"
]
}
6.0 Confusing Cases
- Basic vs advanced: basic rejects advanced-only filter keys. Use
/api/v1/screener/options/to inspectmodes.basicandmodes.advanced. - Rows vs tickers:
result_format="tickers"returns a flattickersarray and forces the backend to request onlyfull_ticker. - Pagination:
page.limitdefaults to 100 and must be between 1 and 1000. - Filter metadata: the current public screener filter, sort, field, and option metadata comes from
/api/v1/screener/options/, not from a static docs table.
For a browser reference to the same screener concepts, see the DFin stock screener.