Get free API key

Guide · Step by step

Build a Japanese stock screener with an API

Five steps from setting conditions against sector medians, to filtering with /stable/screener, to a results table. The Python and JavaScript code runs as you copy it.

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.

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).

Code example
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.

Code example
# 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"
ParameterMeaning
sectorTSE 33-sector code (e.g. 3700 = Transportation Equipment). See /stable/available-sectors
roeMoreThanMinimum ROE (0–1 decimal; 0.1 = 10%)
pbrLowerThanMaximum PBR (times)
perLowerThanMaximum PER (times)
dividendYieldMoreThanMinimum dividend yield (0–1 decimal; 0.03 = 3%)
marketCapMoreThanMinimum market cap (JPY)
isEtftrue = ETFs/REITs only, false = exclude ETFs/REITs
sortByMetric 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)
orderSort order: desc = largest first (default), asc = smallest first
limitMaximum 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.

presetConditions (all must hold)Order
high-dividendDividend yield from 4% to 12%, positive PERHighest dividend yield first
valuePER above 0 and up to 15x, PBR above 0 and up to 1x, ROE of 5% or moreLowest PBR first
high-roeROE from 15% to 100%Highest ROE first
growth3-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-growthFive 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-streaksLongest no-cut streak first
momentum1-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.

Code example
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.

Code example
# 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))
  1. 1Pass roeMedian and pbrMedian from industry-benchmarks as roeMoreThan and pbrLowerThan in screener.
  2. 2Keep only securities code, name, market cap, PER, PBR, ROE and dividend yield. Price columns are not used.
  3. 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.
warning

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.

gavel

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