{"version":"v1","contact":"data.info@csimarket.com","note":"All changes are additive unless marked [breaking]. Field names and response structures are stable within a major version. New fields may be added to existing endpoints without a version bump.","policy":{"deprecation_notice":"Minimum 90 days notice before any field removal or rename.","breaking_changes":"Breaking changes require a new major version (\/api\/v2\/*).","additions":"New fields and endpoints are non-breaking and may be added at any time.","null_fields":"Fields may return null when data is unavailable. Never omitted entirely."},"changelog":[{"version":"1.35.0","date":"2026-10-21","type":"addition","breaking":false,"summary":"Upcoming earnings calendar: per-company and market-wide announced release dates","description":"Two new endpoints expose when companies have said they will release results, read from their own advance 8-K announcements (Items 7.01\/8.01) and detected nightly. \/companies\/{identifier}\/earnings-calendar returns the current best guess per fiscal period plus meta.next_earnings_date; ?as_of=YYYY-MM-DD returns what was known on that date, with each row's status as it stood then. \/market\/earnings-calendar returns every announced date in a range (default next 30 days, max 92). Every row carries status (candidate, confirmed), confidence, cadence_check, the source 8-K and the evidence sentence: an announced date is a prediction, not a guarantee. Coverage is limited to companies that pre-announce their release date in an 8-K. The existing \/earnings-dates endpoint (when past results were announced) is unchanged.","endpoints_added":["GET \/api\/v1\/companies\/{identifier}\/earnings-calendar","GET \/api\/v1\/market\/earnings-calendar"]},{"version":"1.34.0","date":"2026-10-12","type":"feature","breaking":false,"summary":"BEHAVIOR CHANGE: relationship ?tier returns every provenance by default, and tier=reported now means the company's own announcements","description":"The relationship endpoints previously defaulted ?tier to the internal trust_tier REPORTED, which hid 35,551 of 120,656 active edges. Every card_network (1,302) and payment_network (1,904) edge sits outside that tier, so ?type=card_network returned 0 for EVERY company and read as missing data rather than as an applied filter. trust_tier also could not express what callers mean by \"reported\": it spanned the company's own announcements (70,659 edges), news (14,550), established_relationships (2,008) and even general_knowledge (33) in one bucket. ?tier now selects on the EVIDENCE SOURCE and defaults to all: all (default, nothing hidden) | reported (the company's own announcements; alias company_reported) | news | established_relationships (the long-standing curated graph) | general_knowledge (commonly known, not traced to a single document). The older trust_tier axis stays reachable as inferred and both, unchanged. Each edge still names its own provenance in the top-level tier field, now rendered as REPORTED, GENERAL_KNOWLEDGE or INFERRED, so a caller can split or discount rows without a second request. Applies to the relationships, typed convenience, history, concentration, dependency, compare and graph endpoints; the graph endpoints also previously rejected ?tier=general_knowledge with a 400 and now accept the full vocabulary. No field was added, removed or renamed and no response structure changed - this is a change to WHICH ROWS are selected by default, which is why it is documented as a behavior change rather than marked breaking.","parameters_added":["?tier=all (new default)","?tier=company_reported","?tier=news","?tier=established_relationships"],"migration":"Default responses now return MORE rows than before (e.g. NVDA relationships 79 -> 236). If you relied on the old default, pass ?tier=reported, but note its meaning narrowed: it is now the company's own announcements ONLY and no longer silently includes news- or legacy-sourced edges. For the widest set pass ?tier=all. Counts in meta.count and the per-type counts header move accordingly; each row's tier field tells you which provenance it came from."},{"version":"1.33.0","date":"2026-10-12","type":"feature","breaking":false,"summary":"Relationship graph: 12 dimensions, with card networks, payment networks, card processors and technology vendors","description":"The relationship graph now documents and serves 12 dimensions across every surface: the original competitor, customer, supplier, partner, investment, jv, licensing and distribution, plus card_network, payment_network, card_processor and tech_vendor. ?type accepts all 12 on the relationship endpoints, and the multi-hop graph endpoints accept all 12 in ?types= (they previously validated against the original 8 and returned a 400 for the rest). Coverage is honest and uneven at launch: card_network and payment_network carry real edges, tech_vendor carries very few, and card_processor is an accepted value with no edges yet - it returns an empty set rather than an error, and will populate as extraction continues. These dimensions are evidenced at the general_knowledge provenance, so they are returned under the new ?tier=all default and can be isolated with ?tier=general_knowledge.","parameters_added":["?type=card_network","?type=payment_network","?type=card_processor","?type=tech_vendor"]},{"version":"1.32.0","date":"2026-10-12","type":"addition","breaking":false,"summary":"The relationship graph is now discoverable in \/api\/datasets and \/api\/schema","description":"The commercial relationship graph was absent from the dataset catalogue, so \/api\/datasets listed 10 datasets and never mentioned it, and \/api\/schema\/relationships could not be resolved. It is now registered as dataset id \"relationships\" (Commercial Relationship Graph): 120,721 active edges over 9,441 companies, key_field rel_uid, 17 data endpoints, with a free sample at \/api\/test\/companies\/AAPL\/relationships. Because the graph is edge-shaped rather than a period-keyed table of numeric metrics, it carries period_types [] and has no flat field dictionary: field_count is 0 and analytics_endpoints is empty for this dataset BY DESIGN, it does not appear in \/api\/fields, and it is deliberately NOT an OData entity set - the OData surface is unchanged at 41 v1 entity sets, so Excel and Power BI are not offered a tabular feed that cannot be served. The machine-readable field and value vocabularies for the graph live in openapi.json, where ?type (12 values), ?tier (8), ?role (11), ?direction (3) and ?value_provenance (4) are published as enums."},{"version":"1.31.0","date":"2026-10-12","type":"fix","breaking":false,"summary":"BEHAVIOR CHANGE: meta_url is null, instead of a guaranteed 404, for datasets without a field dictionary","description":"\/api\/datasets and \/api\/discovery advertised a meta_url for every dataset, but \/api\/meta\/{dataset} only serves datasets that are registered in the field registry. Four catalogue datasets are not - insider_transactions, institutional_ownership, business_segments and relationships - because they are not period-keyed tables of numeric metrics, so their advertised meta_url was a guaranteed 404. meta_url is now the URL only when \/api\/meta\/{dataset} can actually serve a field dictionary, and null otherwise. The key is always present, so the response SHAPE is unchanged; only the value changes, and only for those four datasets. The other seven are byte-identical. schema_url is unchanged and stays a real URL for every dataset, because \/api\/schema serves all of them. This is presentation only: \/api\/meta and \/api\/schema continue to enforce their own behaviour independently, and calling \/api\/meta\/relationships directly still returns the same 404 as before.","migration":"If you follow meta_url programmatically, treat null as \"this dataset has no field dictionary\" and use schema_url or \/api\/datasets\/{id} instead. Previously the same situation returned a URL that answered 404."},{"version":"1.30.0","date":"2026-09-15","type":"breaking","summary":"Total debt now prefers a filer's own combined debt figure, fixing a real double-counting bug in net_debt_to_equity, net_debt_to_ebitda and the whole enterprise_value family","description":"The internal total-debt figure behind net_debt_to_equity and net_debt_to_ebitda (CompanyFinancialStrengthDefinition \/ IndustryFinancialStrengthDefinition) and behind enterprise_value and its full derivative family (enterprise_value_to_ebitda, enterprise_value_to_sales, enterprise_value_to_free_cash_flow, enterprise_value_to_invested_capital, their 5-year averages, z-scores, vs_history and vs_industry\/vs_naics\/vs_sic\/vs_isic\/vs_gics comparisons, on both the company and industry valuation endpoints) now prefers a filer's own reported combined short+long debt figure (the DebtLongtermAndShorttermCombinedAmount tag family) when the filer discloses one, instead of always summing the separate long-term and short-term debt line items. Real bug fixed: some filers report an \"including current maturities\" long-term debt variant that already subsumes the current portion, so summing it against the short-term debt line double-counted part of the balance - verified on Royal Caribbean (RCL), where the old summed figure reached $46.2B against the filer's own reported $22.8B combined debt. Falls back to the summed components exactly as before for any filer that does not report a combined figure. Affects roughly 1,000 companies that report a combined debt tag (and any industry\/sector aggregate they roll into) - affected values are LOWER than before wherever the old summed figure had been double-counting; every other company is unchanged. NOTICE WAIVED: this is a value correction on existing fields shipped without the 90-day notice, per the same standing product decision already applied to the 2026-09-08 interest-sign fix and the 2026-09-14 operating_income fix - it corrects data that was wrong at the source rather than renaming or removing anything. The 90-day notice remains in force for every field with live consumers going forward."},{"version":"1.29.0","date":"2026-09-14","type":"breaking","summary":"Income statement: operating_income now serves the real Operating Income (Loss); the operating cost line moves to the new operating_expenses field","description":"The income-statement endpoints (single-company, bulk, CSV export) had the two operating slots aliased backwards: operating_income served the source operating COST line (basis varies by filer - usually operating expenses excluding cost of revenue, for some filers total operating costs including it), while the real operating income was only available under operating_income_balance. Anyone computing operating margin from the obvious field name got the cost line instead of income (e.g. 17.4 percent instead of 32.6 percent). From this release: operating_income = Operating Income (Loss), a clean series cross-verified against segment operating-income sums and derived by the statement cascade as the balance of operations, so it is populated even when a filer does not tag a direct operating-income total. The cost line is now exposed honestly as the NEW field operating_expenses, whose description warns it is not operating income despite the underlying source column name. operating_income_balance is kept as a DEPRECATED duplicate of operating_income so consumers of the previously-correct field keep working; it will be removed in a future version. The point-in-time fundamentals endpoint gains matching friendly aliases (operating_income -> opbalance, operating_expenses -> operating) and its suggested-metric list now names opbalance; raw PIT slot names are unchanged. Cash-flow field docs now state that capex, net_cash_flow and free_cash_flow are SINGLE-QUARTER (3-month) values while the other flow lines are YTD-cumulative, and balance-sheet residual fields are documented as derived residuals that can lag the newest quarter. NOTICE WAIVED: the operating_income VALUE change ships without the 90-day notice per an explicit product decision - it corrects a field that was serving wrong data, and every day of dual-serving would keep the wrong number at the obvious name. The 90-day notice remains in force for the eventual removal of operating_income_balance."},{"version":"1.28.0","date":"2026-09-12","type":"breaking","summary":"Fundamental-ratio field names spelled out in full; two naming inconsistencies fixed; company and industry endpoints now share identical names for the same ratio","description":"Every abbreviated field name across the growth, profitability, valuation, financial-strength, efficiency, management-effectiveness and per-share endpoints - for both \/api\/v1\/companies\/{id}\/* and \/api\/v1\/industries\/{id}\/* - is now a fully self-describing name. Renamed: pe -> price_to_earnings, ps -> price_to_sales, pb -> price_to_book, pcf -> price_to_cashflow, ev_ebitda -> ev_to_ebitda, ev_sales -> ev_to_sales, eps_* -> earnings_per_share_*, fcf_* -> free_cash_flow_* (e.g. fcf_yield -> free_cash_flow_yield, fcf_per_share -> free_cash_flow_per_share), ocf_per_share -> operating_cash_flow_per_share, rd_* -> research_and_development_*, sga_* -> selling_general_administrative_*, cape_industry_pe -> cape_industry_price_to_earnings. Every derivative of a renamed metric (_5y_avg, _zscore, _vs_industry\/_naics\/_sic\/_isic\/_gics, _median, _p10\/_p25\/_p75\/_p90, _stddev, _quartile_spread, _mad, _cross_industry_percentile) carries the same new base name; the _yoy\/_seq\/_cagr\/_zscore\/etc. suffix vocabulary itself is unchanged. Two real naming inconsistencies (not stylistic) are fixed in the same release: (1) industry valuation called net-debt-to-EBITDA \"net_debt_ebitda\" while industry financial-strength already correctly called the identical ratio \"net_debt_to_ebitda\" - valuation now matches; (2) on both company and industry management-effectiveness, the return_on_assets\/return_on_equity\/return_on_invested_capital base fields were already spelled out, but their own _vs_industry\/_median\/_p25\/_reported_count sibling fields reverted to roa_\/roe_\/roic_ prefixes - every sibling now uses the same full base name as its own parent field. NOTICE WAIVED: shipped in place on \/api\/v1\/* without the standard 90-day deprecation notice, per an explicit product decision - every existing integration reading an old abbreviated key breaks on this release; there is no dual-serving period and no \/api\/v2 fork for this change. The 90-day notice remains in force for every other field with live consumers going forward. The complete old-name to new-name mapping is enumerated in \/api\/fields for every affected endpoint (each Definition class is the single source of truth for both the live response shape and the field dictionary)."},{"version":"1.27.0","date":"2026-09-08","type":"breaking","summary":"Net interest now carries the US-GAAP sign (negative = net interest expense); interest coverage redefined","description":"The income-statement field net_interest (source column interestnet) is now interest income MINUS interest expense with the US-GAAP credit sign: a NEGATIVE value is a net interest expense (the common case), a positive value is net interest income. Until 2026-09-08 the column was expense-positive (positive = net expense). Every stored row, historical rows included, was converted in place, so a series is consistent across the change. The same balance-type rule now governs every income-statement field at ingestion: costs, expenses and interest expense are positive, revenue and interest income are positive, net lines keep their sign under that rule. Derived: ebit = pretax income - net_interest (the net expense added back), ebitda = ebit + depreciation + amortization; unchanged in value. interest_coverage (companies, industries, sectors) is EBITDA (TTM) divided by the net interest EXPENSE and is now null for a net receiver of interest - it used to be described as EBIT \/ interest expense and could come back negative for net receivers. operating_cash_flow_to_interest divides by the same net interest expense. NOTICE WAIVED: this is a value-sign change on an existing field shipped without the 90-day notice; it corrects a convention error rather than renaming or removing anything, and the field descriptions in \/api\/fields, the OpenAPI document and the MCP conventions resource were updated in the same release. The 90-day notice remains in force for every field with live consumers."},{"version":"1.26.0","date":"2026-09-05","type":"addition","summary":"Multi-hop relationship graph: neighborhood, paths, exposure","description":"Three graph endpoints computed entirely server-side over the disclosed relationship graph, traversing the both-views UNION at every hop (a supply relationship is one edge regardless of which side disclosed it - the same direction rule that fixed supplier-dependency and relationship-risk in 1.23.x). GET \/api\/v1\/companies\/{identifier}\/graph\/neighborhood serves the N-hop (1-3) neighborhood with per-node resolution status, per-edge disclosure provenance and a coverage block (unresolved counterparties are TERMINAL and counted, never dropped - only ~35% of edges resolve to a public company on both sides today). GET \/api\/v1\/graph\/paths ranks relationship paths between two companies by ONE documented rule (path_strength = minimum edge confidence along the path, ties by fewer hops; formula in meta). GET \/api\/v1\/companies\/{identifier}\/graph\/exposure serves the indirect-exposure set with an industry rollup over resolved nodes; weighted exposure propagation is data-blocked at current value coverage and returns a typed not_available rather than an approximation. ?as_of= filters on effective_from <= as_of (approximate point-in-time via edge intervals, stated as such - not vintage-grade replay). All bounds are flagged, never silent. All three require relationships.read."},{"version":"1.25.0","date":"2026-09-04","type":"addition","summary":"Research dossier: GET \/api\/v1\/companies\/{identifier}\/research-dossier","description":"One composed research dossier with 13 controlled sections - overview, financials, growth, profitability, valuation, market (regime + the company's sector rotation row), factors, risk, peers, industry, relationships, supply_chain, changes - each built 1:1 from its existing source endpoint, named in the section's source field; the dossier computes nothing those endpoints do not already return. The endpoint requires companies.read; each section is additionally gated by its own data scope, and a licence missing a scope receives that section as {status: not_licensed, requires_scope: ...} - a partial dossier, never a whole-request 403. ?sections= filters, ?period=TTM|FY|Q applies to the fundamentals sections, ?peer_context=1 enriches up to 5 similar companies with their valuation blocks (requires valuation.read). A section that fails internally degrades to a typed error and is listed in meta.sections_errored - it never fails the whole request. Typed analytical statuses per the 1.23.0 contract throughout."},{"version":"1.24.0","date":"2026-09-04","type":"addition","summary":"Market intelligence: \/market\/regime, \/sectors\/rotation, \/industries\/rotation","description":"Three analytical endpoints (all market.read, all date-aware per the 013\/015 date model). GET \/api\/v1\/market\/regime classifies the market regime by a documented, rules-based vote over cap-weighted return windows, breadth, the VIX band and cross-sectional dispersion; the exact formula is returned in meta.formula and regime_confidence is a deterministic agreement share, never a model probability. GET \/api\/v1\/sectors\/rotation and GET \/api\/v1\/industries\/rotation serve per-group return windows, breadth, rank by return_20_days, prior_rank\/rank_change over ~20 trading sessions (prior ranks always use point-in-time quarter-end weights) and a quadrant classification vs the whole market (leaders \/ improving \/ deteriorating \/ laggards + rotation_candidate). All three emit typed analytical statuses per the 1.23.0 contract - a group with an incomplete return window is unranked with insufficient_history, never silently ranked last."},{"version":"1.23.1","date":"2026-09-04","type":"fix","summary":"relationship-risk supplier factors now include inverse-view suppliers","description":"GET \/api\/v1\/companies\/{identifier}\/relationship-risk computed its supplier factor sections from DIRECT supplier edges only (the company's own filings) - the same direction gap fixed on supplier-dependency in 1.23.0. It now unions the inverse view (a supplier's filing naming the company as its customer), one row per counterparty, and reports direct_disclosed_count \/ inverse_disclosed_count in supplier_dependency_factors. Supplier factor counts will GROW for companies whose suppliers disclose the relationship from their side; roles and sole\/single-source flags still attach to direct edges only."},{"version":"1.23.0","date":"2026-09-04","type":"addition","summary":"Typed analytical statuses + supplier_concentration block on supplier-dependency","description":"Two additions. (1) Typed-null contract for the analytical surface: analytical fields that cannot be computed now carry a typed status - not_applicable, not_available, not_yet_computed, insufficient_history or missing_source_data - instead of a bare null, plus coverage counts wherever a figure aggregates, so a machine reader can distinguish \"structurally never applies\" from \"inputs missing\". Applies to NEW and EXTENDED analytical endpoints from this version; existing endpoints are unchanged. (2) GET \/api\/v1\/companies\/{identifier}\/supplier-dependency now serves a supplier_concentration block with per-basis measures that are labeled and never blended: basis_counts (relationship counts, no economic weighting), basis_disclosed_values (top-1\/3\/5 shares and HHI computed over the value-covered subset ONLY, with observation and coverage counts; requires relationships.value.read), basis_revenue_share (not_available by design - the derived pipeline covers customer revenue concentration only, see \/customer-concentration), and an aggregate per-edge evidence-confidence summary with the trust-tier mix. The zero-supplier case now returns status not_available with an explicit absence-is-unknown note. FIX included: supplier-dependency previously read only DIRECT supplier edges (the company's own filings); it now also includes the inverse view (a supplier's filing naming the company as its customer - the dominant signal for large customers), merged one row per counterparty with per-supplier disclosure provenance (reported_by_viewed_company, corroborated) and direct\/inverse\/corroborated coverage counts - matching what \/companies\/{identifier}\/suppliers already served. Supplier lists on this endpoint will GROW for companies whose suppliers disclose the relationship from their side."},{"version":"1.22.2","date":"2026-09-04","type":"fix","summary":"GICS removed from classification-system listings (metadata only)","description":"GICS is no longer advertised as a classification system in \/api\/discovery, \/api\/methodology or the OpenAPI spec description. The gsic classification path was retired in 1.22.0 and no GICS data exists anywhere in the platform, so the listings were misleading. Metadata-only change: no endpoint, query parameter or response field changed in this version. The *_vs_gics response fields (always null) are unchanged here; their eventual removal will follow the deprecation policy above."},{"version":"1.22.1","date":"2026-09-04","type":"fix","summary":"usa30 (DJI) multi-day return windows now served - benchmark history backfilled","description":"GET \/api\/v1\/indices\/usa30 previously returned null for return_5_days through return_252_days because the DJI benchmark series was sparse (107 sessions on file). The DJI daily history has been backfilled to 1992-01-02 (8,730 sessions, Yahoo ^DJI via the stock price engine, derived windows recomputed), and the suppression is lifted: all multi-day return windows, average_true_range_14_days and relative_volume are now served for usa30, current and historical. No field names or shapes changed; values that were null by suppression now carry data. usa30 remains benchmark-only (constituents null by design - no membership data exists for it)."},{"version":"1.22.0","date":"2026-09-04","type":"breaking","summary":"Price, index and performance response fields renamed to descriptive names","description":"Response field names on the share-price, index and performance endpoints introduced in 1.21.0 changed from abbreviated to descriptive forms: return_1d -> return_1_day, return_5d -> return_5_days, and likewise return_20d\/60d\/120d\/252d -> return_20_days\/60_days\/120_days\/252_days; atr_14 -> average_true_range_14_days. Calendar-window returns are named return_1_week, return_1_month, return_quarter_to_date, return_year_to_date and return_trailing_12_months. GET \/api\/v1\/market\/movers now accepts only the descriptive ?window= values 1_day, 5_days, 20_days, 60_days, 120_days and 252_days; the previous abbreviated forms (e.g. ?window=20d) return 400. This is a RESPONSE-FIELD rename only - the underlying price-table column names are unchanged, and no values, units or semantics were altered. NOTICE WAIVED: this rename shipped without the 90-day deprecation notice stated in the policy above. The waiver was deliberate and is recorded here rather than left implicit - the renamed surface had been live for one day and had no API consumers at the time, so no integration could be broken. The 90-day notice remains in force for every field that has live consumers."},{"version":"1.21.0","date":"2026-09-03","type":"addition","summary":"Share price, index and market-performance endpoints (7 new routes)","description":"Seven new read endpoints. Share prices (stocks.read): GET \/api\/v1\/stocks\/{identifier}\/price returns a quote for the latest or any historical trading day, and GET \/api\/v1\/stocks\/{identifier}\/history returns the daily OHLCV series. Market performance and breadth (market.read): GET \/api\/v1\/market\/performance, GET \/api\/v1\/market\/movers, GET \/api\/v1\/indices\/{index}, GET \/api\/v1\/sectors\/performance and GET \/api\/v1\/industries\/performance. Every endpoint accepts ?date=YYYY-MM-DD, which resolves to the last trading day at or before that date; meta.as_of always reports the date actually served. All seven are snapshot-based (meta.time_basis=\"snapshot\"); passing a period_type other than D returns 400. Aggregate endpoints return cap-weighted trading-day windows (return_1d\/5d\/20d\/60d\/120d\/252d, as percentages) for any date from 2013-04-01 onward. calendar_returns (1w\/1m\/qtd\/ytd\/ttm) are served ONLY on the latest snapshot date and are null for historical dates rather than being back-filled. meta.weight_basis states whether current or point-in-time quarter-end weights were used. For \/indices\/{index}, the benchmark quote (GSPC\/NDX\/RUT) is available historically, but constituent aggregates are current-date only - no point-in-time index membership exists, so a historical constituent request returns null with an explanatory meta note; likewise \/market\/movers with an ?index filter and a historical date returns 400. Quote fields use English names: open\/high\/low\/close, previous_close, change, change_percent, a returns block, volume, dollar_volume, typical_price and atr_14 - the return_* and atr_14 names shipped in this version were renamed to descriptive forms in 1.22.0. typical_price is (high+low+close)\/3, not a volume-weighted average price. Index codes such as GSPC resolve as identifiers alongside tickers and CIKs; DJI multi-day return windows are suppressed while its benchmark feed is sparse."},{"version":"1.20.0","date":"2026-07-30","type":"breaking","summary":"License plan model reindexed: 5 tiers -> 3 (Individual\/Professional\/Enterprise)","description":"product_plan moves from a 5-value scheme (0=Basic, 1=Standard, 2=Premium, 3=Institutional, 4=Enterprise) to a 3-value scheme (0=Individual, 1=Professional, 2=Enterprise), returned as-is in GET \/api\/v1\/license\/status. Every tier now carries the full fundamentals + quant + stocks scope set; bulk.read (bulk\/export access) is granted to Professional and Enterprise only. Existing licenses previously on plan 3 (Institutional) were migrated to plan 1 (Professional), same rate limit (300\/min) and bulk access as before, under the new plan number. Professional additionally introduces a location lock (enforce_ip): the license's first authenticated request auto-captures the caller's IP as its allowed range going forward.","breaking":true},{"version":"1.19.0","date":"2026-07-21","type":"feature","summary":"Free-tier sample endpoints now return up to 8 periods (~2 years of TTM), was 1","description":"The keyless free-evaluation endpoints at \/api\/test\/industries\/201\/{growth|profitability|valuation|financial-strength|efficiency|management-effectiveness|dashboard} now return the 8 most-recent periods per analytic (newest-first) instead of a single latest period. The newest TTM period is only ~2% into its reporting cycle (metrics still mostly null), so the previous single-period sample surfaced mostly nulls; returning ~2 years of TTM history gives evaluators fully populated data. The row cap is now one config value (config\/api.php => sample_period_limit, default 8), applied to every sample endpoint and period type (TTM\/Q\/FY) and reported as meta.period_limit (=8) in every sample response. meta.sample stays true; auth, scopes and the exposed industry (201) are unchanged. The consolidated bundle at GET \/api\/industry-sample reflects the same cap.","breaking":false},{"version":"1.18.0","date":"2026-07-09","type":"feature","summary":"OData full-surface mirror: 18 test + 41 v1 entity sets matching \/api\/v1\/ exactly","description":"Both OData tiers now mirror the full \/api\/v1\/ REST surface. Test tier (free): 18 entity sets: Industries_List\/Overview\/6 analytics\/Dashboard + Sectors same. V1 tier (licensed): 41 entity sets covering all industry, sector, and company analytics, Companies_PerShare, Scores, Zscores, QuantProfile, Signals (all cross-sectional from company_fundamental_ratios), all 4 Companies_MarketStructure_* entries, 3 statement sets, Companies_QuantFactors, QuantFactors_Screener. Market-structure and quant-factors sets are declared in EDMX but redirect to REST via @odata.note. Test tier entity set names changed: IndustryGrowth \u2192 Industries_Growth etc.","breaking":["test tier: entity set names changed (e.g. IndustryGrowth \u2192 Industries_Growth, SectorGrowth \u2192 Sectors_Growth)"],"migration":"Update \/api\/odata\/test\/ callers to new plural names. V1 names from 1.17.0 unchanged. QuantFactors \u2192 Companies_QuantFactors; MarketStructure_Technical \u2192 Companies_MarketStructure_Technical; MarketStructure_Advanced \u2192 Companies_MarketStructure_Advanced."},{"version":"1.17.0","date":"2026-07-09","type":"feature","summary":"OData v1 entity set expansion: 24 sets mirroring \/api\/v1\/ + FieldRegistry EDMX","description":"The gated OData v1 tier (\/api\/odata\/v1\/) now exposes 24 entity sets that mirror \/api\/v1\/ path segments exactly (plural, underscore-separated). Industries_Growth through Industries_ManagementEffectiveness (6 sets), Sectors_Growth through Sectors_ManagementEffectiveness (6 sets): cross-sectional industry\/sector data. Companies_Growth through Companies_ManagementEffectiveness (6 sets): cross-sectional data from company_fundamental_ratios; supports $filter=ticker eq 'AAPL' to scope to one company. Companies_IncomeStatements, Companies_BalanceSheets, Companies_CashFlow (3 sets): declared in service document + EDMX; require $filter=ticker eq 'AAPL'. QuantFactors, MarketStructure_Technical, MarketStructure_Advanced (3 sets): declared in service document + EDMX; use $filter=ticker eq 'AAPL' or the REST screener. V1 $metadata EDMX is now generated from FieldRegistry field definitions, keeping property types and names automatically in sync with \/api\/meta, \/api\/fields, and \/api\/schema. The test tier (\/api\/odata\/test\/) is unchanged: 12 entity sets, existing names, existing EDMX.","entity_sets_v1":["Industries_Growth","Industries_Profitability","Industries_Valuation","Industries_FinancialStrength","Industries_Efficiency","Industries_ManagementEffectiveness","Sectors_Growth","Sectors_Profitability","Sectors_Valuation","Sectors_FinancialStrength","Sectors_Efficiency","Sectors_ManagementEffectiveness","Companies_Growth","Companies_Profitability","Companies_Valuation","Companies_FinancialStrength","Companies_Efficiency","Companies_ManagementEffectiveness","Companies_IncomeStatements","Companies_BalanceSheets","Companies_CashFlow","QuantFactors","MarketStructure_Technical","MarketStructure_Advanced"],"breaking":false,"migration":"Existing callers of \/api\/odata\/v1\/{entity} using old names (IndustryGrowth, SectorGrowth) should update to the new plural names (Industries_Growth, Sectors_Growth). The test tier entity set names are unchanged."},{"version":"1.16.0","date":"2026-07-09","type":"feature","summary":"OData 4.0 two-tier split (test\/v1) + IEEE754 decimal fix","description":"The OData 4.0 service is now split into two tiers matching the REST API structure. GET \/api\/odata\/test\/: free, no API key, 12 industry + sector entity sets. Paste into Excel \"From OData Feed\" with no credentials. GET \/api\/odata\/v1\/: gated, requires X-API-Key (same ValidateLicense middleware as \/api\/v1\/*). Same 12 entity sets for now; company\/quant entity sets will be added here as they are built. Each tier has its own $metadata EDMX and entity set list. Also fixes IEEE754Compatible=true type conflict: all Edm.Decimal values are now always returned as JSON strings (e.g. \"15.5\" not 15.5) so the Excel OData connector accepts them without a type error. The EDMX declares each field as Edm.Decimal so Excel casts back to number correctly. Backward-compat unversioned \/api\/odata\/ routes are preserved and delegate to the test tier.","endpoints_added":["GET \/api\/odata\/test\/","GET \/api\/odata\/test\/$metadata","GET \/api\/odata\/test\/{entity}","GET \/api\/odata\/v1\/","GET \/api\/odata\/v1\/$metadata","GET \/api\/odata\/v1\/{entity}"],"breaking":false,"migration":"Existing \/api\/odata\/{entity} callers are unaffected (unversioned routes still work). Update Excel OData Feed URL to \/api\/odata\/test\/ (free) or \/api\/odata\/v1\/ (licensed) for tier-specific service documents."},{"version":"1.15.0","date":"2026-07-09","type":"feature","summary":"Proper OData 4.0 service at \/api\/odata\/ (Excel-ready)","description":"A full OData 4.0 service is now available at GET \/api\/odata\/. It exposes 12 entity sets (6 industry analytics + 6 sector analytics) backed by ind_fundamentals_ratio. Three endpoints: (1) GET \/api\/odata\/: service document listing all entity sets; (2) GET \/api\/odata\/$metadata: programmatically generated EDMX XML with EntityType definitions, property types (Edm.String\/Edm.Int32\/Edm.Decimal), and EntityContainer; (3) GET \/api\/odata\/{entity}: data endpoint with OData query options: $filter (field eq\/ne\/gt\/lt\/ge\/le value, and-chained), $select (comma-separated field list, applied post-query), $top (default 100, max 500), $orderby (field asc|desc). Every row carries industry_id\/sector_id as the entity key, period_label, and all analytic columns for the entity set. Public: no API key required. Paste \/api\/odata\/ into Excel \"Data \u2192 Get Data \u2192 From OData Feed\" for a live connection. Also: ?format=odata removed from the per-endpoint middleware (it was not a valid OData service, no $metadata, rejected by Excel). The middleware now supports ?format=csv only; use \/api\/odata\/ for Excel.","endpoints_added":["GET \/api\/odata\/","GET \/api\/odata\/$metadata","GET \/api\/odata\/IndustryGrowth","GET \/api\/odata\/IndustryProfitability","GET \/api\/odata\/IndustryValuation","GET \/api\/odata\/IndustryFinancialStrength","GET \/api\/odata\/IndustryEfficiency","GET \/api\/odata\/IndustryManagementEffectiveness","GET \/api\/odata\/SectorGrowth","GET \/api\/odata\/SectorProfitability","GET \/api\/odata\/SectorValuation","GET \/api\/odata\/SectorFinancialStrength","GET \/api\/odata\/SectorEfficiency","GET \/api\/odata\/SectorManagementEffectiveness"],"parameters_removed":["?format=odata: removed from per-endpoint middleware (was a non-standard envelope, not a valid OData 4.0 service)"],"breaking":false,"migration":"Replace ?format=odata calls with the \/api\/odata\/{entity} endpoints. The OData service is richer: full $metadata, $filter, $select, $top, $orderby, and Excel-compatible."},{"version":"1.14.0","date":"2026-07-09","type":"feature","summary":"Response format negotiation: ?format=odata and ?format=csv on all endpoints","description":"A single global middleware (FormatResponseMiddleware) now intercepts ?format= on every JSON-returning endpoint, no per-controller changes. ?format=odata rewraps the response as an OData v4 envelope ({@odata.context, @odata.count, @meta, value:[...]}) with Content-Type application\/json;odata.metadata=minimal, enabling Excel \"Get Data \u2192 From OData Feed\" with zero configuration. ?format=csv streams a UTF-8 CSV download: header row from the first data row's field names, filename derived from the request path (e.g. industries-201-growth.csv), null values as empty cells, numbers unquoted unless they contain commas. ?format=csv is not supported on \/dashboard endpoints (returns 400 with guidance to use a single-analytic endpoint instead); ?format=odata is supported on all endpoints including dashboard. A free keyless evaluation suite is also added at \/api\/test\/industries\/201\/{endpoint} (all 8 industry endpoints, period caps TTM=1\/Q=5\/FY=2, 60 req\/min); the consolidated bundle at GET \/api\/industry-sample remains available.","parameters_added":["?format=odata: all JSON endpoints (industries, sectors, companies, quant-factors, screener, statements, bulk, license, metadata)","?format=csv: all single-section endpoints; \/dashboard returns 400 with guidance"],"endpoints_added":["GET \/api\/test\/industries\/201","GET \/api\/test\/industries\/201\/growth","GET \/api\/test\/industries\/201\/profitability","GET \/api\/test\/industries\/201\/valuation","GET \/api\/test\/industries\/201\/financial-strength","GET \/api\/test\/industries\/201\/efficiency","GET \/api\/test\/industries\/201\/management-effectiveness","GET \/api\/test\/industries\/201\/dashboard"],"breaking":false},{"version":"1.13.0","date":"2026-06-25","type":"feature","summary":"Quant-factors cross-sectional screener","description":"New gated endpoint GET \/api\/v1\/quant-factors\/screener: screen the quant-factors universe for one period (period_type=D live snapshot, default; or Q with tm=YYYYPP). Numeric range filters (<field>_min \/ <field>_max) and exact filters (<field>=value) over a whitelisted field set (listed at \/api\/schema\/quant_factors, screener block); whitelisted sort\/order, capped pagination (limit default 50, max 200, offset). Fail-loud: an unknown filter param or a non-numeric value on a numeric filter returns 400, a mistyped filter never silently returns the unfiltered universe. By default returns the live universe (is_active_company=1 fresh price within ~5 trading days, is_operating_company=1, is_price_reliable=1); include_inactive=1 \/ include_unreliable=1 relax that. Each row carries the same fields + held block as the single-cik endpoint.","endpoints_added":["GET \/api\/v1\/quant-factors\/screener"],"breaking":false},{"version":"1.12.0","date":"2026-06-25","type":"feature","summary":"Quant Factors & Risk endpoint launched","description":"New gated endpoint GET \/api\/v1\/companies\/{cik}\/quant-factors over the daily market-structure model: style factor exposures (size\/value\/quality\/growth\/momentum\/low-vol\/liquidity), a composite factor score with cross-sectional percentile rank, factor z-scores, percentile-ranked risk scores (tail\/stress\/crash\/trend\/concentration), market structure (cap weight, ranks), betas\/alphas vs US-500\/Tech\/Nasdaq\/Small-2000 benchmarks, index membership, institutional\/insider\/short-interest ownership, liquidity, technicals, and regime labels. period_type D = live daily snapshot (default) or Q = quarterly history (2013 Q1 onward; ?tm=YYYYPP for a single quarter, PP in {00,25,50,75}). Every advertised field is always present (null where unavailable). Rows the pricing model flags as unreliable return the affected fields as null with a per-row \"held\" object stating the reason (dual_class_basis_pending, price_magnitude_implausible, return_discontinuity), a garbage value is never served. A new percentile unit (0-100 cross-sectional rank, not a probability) is documented in the value scale. Registered across the public metadata layer (\/api\/discovery, \/api\/datasets, \/api\/schema\/quant_factors, \/api\/meta\/quant_factors, \/api\/fields, \/api\/openapi.json).","endpoints_added":["GET \/api\/v1\/companies\/{cik}\/quant-factors"],"breaking":false},{"version":"1.11.1","date":"2026-06-13","type":"fix","summary":"Industry & sector \/dashboard endpoints restored (growth sub-key 500)","description":"The industry and sector \/dashboard endpoints returned a 500 for all entities because their combined query omitted several growth columns that the growth sub-key reads, the free_cash_flow \/ capex \/ eps reported-counts and the sequential (quarter-over-quarter) beat\/miss percentages, surfacing first as free_cash_flow_reported_count. The dashboard queries now fetch all of them, so \/dashboard returns the full growth section exactly like the standalone \/growth endpoint. Defensive null-coalescing was also added to the growth field mapper so a future column omission degrades to null instead of a 500. Values only; no field renames or shape changes.","breaking":false},{"version":"1.11.0","date":"2026-06-13","type":"data-quality","summary":"Inventory-derived efficiency ratios now null at financial-sector aggregates","description":"At financial-sector industry and sector aggregates (banks, insurers, REITs), the inventory-derived efficiency ratios are now returned as null rather than a computed value. The inventory reported at these aggregates is real but belongs to a few diversified conglomerate-insurers (e.g. Berkshire Hathaway in P&C, Cigna \/ Express Scripts in A&H), which makes the derived turnover \/ days \/ cycle ratios structurally meaningless for the sector as a whole. This applies uniformly across all classification systems (CSIMarket sector 700 and industries 701-799, NAICS 52xx plus 5311, SIC 6000-6799, ISIC 64\/65\/66\/68), on the efficiency and dashboard endpoints and in the industries\/sectors bulk exports. Total-Market and S&P-500 aggregates are never affected. The raw inventory balance-sheet figure itself is unchanged; only these derived ratios are nulled. Non-financial sectors are unaffected.","fields_nulled_for_financials":["inventory_turnover_cogs","days_inventory_outstanding","operating_cycle_days","cash_conversion_cycle","cash_conversion_cycle_median","cash_conversion_cycle_stddev","efficiency_score"],"breaking":false},{"version":"1.10.0","date":"2026-06-10","type":"addition","summary":"New CURRENT period_type for live valuation multiples (valuation only)","description":"Added an opt-in CURRENT period_type to the valuation endpoints: the current\/spot market price divided by the last complete trailing-twelve-month fundamentals (P\/E, P\/S, P\/B, P\/CF, EV\/EBITDA, EV\/Sales). It is a single rolling snapshot per entity (overwritten each cycle, not a historical series). CURRENT is valuation-only; other datasets do not carry it. Additive and non-breaking: the default period_type stays TTM; request it with ?period_type=CURRENT.","breaking":false},{"version":"1.9.0","date":"2026-06-10","type":"breaking","summary":"Valuation field names standardized to the short metric stem","description":"Valuation dispersion and ranking output fields were renamed to one short stem per metric, dropping the redundant _ratio \/ _to_ infixes so every derivative reads off the same stem (pe, ev_ebitda). The underlying DB columns and the ingestion pipeline were migrated to match, and empty unused alias columns were removed. Values are unchanged; only field names changed.","fields_renamed":["pe_ratio_median -> pe_median, pe_ratio_p25 -> pe_p25, pe_ratio_p75 -> pe_p75","pe_ratio_stddev -> pe_stddev, pe_ratio_quartile_spread -> pe_quartile_spread, pe_ratio_mad -> pe_mad","pe_ratio_cross_industry_percentile -> pe_cross_industry_percentile (sectors: pe_cross_sector_percentile)","ev_to_ebitda_median -> ev_ebitda_median, ev_to_ebitda_p25 -> ev_ebitda_p25, ev_to_ebitda_p75 -> ev_ebitda_p75","ev_to_ebitda_stddev -> ev_ebitda_stddev, ev_to_ebitda_quartile_spread -> ev_ebitda_quartile_spread","free_cash_flow_yield -> fcf_yield"],"migration_note":"If you consume the valuation endpoints, update field references to the new names; the old names are no longer returned.","breaking":true},{"version":"1.8.0","date":"2026-06-01","type":"breaking","summary":"Monetary and share-count fields corrected from millions to raw units","description":"All absolute monetary values now return raw USD rather than millions-of-dollars, and share-count fields now return raw share counts rather than millions-of-shares. Affects both ratio tables (company_fundamental_ratios, ind_fundamentals_ratio) and all six financial statement controllers, the bulk and per-company controllers for each of the three statement datasets: income statements, balance sheets, and cash flow statements. Ratios, margins, per-share values, percentiles, and dividends_in_millions are unchanged.","migration_note":"Divide previously returned monetary values by 1,000,000 to reproduce the old scale. Example: Apple annual revenue was ~380,000 (old, millions); it is now ~380,000,000,000 (raw USD). Apply the same divisor to share-count fields.","scaled_fields":["All absolute monetary fields (revenue, net_income, ebitda, assets, debt, cash, \u2026)","Share-count fields: outshares, dilutedavg, basic_shares, diluted_shares"],"fields_unchanged":["All per-share fields (eps_*, *_per_share, fcf_per_share, ocf_per_share)","All ratio and multiple fields (pe, ps, pb, debt_to_equity, interest_coverage, \u2026)","All percentage and margin fields (*_margin, *_yoy, *_seq, effective_tax_rate, \u2026)","All percentile and rank fields (*_percentile, *_rank_*)","dividends_in_millions (explicitly million-denominated by name)"],"breaking":true},{"version":"1.7.1","date":"2026-06-01","type":"fix","summary":"Identifier parity, z-score ETL fix, and income statement controller fix","description":"Three targeted corrections: (1) lei, isin, and cusip global security identifiers now appear on company list\/detail endpoints and all six statement controllers via a new CompanyIdentifiers shared definition, the shared definition provides parity across both the per-company statement endpoints and the bulk dataset endpoints. (2) The z-score ETL --hist argument was a dead no-op, leaving all historical z-score columns unpopulated; corrected. (3) The bulk income statement controller was reading from company_fundamental_ratios instead of the dedicated income statement table; rebuilt against the correct source.","fields_added":["lei: GET \/api\/v1\/companies (inline), GET \/api\/v1\/companies\/{cik} (inline), and meta.identifiers block on all six statement endpoints (per-company: \/companies\/{cik}\/income-statements, \/companies\/{cik}\/balance-sheets, \/companies\/{cik}\/cash-flow-statements; bulk: \/income-statements, \/balance-sheets, \/cash-flow-statements)","isin: same coverage","cusip: same coverage"],"internal_fixes":["ETL: --hist flag now correctly triggers historical z-score column population","BulkIncomeStatementController: rebuilt to read from income statement table, not company_fundamental_ratios"],"breaking":false},{"version":"1.7.0","date":"2026-05-30","type":"feature","summary":"Public metadata layer launched","description":"Added eight public endpoints for API discovery, schema exploration, and coverage verification. No license required.","endpoints_added":["GET \/api\/discovery","GET \/api\/coverage","GET \/api\/schema\/{dataset}","GET \/api\/status","GET \/api\/changelog","GET \/api\/methodology","GET \/api\/openapi.json","GET \/api\/docs"],"breaking":false},{"version":"1.6.0","date":"2026-05-15","type":"feature","summary":"Company scores, z-scores, quant profile, and signals endpoints","description":"Added six new company endpoints covering factor scores, Piotroski\/Beneish\/Altman forensic scores, cross-sectional z-scores, 5-year statistical profiles, and probabilistic signals.","endpoints_added":["GET \/api\/v1\/companies\/{cik}\/scores","GET \/api\/v1\/companies\/{cik}\/zscores","GET \/api\/v1\/companies\/{cik}\/quant-profile","GET \/api\/v1\/companies\/{cik}\/signals","GET \/api\/v1\/companies\/{cik}\/per-share"],"breaking":false},{"version":"1.5.0","date":"2026-04-20","type":"feature","summary":"Cross-industry and cross-sector percentile rankings added to all endpoints","description":"All industry and sector endpoints now include cross-industry\/cross-sector percentile columns for key metrics (revenue_yoy, operating_margin, net_margin, debt_to_equity, interest_coverage, pe_ratio, ev_ebitda, earnings_yield, ev_sales, return_on_equity, return_on_invested_capital).","fields_added":["revenue_yoy_cross_industry_percentile","revenue_3y_cagr_cross_industry_percentile","operating_margin_cross_industry_percentile","net_margin_cross_industry_percentile","debt_to_equity_cross_industry_percentile","interest_coverage_cross_industry_percentile","pe_ratio_cross_industry_percentile","ev_ebitda_cross_industry_percentile","ev_sales_cross_industry_percentile","earnings_yield_cross_industry_percentile","return_on_equity_cross_industry_percentile","...and sector equivalents (_cross_sector_percentile)"],"breaking":false},{"version":"1.4.0","date":"2026-03-10","type":"feature","summary":"Company fundamentals API launched","description":"Full company fundamentals coverage added with growth, profitability, valuation, financial strength, efficiency, management effectiveness, and dashboard endpoints. Supports TTM, FY, and Q period types.","endpoints_added":["GET \/api\/v1\/companies","GET \/api\/v1\/companies\/{cik}","GET \/api\/v1\/companies\/{cik}\/growth","GET \/api\/v1\/companies\/{cik}\/profitability","GET \/api\/v1\/companies\/{cik}\/valuation","GET \/api\/v1\/companies\/{cik}\/financial-strength","GET \/api\/v1\/companies\/{cik}\/efficiency","GET \/api\/v1\/companies\/{cik}\/management-effectiveness","GET \/api\/v1\/companies\/{cik}\/dashboard"],"breaking":false},{"version":"1.3.0","date":"2026-02-01","type":"feature","summary":"Sector API added: mirrors industry API structure","description":"Full sector-level analytics added. Shares the same endpoint pattern and field set as industries, with cross-sector percentile rankings instead of cross-industry.","endpoints_added":["GET \/api\/v1\/sectors","GET \/api\/v1\/sectors\/{id}","GET \/api\/v1\/sectors\/{id}\/growth","GET \/api\/v1\/sectors\/{id}\/profitability","GET \/api\/v1\/sectors\/{id}\/valuation","GET \/api\/v1\/sectors\/{id}\/financial-strength","GET \/api\/v1\/sectors\/{id}\/efficiency","GET \/api\/v1\/sectors\/{id}\/management-effectiveness","GET \/api\/v1\/sectors\/{id}\/dashboard"],"breaking":false},{"version":"1.2.0","date":"2026-01-15","type":"feature","summary":"Dispersion statistics added to industry growth and profitability","description":"Full cross-sectional dispersion statistics added to industry growth and profitability endpoints: median, p10, p25, p75, p90, stddev, IQR, MAD, CV, skewness, kurtosis, downside stddev, HHI concentration, entropy, breadth diffusion index.","fields_added":["revenue_yoy_median","revenue_yoy_p10","revenue_yoy_p25","revenue_yoy_p75","revenue_yoy_p90","revenue_yoy_stddev","revenue_yoy_iqr","revenue_yoy_mad","revenue_yoy_cv","revenue_yoy_skewness","revenue_yoy_kurtosis","revenue_yoy_downside_stddev","revenue_concentration_hhi","profit_concentration_hhi","revenue_dispersion_entropy","earnings_dispersion_entropy","breadth_diffusion_index","earnings_breadth_score","operating_margin_median","operating_margin_stddev","...and full operating margin and net margin dispersion sets"],"breaking":false},{"version":"1.1.0","date":"2025-12-01","type":"feature","summary":"Industry dashboard endpoint and z-score fields added","description":"Dashboard endpoint added returning all six analytical sections in a single request. Z-score fields added across growth, profitability, valuation, financial strength, and management effectiveness.","endpoints_added":["GET \/api\/v1\/industries\/{id}\/dashboard"],"fields_added":["revenue_yoy_zscore","revenue_growth_zscore","operating_margin_zscore","net_margin_zscore","roic_zscore","debt_to_equity_zscore","interest_coverage_zscore","valuation_composite_zscore","quality_factor_zscore"],"breaking":false},{"version":"1.0.0","date":"2025-11-01","type":"launch","summary":"API v1 launched: industry fundamentals","description":"Initial release. Industry-level fundamentals across growth, profitability, valuation, financial strength, efficiency, and management effectiveness. Supports CSIMarket, SIC, and NAICS classification systems. TTM, FY, and Q period types.","endpoints_added":["GET \/api\/v1\/industries","GET \/api\/v1\/industries\/{id}","GET \/api\/v1\/industries\/{id}\/growth","GET \/api\/v1\/industries\/{id}\/profitability","GET \/api\/v1\/industries\/{id}\/valuation","GET \/api\/v1\/industries\/{id}\/financial-strength","GET \/api\/v1\/industries\/{id}\/efficiency","GET \/api\/v1\/industries\/{id}\/management-effectiveness"],"breaking":false}]}