Public data & feeds
Everything machine-readable we publish
These are the data files behind the daily digests and the
divergence board. No key, no signup, no rate limit worth mentioning —
if you want to pull them on a schedule, please do.
Start here
curl -s https://www.insightintochina.com/data/index.json | jq '.datasets[] | {path, row_count}'
That catalog is the contract: it lists every dataset with its
schema, row count and update time. It is regenerated on every build, so it cannot go stale quietly.
Datasets · api v1 · catalog built 2026-10-13
Dataset catalog
JSON
each deploy
This list, in machine-readable form — every public dataset with its schema, row count and update time. Machines should start here.
/data/index.json
9 datasets · 22.0 KB · updated 2026-10-13
ProvenanceAssembled at build time by pipeline/gen-data-api.mjs from this repository. Row counts and byte sizes are measured from the files on disk as they are published — none of them is estimated.
Limitations- Describes the datasets, not the market. This file carries no market figures of its own.
- An optional dataset with no instance this build (for example the share images on a day the renderer did not run) is removed from datasets[] entirely rather than listed with zero rows, so a missing entry means "none produced yet", not "broken".
- license.requests[] are non-binding asks, not licence conditions — the licence is CC BY 4.0 and it permits commercial use. Only license.terms[] is stated as a condition, and those terms do not narrow CC BY 4.0 beyond attribution and non-endorsement.
Fields (5)
api_version string- Schema version of the public data namespace.
generated_at string- ISO 8601 timestamp of the build that produced these files.
site string- Canonical origin. All dataset URLs resolve under it.
license object- licence id, summary, license_url, terms[] (conditions) and requests[] (non-binding asks). The dataset licence is CC BY 4.0.
datasets array- One object per dataset: id, title, url, format, cadence, description, row_count, bytes, updated_at, fields, provenance, limitations.
Daily digest index
JSON
daily (after each build)
Every trading day for which a sector-grouped digest was produced, with how many briefs it covers and how many sectors they fell into.
/data/daily/manifest.json
16 days · 1.8 KB · updated 2026-10-13
ProvenanceDerived from the same briefing database the site publishes, one entry per trading day that produced a digest. Counts are read back from each day’s own digest file rather than counted a second time.
Limitations- Covers only trading days on which a digest was produced. The earliest day held is 2026-09-27; there is no earlier history.
- otherRatio is our classifier’s failure rate, not a market measure. A high value means the taxonomy did not fit that day, not that the market was unusual.
Fields (3)
generated_at string- ISO 8601 build timestamp.
days object- Map of YYYY-MM-DD → { total, industryCount, otherRatio }. otherRatio is the share of briefs the sector classifier could not place.
order array- Date strings, newest first. Same order as the human index at /daily.
Single-day digest
JSON
daily (after each build)
One day’s briefs grouped into sectors, with tone distribution per sector, the representative views, the tickers mentioned most, market breadth and the divergence board.
/data/daily/{YYYY-MM-DD}.json
38 days · 24.1 KB · updated 2026-10-13
ProvenanceBuilt for one trading day from the briefs published that day. Sector assignment is a deterministic keyword classifier over each headline, with the body used only as a fallback. Tone buckets come from the same per-brief scores the site shows, and board is the same divergence computation described at /methodology.
Limitations- Figures in the briefs originate from third-party Chinese-language institutional research. We translate and structure them; we do not verify them.
- Sector labels are a navigation aid, not an analyst judgment. stats.otherRatio is the share the classifier could not place — treat it as the error budget for that day.
- A day with too few briefs is flagged stats.insufficient rather than padded out to look representative. Several days in the archive are like this: 2026-10-05 carries 5 briefs, 2026-10-02 carries 7.
- board is empty on days when no scored cross-section was available (for example non-trading days), so an empty array is a normal state, not a failure.
- Subscriber-only fields are excluded by construction. There is no flag to include them.
Fields (8)
date string- Trading day, YYYY-MM-DD.
headlines array- Up to 3 cross-sector stories: lake_id, title, bottom_line, sentiment, tickers, industryId, industryEn. Restricted to briefs whose sector came from the headline, not from the body.
industries array- Sector sections, sorted by brief count. Each: id, en, zh, noteCount, highConfidenceCount, sentiment{}, tickers[], leaders[], keyDataPoints[].
other object|null- Briefs the classifier could not place. Kept separate from industries[] rather than folded into one.
sentiment object- counts{} per tone bucket, unknownSentiment, bullRatio (null when nothing was rated), netTone, scoredCount, bullCount, bearCount.
stats object- total, classified, other, otherRatio, viaBody, industryCount, insufficient, minRequired. insufficient=true means too few briefs for a representative digest.
regime object|null- Market breadth reading used: fg_score, fg_label, pct_above_ma20, adv_cnt, dec_cnt, median_ret_20d, as_of_date.
board array- Names where research tone and price diverge most: ticker, gap, ret_1d, ret_20d, note_cnt, senti_score, last_close.
Divergence board — latest
JSON
daily
The names where the tone of published research and the price move disagree most, as of the most recent scored trading day. Gap = tone percentile minus 20-day return percentile.
/data/divergence/latest.json
50 tickers · 9.3 KB · updated 2026-10-13
ProvenanceComputed from the same scored rows as the divergence board page: gap = tone percentile minus 20-day return percentile, rounded to four decimals. The percentile basis is the scored cross-section of that single trading day, so a gap is comparable within a date and not across dates.
Limitations- A description of disagreement between two measurements. It is not a prediction, a rating, a price target or a recommendation.
- The tone input derives from third-party institutional research; only the comparison between tone and return is ours.
- Cross-sections are small. The widest day we have scored carries about 600 names. This file holds a single day — for the accumulated history use /data/divergence/history.json.
- Rows are deduplicated by ticker keeping the larger absolute gap, so a name that qualified on both sides appears once. sides{} reports how many rows each side contributed before deduplication.
Fields (3)
as_of_date string- Trading day the board was computed for.
score_note string- Plain-language definition of gap, repeated so a consumer does not have to fetch /methodology.
rows array- ticker, gap, ret_1d, ret_20d, note_cnt, senti_score, last_close. Sorted by gap, descending.
Divergence board — accumulated panel
JSON
daily (appends one cross-section per scored trading day)
Every scored trading day the board has covered, one row per ticker — the accumulated history behind the single-day board. It is appended to, not mirrored, so a day stays in the file even after the source view stops serving it.
/data/divergence/history.json
2416 rows · 452.8 KB · updated 2026-10-13
ProvenanceAccumulated from the same divergence_daily_view the board pages read, one cross-section per scored trading day. Each day is re-fetched on every run and replaced whole — but only after its row count is reconciled against the count the source view declares for itself (divergence_dates_view.rows_cnt). A day that fails reconciliation keeps its previous rows rather than being half-replaced. Days the source no longer serves are kept. The file carries no timestamp, so it is byte-for-byte reproducible from the source view.
Limitations- Three complete cross-sections so far (2026-09-24 with 580 rows, 2026-10-08 with 626, 2026-10-09 with 555). 2026-09-18 (5 rows) and 2026-10-07 (6 rows) are functional test runs, not full cross-sections — check rows_per_day before using a date as a panel observation.
- gap is computed against that day’s own cross-section, so it is comparable within a date and not across dates. A gap of +0.8 on one day does not mean the same thing as +0.8 on another.
- Units are mixed, because they are the units the source uses: gap and senti_score are 0–1, ret_1d and ret_20d are already percentages, and last_close is unadjusted for splits or dividends.
- The tone input is our scoring of third-party research; only the comparison between tone and return is ours.
- Depth grows one cross-section per scored trading day. Days the pipeline did not score cannot be back-filled, so a gap in days[] is permanent.
- Nothing here is a buy or sell call, a rating, a price target or investment advice.
Fields (9)
schema string- Format version of this file.
source_view string- The view the rows are read from.
primary_key array- ["as_of_date", "ticker"] — unique across the whole file.
units object- The unit of each measure. gap and senti_score are 0–1; ret_1d and ret_20d are already multiplied by 100.
rows_per_day object- Map of YYYY-MM-DD → row count. Read this before treating a date as a panel observation: a day with a handful of rows is a test run, not a cross-section.
days array- Every date present in rows[], ascending.
days_no_longer_in_source array- Dates still in this file that the source view no longer serves. Kept deliberately — that is what "accumulated" means.
count number- Total rows; equals rows.length.
rows array- as_of_date, ticker, gap, ret_1d, ret_20d, note_cnt, senti_score, last_close.
Latest institutional briefs
JSON
daily
The most recently published briefs with their headline, one-line bottom line, tone and mentioned tickers — the same public fields the site shows, in the order they were published.
/data/notes/latest.json
50 briefs · 25.2 KB · updated 2026-10-13
ProvenanceThe public fields of the most recently published briefs, in publication order, read from the same masked database view that serves logged-out visitors. The industry value is added by our classifier and is labelled as such inside the file, because who assigned a label decides how it may be cited.
Limitations- Headlines and bottom lines are our English renderings of third-party Chinese-language research. The argument belongs to the named source institution; cite that institution too.
- sentiment_score is our scoring of the source text, not the source’s own rating. Do not restate it as the research house’s view.
- key_data_points are figures taken from the source material. They are not independently verified by us.
- Only the newest 50 briefs are present, so this file is a window and not an archive. Use the per-day digests for history.
- excluded_fields lists every subscriber-only field by name and reason. A reader can tell what was withheld rather than having to guess.
Fields (4)
generated_at string- ISO 8601 build timestamp.
count number- Number of briefs included.
rows array- published_at, lake_id, title_en, bottom_line, sentiment_score, tickers[], key_data_points[], industry (our classifier’s sector id).
excluded_fields array- Names of the subscriber-only fields deliberately not included, with the reason.
Ticker index
JSON
daily (after each build)
Every ticker with at least one published brief, with its note count. Keys are abbreviated because this file is also loaded by the site’s own /tickers page — the schema below is the contract.
/data/tickers-index.json
2481 tickers · 110.6 KB · updated 2026-10-13
ProvenanceEvery ticker appearing in at least one published brief, with the count of briefs that mention it. Rebuilt from the same briefing database as the rest of the site; the symbol list is derived from tickers[] on the briefs themselves.
Limitations- A coverage count, not a ranking. A high count means our sources wrote about it often, not that it is more important, more liquid or better regarded.
- Company names are best-effort and may be empty for thinly covered symbols.
- Absence means "not mentioned in any brief we hold", not "not listed on the exchange". The index covers only names our sources named.
- Symbol suffixes follow the market of listing (.SZ / .SH / .SS / .HK) as they appear in the source material; they are not normalised across vendors.
Fields (3)
[].s string- Ticker symbol, e.g. 300308.SZ. Sorted ascending by code unit order.
[].n string- Company name (may be empty).
[].c number- Number of published briefs whose tickers[] contains this symbol. Always > 0 here.
Daily share images
PNG
daily on trading days (best-effort)
A ready-to-post picture of each day’s sector breakdown — the same content as the digest page, laid out for social feeds. Only exists for trading days where a digest was produced; days without one have no image, and the digest page simply shows no share link.
/daily/{YYYY-MM-DD}.png
10 images · 582.6 KB · updated 2026-10-13
ProvenanceA screenshot of that day’s digest page: rendered headlessly at 1080×1350 logical pixels, captured at 2× device scale, written next to the page it depicts.
Limitations- Best-effort by design. Production depends on a browser rendering channel, so some trading days have no image and the digest page simply shows no share link. A missing day is not a failed digest.
- The image follows the digest page exactly. It adds no figure that is not already in the JSON, so it is the wrong thing to parse — use the dataset files for anything computed.
- The updated_at on this entry is the PNG’s real file mtime, not the build time, because the image can legitimately lag the build by a day.
Fields (3)
(binary) png- PNG, 1080×1350 logical pixels captured at 2× device scale (2160×2700 px).
content string- Date, brief and sector counts, market breadth, and up to 5 sector cards each with tone distribution and a one-line representative view.
coverage string- Not every calendar day. Follow the <enclosure> in /feed.xml or the links listed at /daily for the days that have one.
RSS feed — daily digests
RSS 2.0
daily (after each build)
RSS 2.0 feed of the daily digests, newest first. Each item links to the full digest and carries a plain-text summary of that day’s sector breakdown.
/feed.xml
16 items · 38.6 KB · updated 2026-10-13
ProvenanceRSS 2.0 built from the same digest files the site publishes, newest first. The guid is the digest URL, which is stable and never reused, so subscribers do not see the same day twice.
Limitations- The description carries the same public text as the digest page and nothing more. Subscriber-only fields never appear in the feed.
- The <enclosure> is present only on days that have a share image, so a subscriber cannot rely on a picture every day.
- The feed window is bounded — it is a rolling list, not an archive. Use the per-day files at /data/daily for the full history.
Fields (6)
channel/item/title string- China Institutional Research Digest — YYYY-MM-DD
channel/item/link string- Absolute URL of the digest page.
channel/item/pubDate string- RFC 822 date.
channel/item/guid string- Stable, never reused: the digest URL.
channel/item/description string- Plain-text digest: brief count, sector breakdown with tone, and the top stories.
channel/item/enclosure string- Present only on days that have a share image: url, length (real byte count), type=image/png.
Terms of use
IIC-Data-1.0 (CC BY 4.0 compatible) — Free to use, quote and redistribute with attribution and a link back to the source URL. Released under Creative Commons Attribution 4.0 (CC BY 4.0); the requests below are not conditions of the licence.
Licence text: https://creativecommons.org/licenses/by/4.0/
- Attribution: credit "InsightIntoChina" and link to the source URL of the dataset or report you used. This is what CC BY 4.0 requires.
- Do not state or imply that InsightIntoChina endorses you, your product or your conclusions.
- Derived figures must state that they are derived — do not present our classification or scoring as your own measurement.
- Facts and figures originate from third-party Chinese-language institutional research. InsightIntoChina structures and translates them; it does not verify them.
- Nothing published here is a buy or sell call, a rating, a price target, or investment advice.
Requests — not conditions of the licence:
- We ask that you do not resell this as a standalone data product. This is a request, not a condition — the CC BY 4.0 licence above permits commercial use.
What is not here, and why. Subscriber-only fields — the full translation of the
original Chinese, the analyst's hidden reasoning, the extracted key numbers and the risk analysis — are
deliberately absent from every file on this page. They are what a subscription pays for, so publishing them
here would make the subscription meaningless. The briefs above carry only the public fields the site itself shows.
How sectors and scores are produced. Sector assignment comes from a
deterministic keyword classifier over each headline, with the body used only as a fallback. It is a navigation
aid, not an analyst judgment, and it is imperfect by design — which is why every digest reports how many briefs
it could not place. The divergence score is a percentile comparison, not a prediction;
the methodology page has the full definition.