Guide · Step by step
Build a Japanese stock screener with an API
Key points
- check_circleBasing conditions on the sector distribution, such as "ROE above and PBR below the sector median", keeps differences between sectors from skewing the results.
- check_circle/stable/screener combines optional conditions and returns up to 100 rows ordered by the metric you choose.
- check_circleThe six ready-made screens (/stable/screener-presets) publish their conditions, so you can reproduce and adapt them.
Why build your own screener
Ready-made screeners are convenient, but they may not let you save conditions, run them automatically every day or combine them with your own metrics.
With an API you can run the same conditions the same way every day and send the results to a CSV file or your own app. This guide uses four ZENITH Code endpoints.
- GET
/stable/industry-benchmarksIndustry benchmarks by sectorSector medians and quartiles (the reference for conditions)10 tokens per call - GET
/stable/screenerStock screenerFilter by combined conditions10 tokens per call - GET
/stable/screener-presetsReady-made screens (high dividend, value, growth, etc.)Filter with six ready-made screens10 tokens per call - GET
/stable/available-sectorsTSE 33 sectors listSector codes1 tokens per call
1Look at sector medians and set conditions
First get the medians and quartiles for the target sector. The example is Electric Appliances (sector code 3650).
curl -sG "https://api.zenithcode.net/stable/industry-benchmarks" \ --data-urlencode "sector=3650" \ -H "x-api-key: YOUR_API_KEY"
- check_circleroeMedian and pbrMedian are the values of the sector's middle company. Ratios are 0–1 decimals (0.08 = 8%).
- check_circleThe middle half of companies falls between the first quartile (Q1) and the third quartile (Q3). To target the top quarter, use Q3.
- check_circleOutliers (multiples of 0 or below, ROE or ROA above 100% and so on) are excluded. The definitions are on the sector data page.
2Filter with /stable/screener
Pass your conditions as parameters. The example asks for Electric Appliances with ROE of 10% or more and PBR of 1.5x or less, highest ROE first. Ratio parameters are 0–1 decimals too.
# Electric Appliances (3650): ROE of 10% or more, PBR of 1.5x or less, sorted by ROE curl -sG "https://api.zenithcode.net/stable/screener" \ --data-urlencode "sector=3650" \ --data-urlencode "roeMoreThan=0.1" \ --data-urlencode "pbrLowerThan=1.5" \ --data-urlencode "isEtf=false" \ --data-urlencode "sortBy=roe" \ --data-urlencode "order=desc" \ --data-urlencode "limit=50" \ -H "x-api-key: YOUR_API_KEY"
| Parameter | Meaning |
|---|---|
| sector | TSE 33-sector code (e.g. 3700 = Transportation Equipment). See /stable/available-sectors |
| roeMoreThan | Minimum ROE (0–1 decimal; 0.1 = 10%) |
| pbrLowerThan | Maximum PBR (times) |
| perLowerThan | Maximum PER (times) |
| dividendYieldMoreThan | Minimum dividend yield (0–1 decimal; 0.03 = 3%) |
| marketCapMoreThan | Minimum market cap (JPY) |
| isEtf | true = ETFs/REITs only, false = exclude ETFs/REITs |
| sortBy | Metric to sort by. Defaults to marketCap. Allowed values (marketCap, per, pbr, psr, roe, roa, dividendYield, evToEbitda, changePct1d, changePct1w, changePct1m, changePctYtd, dividendCagr3y, dividendCagr5y, revenueCagr3y, revenueCagr5y, epsCagr3y, epsCagr5y, progressiveDividendYears) |
| order | Sort order: desc = largest first (default), asc = smallest first |
| limit | Maximum number of rows to return (default 50, max 100) |
Parameter descriptions come from the API catalog (/stable/screener).
3Compare with ready-made screens
/stable/screener-presets runs six common screens as they are. Their conditions are published, so you can see how they differ from yours.
| preset | Conditions (all must hold) | Order |
|---|---|---|
| high-dividend | Dividend yield from 4% to 12%, positive PER | Highest dividend yield first |
| value | PER above 0 and up to 15x, PBR above 0 and up to 1x, ROE of 5% or more | Lowest PBR first |
| high-roe | ROE from 15% to 100% | Highest ROE first |
| growth | 3-year revenue growth of 10% or more, 3-year EPS growth of 10% or more, ROE from 8% to 100% | Highest 3-year revenue growth first |
| dividend-growth | Five or more consecutive years without a dividend cut (increases or unchanged), dividend yield from 2% to 100%. Check strict increase streaks one company at a time with /stable/dividend-streaks | Longest no-cut streak first |
| momentum | 1-month change of +10% or more and year-to-date change of +20% or more (price-based, so handle results with care) | Highest year-to-date change first |
Every screen covers common stocks with a market cap of at least ¥5 billion. Add sector to limit it to one sector.
curl -sG "https://api.zenithcode.net/stable/screener-presets" \ --data-urlencode "preset=value" \ --data-urlencode "sector=3650" \ --data-urlencode "limit=20" \ -H "x-api-key: YOUR_API_KEY"
4Run it end to end and make a table
Steps 1 and 2 in one script. It uses the sector medians directly as conditions and saves the results to CSV.
# pip install requests pandas
import pandas as pd
import requests
API = "https://api.zenithcode.net/stable"
HEADERS = {"x-api-key": "YOUR_API_KEY"}
SECTOR = "3650" # TSE 33-sector code (see /stable/available-sectors)
def get(path, **params):
res = requests.get(API + path, params=params, headers=HEADERS)
res.raise_for_status()
return res.json()
# 1. Sector medians become the thresholds
bench = get("/industry-benchmarks", sector=SECTOR)[0]
roe_min = bench["roeMedian"] # 0-1 decimal
pbr_max = bench["pbrMedian"] # times
print(f"{bench['sectorEn']}: median ROE {roe_min:.1%}, median PBR {pbr_max:.2f}x")
# 2. Companies above the median ROE and below the median PBR
rows = get(
"/screener",
sector=SECTOR,
roeMoreThan=roe_min,
pbrLowerThan=pbr_max,
isEtf="false",
sortBy="roe",
order="desc",
limit=50,
)
# 3. Keep fundamental columns only (no price columns) and save
df = pd.DataFrame(rows)[["symbol", "nameJa", "marketCap", "per", "pbr", "roe", "dividendYield"]]
df["marketCap"] = (df["marketCap"] / 1e8).round(0) # 100M JPY
df["roe"] = (df["roe"] * 100).round(1) # %
df["dividendYield"] = (df["dividendYield"] * 100).round(2)
df.to_csv("screen.csv", index=False)
print(df.to_string(index=False))- 1Pass roeMedian and pbrMedian from industry-benchmarks as roeMoreThan and pbrLowerThan in screener.
- 2Keep only securities code, name, market cap, PER, PBR, ROE and dividend yield. Price columns are not used.
- 3Convert market cap to ¥100M and ROE and dividend yield to percent, then save the CSV.
5Run after the data is refreshed
The data behind screener and industry-benchmarks is rebuilt at 16:45・19:45 Japan time, Monday to Saturday. For a daily run, schedule it after that to filter on the latest values.
Things to note
- errorIf a sector median is null (fewer than three valid values), you cannot pass it as a condition. Check for null first.
- errorResponses also include price (latest close) and changePctYtd (year-to-date change), both exchange data. Keep them out of tables you show.
- errorWith no conditions, the top rows by the sort metric are returned. limit is at most 100.
- errorAdd isEtf=false to exclude ETFs and REITs.
Showing results to others
Screening results are processed ZENITH Code data. Free and Pro do not allow showing them to third parties; showing them in your own service requires Business or higher. Exchange data such as stock prices may not be shown on any plan. See the fintech use case
FAQ for investors and developers
How should I choose conditions?expand_more
Basing them on sector medians and quartiles reduces the effect of different typical levels across sectors. Judge whether a condition suits you based on your own goals.
Can I filter by price or price change?expand_more
Price changes are available as sort metrics, but price-related values are exchange data. They require an account, and results may not be shown to third parties.
How is this different from the screener on this site?expand_more
The site's screener uses the same /stable/screener. With the API you can save conditions, run them automatically and combine them with your own data.
FAQ for AI builders and researchers
Can an AI run screens?expand_more
With the MCP tools screen_stocks, run_screener_preset and get_industry_benchmarks, an AI can follow the same steps. Tell it to pass ratios as 0–1 decimals.
Can the ready-made screen definitions change?expand_more
The OpenAPI and MCP descriptions are generated from the server-side definition table, so any change appears in the API documentation as well.
What date do the results reflect?expand_more
The date of the underlying snapshot. /stable/system/freshness shows each dataset's as-of time.
Disclaimer
This page explains how to use a Japanese equity data API and how its data is defined. It is not a solicitation to invest or a recommendation to buy or sell any security. The content reflects information at the time of writing and is not guaranteed to be accurate or complete. Investment decisions are your own responsibility.
What you may show or provide to third parties depends on your plan and Article 8 of the Terms. Exchange data such as stock prices may not be shown to third parties on any plan. Terms, Article 8 · Data sources and terms of use

