Skip to content

feat!: nullable members for values FMP can omit (1.2.0) #368

Closed

Description

@benbenbang

Source: docs/requests/nillable.md (item 1). Target: 1.2.0, breaking. Commit type feat(...) / fix(...) with a BREAKING CHANGE: footer (maps to a minor bump in .releaserc.json). Never incompat (maintainer policy: breaking changes ship as minors).

Problem

Members that FMP leaves empty are required non-null types. One null fails a whole call, and consumers cannot tell "no value" from zero (a missing treasury tenor must not read as 0%).

Part 1: audit first

Sample live data once with a script (screener, profile, economics, analyst, statements) and list every member seen as null, missing, "" or malformed. Output a table in this issue. Goal: one breaking release, not another one after the next surprise. Never commit the API key or raw responses with secrets.

Part 2: make them optional

Known so far:

Model Members Seen
TreasuryRate all 12 tenors month2 null before 2018
EconomicIndicatorObservation value risk
CompanyScreenerResult beta null
CompanyProfile phone, full_time_employees, ipo_date null / "" / malformed date
  • Rust Option<T>; Go pointers via existing gen_go mapping; Python T | None. Regen all.
  • ipo_date: empty, null and malformed -> None (ADR 0002 already maps empty/null dates to None).
  • Record the policy in an ADR: which members are optional and why.

Acceptance

  • treasury-rates row with "month2": null decodes with month_2 == None / Month2 == nil, other tenors set.
  • Screener page with one "beta": null row decodes.
  • aquor can drop internal/screener/sanitize.go and move internal/macro onto the SDK.

Out of scope

A lenient/zero-default decoding option (item 2 of the request): declined, because it recreates the zero vs no-value ambiguity.

Activity

  1. changed the title [-]incompat: nullable members for values FMP can omit (2.0)[/-] [+]feat!: nullable members for values FMP can omit (1.2.0)[/+] on Sep 29, 2026
  2. benbenbang commented on Sep 30, 2026

    @benbenbang
    MemberAuthor

    Part 1: live audit results (2026-09-30)

    Coverage: 392 requests (277x 200, 96x 402 plan tier, 0x 401/429). 230 of 243 registry paths tried, 148 returned data. Raw responses were not kept.

    Recommended Option list for 1.2.0

    • TreasuryRate: all 12 tenors
    • EconomicIndicatorObservation.value (policy, never seen null)
    • CompanyScreenerResult: beta, country, lastAnnualDividend, price
    • CompanyProfile: cik, cusip, fullTimeEmployees, phone, ipoDate (empty_or_null_date); + price, changes, range, dcfDiff (parity with ExchangeVariant, not observed)
    • ExchangeVariant: cik, cusip (empty->None), dcfDiff, phone, price, range, changes (+ profile parity)
    • KeyMetrics.grahamNumber, KeyMetricsTtm.grahamNumberTTM
    • EarningsEvent: epsEstimated, revenueEstimated
    • ShareFloat: floatShares, freeFloat
    • CryptocurrencyListing: totalSupply, circulatingSupply, icoDate (empty_or_null_date)
    • FinancialStatementSymbol.reportingCurrency (empty->None)
    • NewsArticle.image; CompanyExecutive.gender; ExchangeHoliday.isClosed
    • AvailableExchange: countryCode (empty->None), delay
    • InsiderTrade: transactionType (empty->None), directOrIndirect
    • BeneficialOwnershipAcquisition.sharedVotingPower (+ consider 3 sibling *Power)
    • CrowdfundingOffering: compensationAmount, financialInterest, intermediaryCommissionFileNumber, intermediaryCompanyName, issuerWebsite, offeringDeadlineDate (UsDate null-aware), overSubscriptionAllocationType, securityOfferedType
    • RegulationDOffering: revenueRange, securitiesOfferedAreOfEquityType
    • SecCompanySearchResult.phoneNumber
      Policies: "" on plain String stays ""; "" on typed codes -> None; malformed ipoDate stays an error (0 seen; ask aquor for the exact string).
      Separate bug: financial-reports-json returns one object, SDK decodes Vec (endpoints/statements/reports.rs:121) -> always fails.
      Doc note: treasury-rates returns max ~60 rows per call.

    Open decisions

    1. Scope: the full list above (evidence + parity), evidence-only members, or only the 4 models named in this issue?
    2. "" on typed codes (countryCode, cusip, transactionType, reportingCurrency): map to None, or keep as a decode error?
    3. File the financial-reports-json bug as a separate fix issue?
    Evidence table (77 rows)
    model member rust wire obs (count) rows seen sdk fails today samples example where (first 4)
    AvailableExchange countryCode CountryCode empty_string(str) 1 63 invalid_value empty_string 1 "" x1 {'empty_string(str)': 'available-exchanges'}
    AvailableExchange countryName String empty_string(str) 1 63 - "" x1 {'empty_string(str)': 'available-exchanges'} available-exchanges:
    AvailableExchange delay String null 1 63 null null 1 null x1 {'null': 'available-exchanges'}
    BeneficialOwnershipAcquisition sharedVotingPower NumericString null 1 99 null null 1 null x1 {'null': 'acquisition-of-beneficial-ownership?symbol=AAPL'}
    CommodityListing exchange Option < ExchangeCode > (opt) null 40 40 - null x40 {'null': 'commodities-list'} commodities-list:ALIUSD, commodities-list:BZUSD, commodities-list:CCUSD, commodities-list:CLUSD
    CompanyExecutive gender String null 6 28 null null 6 null x6 {'null': 'key-executives?symbol=AAPL'}
    CompanyExecutive pay Option < DynamicJson > (opt) null 17 28 - null x17 {'null': 'key-executives?symbol=AAPL'} key-executives:
    CompanyExecutive titleSince Option < DynamicJson > (opt) null 28 28 - null x28 {'null': 'key-executives?symbol=AAPL'} key-executives:
    CompanyExecutive yearBorn Option < DynamicJson > (opt) null 15 28 - null x15 {'null': 'key-executives?symbol=AAPL'} key-executives:
    CompanyProfile ceo String empty_string(str) 1 24 - "" x1 {'empty_string(str)': 'profile?symbol=SPY'} profile:SPY
    CompanyProfile cik Cik null 5 24 null null 5 null x5 {'null': 'profile?symbol=SAP.DE'}
    CompanyProfile cusip Cusip null 2 24 null null 2 null x2 {'null': 'profile?symbol=SAP.DE'}
    CompanyProfile fullTimeEmployees NumericString null 1 24 null null 1 null x1 {'null': 'profile?symbol=SPY'}
    CompanyScreenerResult beta Ratio null 33 5924 null null 33 null x33 {'null': 'company-screener?limit=1000'}
    CompanyScreenerResult country CountryCode null 13 5924 null null 13 null x13 {'null': 'company-screener?limit=1000'}
    CompanyScreenerResult lastAnnualDividend PerShareAmount null 2 5924 null null 2 null x2 {'null': 'company-screener?limit=1000,country=JP'}
    CompanyScreenerResult price Price null 1 5924 null null 1 null x1 {'null': 'company-screener?limit=1000,country=JP'}
    CongressionalTrade capitalGainsOver200USD Option < TitleCaseBoolFlag > (opt) missing 100 586 - "" x100 {'missing': 'senate-latest'} senate-latest:, senate-latest:ALNY, senate-latest:AMGN, senate-latest:AMZN
    CongressionalTrade comment String empty_string(str) 511 586 - "" x511 {'empty_string(str)': 'senate-latest'} house-latest:, house-latest:AAPL, house-latest:AIO, house-latest:BSX
    CongressionalTrade district String empty_string(str) 15 586 - "" x15 {'empty_string(str)': 'senate-latest'} house-trades:AAPL, senate-latest:AVB, senate-latest:EA, senate-latest:WMB
    CongressionalTrade owner String empty_string(str) 94 586 - "" x94 {'empty_string(str)': 'senate-latest'} house-latest:AAPL, house-latest:AIO, house-latest:BSX, house-latest:CL
    CongressionalTrade symbol String empty_string(str) 25 586 - "" x25 {'empty_string(str)': 'senate-latest'} house-latest:, house-trades-by-name:, senate-latest:, senate-trades-by-id:
    CrowdfundingOffering compensationAmount String null 12 103 null null 12 null x12 {'null': 'crowdfunding-offerings-latest'}
    CrowdfundingOffering financialInterest String null 16 103 null null 16 null x16 {'null': 'crowdfunding-offerings-latest'}
    CrowdfundingOffering intermediaryCommissionFileNumber String null 12 103 null null 12 null x12 {'null': 'crowdfunding-offerings-latest'}
    CrowdfundingOffering intermediaryCompanyName String null 12 103 null null 12 null x12 {'null': 'crowdfunding-offerings-latest'}
    CrowdfundingOffering issuerWebsite String null 11 103 null null 11 null x11 {'null': 'crowdfunding-offerings-latest'}
    CrowdfundingOffering offeringDeadlineDate UsDate null 12 103 null null 12 null x12 {'null': 'crowdfunding-offerings-latest'}
    CrowdfundingOffering overSubscriptionAllocationType String null 12 103 null null 12 null x12 {'null': 'crowdfunding-offerings-latest'}
    CrowdfundingOffering securityOfferedOtherDescription Option < String > (opt) null 43 103 - null x43 {'null': 'crowdfunding-offerings-latest'} crowdfunding-offerings-latest:01-01-2019, crowdfunding-offerings-latest:01-01-2020, crowdfunding-offerings-latest:01-01-2022, crowdfunding-offerings-latest:01-01-2023
    CrowdfundingOffering securityOfferedType String null 12 103 null null 12 null x12 {'null': 'crowdfunding-offerings-latest'}
    CrowdfundingOfferingSearchResult date Option < DynamicJson > (opt) null 5 95 - null x5 {'null': 'crowdfunding-offerings-search?name=energy'} crowdfunding-offerings-search:
    CryptocurrencyListing circulatingSupply TokenSupply null 1 4793 null null 1 null x1 {'null': 'cryptocurrency-list'}
    CryptocurrencyListing icoDate Date null 33 4793 null null 33 null x33 {'null': 'cryptocurrency-list'}
    CryptocurrencyListing totalSupply TokenSupply null 1474 4793 null null 1474 null x1474 {'null': 'cryptocurrency-list'}
    DividendEvent declarationDate Option < Date > (opt) [codec:empty_or_null_date] empty_string 29 138 - "" x29 {'empty_string': 'dividends?symbol=AAPL'} dividends:AAPL, dividends:TSM
    EarningsEvent epsActual Option < f64 > (opt) null 2 281 - null x2 {'null': 'earnings?symbol=AAPL'} earnings:AAPL, earnings:TSM
    EarningsEvent epsEstimated f64 null 59 281 null null 59 null x59 {'null': 'earnings?symbol=AAPL'}
    EarningsEvent revenueActual Option < MarketValue > (opt) null 2 281 - null x2 {'null': 'earnings?symbol=AAPL'} earnings:AAPL, earnings:TSM
    EarningsEvent revenueEstimated MarketValue null 55 281 null null 55 null x55 {'null': 'earnings?symbol=AAPL'}
    ExchangeHoliday adjCloseTime Option < DynamicJson > (opt) null 10 12 - null x10 {'null': 'holidays-by-exchange?exchange=NYSE'} holidays-by-exchange:2025-11-27, holidays-by-exchange:2025-12-25, holidays-by-exchange:2026-01-01, holidays-by-exchange:2026-01-19
    ExchangeHoliday adjOpenTime Option < DynamicJson > (opt) null 12 12 - null x12 {'null': 'holidays-by-exchange?exchange=NYSE'} holidays-by-exchange:2025-11-27, holidays-by-exchange:2025-11-28, holidays-by-exchange:2025-12-24, holidays-by-exchange:2025-12-25
    ExchangeHoliday isClosed bool null 2 12 null null 2 null x2 {'null': 'holidays-by-exchange?exchange=NYSE'}
    ExchangeVariant changes Change null 1 7 null null 1 null x1 {'null': 'search-exchange-variants?symbol=AAPL'}
    ExchangeVariant cik Cik null 5 7 null null 5 null x5 {'null': 'search-exchange-variants?symbol=AAPL'}
    ExchangeVariant cusip Cusip empty_string(str) 1 7 invalid_value empty_string 1 "" x1 {'empty_string(str)': 'search-exchange-variants?symbol=AAPL'}
    ExchangeVariant dcfDiff PerShareAmount null 2 7 null null 2 null x2 {'null': 'search-exchange-variants?symbol=AAPL'}
    ExchangeVariant phone String null 1 7 null null 1 null x1 {'null': 'search-exchange-variants?symbol=AAPL'}
    ExchangeVariant price Price null 1 7 null null 1 null x1 {'null': 'search-exchange-variants?symbol=AAPL'}
    ExchangeVariant range String null 1 7 null null 1 null x1 {'null': 'search-exchange-variants?symbol=AAPL'}
    FinancialStatementSymbol reportingCurrency CurrencyCode null 120, empty_string(str) 1 26200 null null 120, invalid_value empty_string 1 null x120, "" x1
    InsiderTrade acquisitionOrDisposition String empty_string(str) 6 200 - "" x6 {'empty_string(str)': 'insider-trading/latest'} insider-trading/latest:FUL, insider-trading/search:AAPL
    InsiderTrade directOrIndirect String null 1 200 null null 1 null x1 {'null': 'insider-trading/latest'}
    InsiderTrade securityName String empty_string(str) 1 200 - "" x1 {'empty_string(str)': 'insider-trading/latest'} insider-trading/latest:FUL
    InsiderTrade transactionType TransactionTypeCode empty_string(str) 6 200 invalid_value empty_string 6 "" x6 {'empty_string(str)': 'insider-trading/latest'}
    IpoCalendarEvent marketCap Option < DynamicJson > (opt) null 178 221 - null x178 {'null': 'ipos-calendar?from=2025-06-01,to=2025-06-30'} ipos-calendar:001235.SZ, ipos-calendar:001388.SZ, ipos-calendar:0F08.L, ipos-calendar:0MQG.L
    IpoCalendarEvent priceRange Option < DynamicJson > (opt) null 179 221 - null x179 {'null': 'ipos-calendar?from=2025-06-01,to=2025-06-30'} ipos-calendar:001235.SZ, ipos-calendar:001388.SZ, ipos-calendar:2050.HK, ipos-calendar:2609.HK
    IpoCalendarEvent shares Option < DynamicJson > (opt) null 178 221 - null x178 {'null': 'ipos-calendar?from=2025-06-01,to=2025-06-30'} ipos-calendar:001388.SZ, ipos-calendar:0F08.L, ipos-calendar:0MQG.L, ipos-calendar:2050.HK
    KeyMetrics grahamNumber f64 null 10 80 null null 10 null x10 {'null': 'key-metrics?symbol=GPRO,period=annual,limit=20'}
    KeyMetricsTtm grahamNumberTTM f64 null 1 3 null null 1 null x1 {'null': 'key-metrics-ttm?symbol=GPRO'}
    NewsArticle image String null 16 100 null null 16 null x16 {'null': 'news/stock-latest'}
    NewsArticle symbol Option < Ticker > (opt) null 20 100 - null x20 {'null': 'news/general-latest'} news/general-latest:
    RegulationDOffering dateOfFirstSale Option < Date > (opt) [codec:empty_date] empty_string 4 16 - "" x4 {'empty_string': 'fundraising-latest'} fundraising-latest:2026-06-25, fundraising-latest:2026-09-29
    RegulationDOffering incorporatedWithinFiveYears Option < bool > (opt) null 6 16 - null x6 {'null': 'fundraising-latest'} fundraising-latest:2026-09-29, fundraising:2012-04-18, fundraising:2012-07-02, fundraising:2013-02-06
    RegulationDOffering revenueRange String null 2 16 null null 2 null x2 {'null': 'fundraising-latest'}
    RegulationDOffering securitiesOfferedAreOfEquityType bool null 6 16 null null 6 null x6 {'null': 'fundraising-latest'}
    RegulationDOffering yearOfIncorporation String empty_string(str) 6 16 - "" x6 {'empty_string(str)': 'fundraising-latest'} fundraising-latest:2026-09-29, fundraising:2012-04-18, fundraising:2012-07-02, fundraising:2013-02-06
    SecCompanyProfile securityType Option < DynamicJson > (opt) null 2 2 - null x2 {'null': 'sec-profile?symbol=AAPL'} sec-profile:AAPL, sec-profile:TSM
    SecCompanyProfile stateOfIncorporation String empty_string(str) 1 2 - "" x1 {'empty_string(str)': 'sec-profile?symbol=TSM'} sec-profile:TSM
    SecCompanySearchResult industryTitle String empty_string(str) 50 154 - "" x50 {'empty_string(str)': 'sec-filings-company-search/name?company=Apple'} sec-filings-company-search/name:None
    SecCompanySearchResult phoneNumber String null 6 154 null null 6 null x6 {'null': 'sec-filings-company-search/name?company=Apple'}
    SecCompanySearchResult sicCode String empty_string(str) 50 154 - "" x50 {'empty_string(str)': 'sec-filings-company-search/name?company=Apple'} sec-filings-company-search/name:None
    SecFiling hasFinancials Option < bool > (opt) null 11, missing 100 300 - "" x100, null x11 {'null': 'sec-filings-8k?from=2025-06-01,to=2025-06-20', 'missing': 'sec-filings-search/form-type?formType=8-K,from=2025-06-01,to=2025-06-10'} sec-filings-8k:BEEP, sec-filings-8k:CURR, sec-filings-8k:GPUS, sec-filings-8k:GPUS-PD
    ShareFloat floatShares Quantity null 62 1000 null null 62 null x62 {'null': 'shares-float-all'}
    ShareFloat freeFloat Percentage null 68 1000 null null 68 null x68 {'null': 'shares-float-all'}
    TreasuryRate month1 Percentage null 82 549 null null 82 null x82 {'null': 'treasury-rates?from=2001-07-01,to=2001-09-27'}
    TreasuryRate month2 Percentage null 312 549 null null 312 null x312 {'null': 'treasury-rates?from=2016-01-01,to=2016-03-30'}
    TreasuryRate year20 Percentage null 62 549 null null 62 null x62 {'null': 'treasury-rates?from=1990-01-01,to=1990-03-30'}
    TreasuryRate year30 Percentage null 60 549 null null 60 null x60 {'null': 'treasury-rates?from=2003-01-01,to=2003-03-30'}
  3. benbenbang commented on Sep 30, 2026

    @benbenbang
    MemberAuthor

    Audit rerun on the ULTIMATE plan (2026-09-30)

    metric first run rerun
    requests 392 4012
    paths tried / with data 230 / 148 241 / 231
    fields where the SDK fails today 47 194

    Only the 7 tipranks-* paths still return 402 (add-on). All 47 first-run failures still fail.

    Decisions (maintainer, 2026-09-30)

    1. Scope: every field observed failing (194) plus the 4 models named in this issue. No parity siblings that were never seen failing.
    2. "" on typed codes and dates -> None. "" on plain String members stays "".
    3. Type bugs filed separately: fix!: bulk routes return CSV, SDK decodes JSON #376 bulk CSV, fix!: congressional net worth amounts are fractional and some members are absent #377 congressional net worth, fix!: IndexConstituent.founded is a year string, not a date #378 IndexConstituent.founded, fix!: IpoProspectus.ipoDate can be year-only #379 IpoProspectus.ipoDate, fix!: FundDisclosureSearchResult.entityOrgType receives the string NULL #380 FundDisclosureSearchResult.entityOrgType, fix: economic-indicators plain-text Invalid name surfaces as a decode error #381 economic-indicators Invalid name. fix!: financial-reports-json returns one object, SDK decodes a list #374 (financial-reports-json) is already fixed in PR 375.

    Updated Option list ("(new)" = found by the rerun)

    • TreasuryRate: all 12 tenors (now all 12 observed null; month3..year10 are (new) observations, already in the list)
    • EconomicIndicatorObservation.value (policy, still never seen null: ~1500 observations, GDP/CPI/unemploymentRate/federalFunds walked 1990-2026)
    • CompanyScreenerResult: beta, country, lastAnnualDividend, price, isFund (new)
    • CompanyProfile: cik, cusip, fullTimeEmployees, phone, ipoDate (empty_or_null_date); + price, changes, range, dcfDiff (parity with ExchangeVariant)
    • ExchangeVariant: cik, cusip (empty->None), dcfDiff, phone, price, range, changes, address (new), ceo (new), city (new), state (new), zip (new), website (new), fullTimeEmployees (new), isin (new) (+ profile parity)
    • KeyMetrics.grahamNumber, KeyMetricsTtm.grahamNumberTTM
    • EarningsEvent: epsEstimated, revenueEstimated
    • ShareFloat: floatShares, freeFloat; CompanyShareFloat.source (new)
    • CryptocurrencyListing: totalSupply, circulatingSupply, icoDate (empty_or_null_date)
    • FinancialStatementSymbol.reportingCurrency (empty->None)
    • NewsArticle.image, NewsArticle.site (new); CompanyExecutive.gender; ExchangeHoliday.isClosed
    • AvailableExchange: countryCode (empty->None), delay
    • InsiderTrade: transactionType (empty->None), directOrIndirect, securitiesOwned (new)
    • BeneficialOwnershipAcquisition.sharedVotingPower (+ consider 3 sibling *Power), citizenshipOrPlaceOfOrganization (new), cusip (new)
    • CrowdfundingOffering: compensationAmount, financialInterest, intermediaryCommissionFileNumber, intermediaryCompanyName, issuerWebsite, offeringDeadlineDate (UsDate null-aware), overSubscriptionAllocationType, securityOfferedType
    • RegulationDOffering: revenueRange, securitiesOfferedAreOfEquityType
    • SecCompanySearchResult.phoneNumber
    • BalanceSheetStatement (new): 38 StatementAmount members null in one RIVN quarterly row -> Option on StatementAmount members, or skip/tolerate all-null pre-IPO stub rows (BS 2021-03-31 Q1)
    • CashFlowStatement (new): 19 StatementAmount members null in RIVN stub quarters 2020-12-31 Q4, 2021-09-30 Q3 (same choice as above)
    • IncomeStatement (new): interestIncome, filingDate, acceptedDate (VOD.L quarterly)
    • AsReportedFinancialStatement.reportedCurrency (new)
    • FinancialEstimate (new): ebitAvg/High/Low, ebitdaAvg/High/Low (131/1613 rows)
    • FinancialScore.altmanZScore (new)
    • ExecutiveCompensation.optionAward (new)
    • EconomicCalendarEvent (new): actual, estimate, previous, change, changePercentage, unit
    • DividendEvent (new): paymentDate, recordDate (empty->None; ~17% of calendar rows)
    • StockSplitEvent.splitType (new)
    • IpoCalendarEvent.exchange (new); IpoProspectus: discountsAndCommissionsPerShare, discountsAndCommissionsTotal (new)
    • QuoteShort (new): price, change, volume (batch etf/mutualfund/forex quotes)
    • AftermarketTrade.tradeSize (new)
    • CompanyMarketCapitalization.marketCap (new, forex/crypto symbols)
    • CompanyNote.exchange (new)
    • SecCompanyProfile: employees, fiscalYearEnd (new, ETFs)
    • EsgDisclosure: companyName, formType (new); EsgRating.companyName (new)
    • EtfFundHolding: asset, isin, securityCusip (new, empty->None)
    • FundDisclosure: symbol, isin (empty->None) (new); FundDisclosureSearchResult.address (new)
    • InstitutionalHolding.symbol (new)
    • HolderIndustryBreakdown: industryTitle, changeInWeightPercentage, performancePercentage (new)
    • MergerAcquisition: targetedCik, targetedSymbol (new)
    • HistoricalIndexConstituent: addedSecurity, reason (new); removedTicker already Option but "" fails -> empty->None (new)
    • CongressionalMemberProfile.image (new); CongressionalMemberNetWorth: category, name, value (new)
      Policies: "" on plain String stays ""; "" on typed codes and dates -> None; malformed ipoDate stays an error (0 seen on profile).
    Rerun diff (new rows and still-failing paths)

    #368 audit rerun vs first run (a368-v2, 2026-09-30)

    Plan: ULTIMATE. Code: main 78da933. First run: 392 req, 277x200, 96x402, 230/243 paths tried, 148 with data.

    Request stats

    metric first run rerun
    requests (index) 392 4012
    HTTP 200 277 3924
    HTTP 402 96 88 (all tipranks-*)
    HTTP 401 / 429 / other 0 0
    bulk partial-read probes (bulk-probe.jsonl) - 9 (all 200, text/csv)
    registry paths (non-binary, non-dynamic) 243 241 (+2 skipped: financial-reports-xlsx binary, industry-classification-search dynamic)
    paths tried 230 241
    paths with data 148 231
    report rows (model, member) 79 243
    rows where SDK decode fails today 47 194 (+2 nested)

    Coverage: 10 non-US listings (7203.T, SAP.DE, NESN.SW, VOD.L, SHOP.TO, 0700.HK, RELIANCE.NS, ASML.AS, 005930.KS, BHP.AX), ETFs SPY/QQQ/VTI, funds VFIAX/FXAIX, ADRs TSM/BABA/NVO/SONY/TM, small caps + loss-makers (GPRO, ZOM, RIVN, LCID, PLUG, SNDL, MVIS, CLOV, PTON, SNAP), crypto/forex/commodities/indexes, treasury walked 1990-2026 in 84-day windows (9560 rows), 21 economic indicators in decade windows plus GDP/CPI/unemploymentRate/federalFunds walked 1990-2026 in 91-day windows (1500 observations, value never null), statements annual (limit 60) + quarterly (limit 120), 6 screener pages + 25 filter combos, calendars in 2008/2020/2026.

    Resume note: the previous worker's in-flight bulk calls reused request numbers 415/416 on resume, overwriting their raw bodies with treasury rows. Those two index records now point to stubs holding the logged CSV header (field note), so they register as not_json like every other bulk route.

    Still failing / never decodable

    path(s) reason
    tipranks-analysts, -search, -pit-symbol, -pit-analyst, -analyst-summary, -firm-summary, -symbol-summary (7) 402 Restricted Endpoint even on ULTIMATE (separate add-on)
    all 18 *-bulk routes 200 text/csv (Accept: application/json ignored); SDK decodes a JSON array and test fixtures are JSON -> every client.bulk call fails. NEW (no bulk route returned a 200 body in the first run)
    financial-reports-json still one JSON object vs Vec<FinancialReportJson> (5/5 calls fail)
    economic-indicators row cap a 1-year window returns 2 rows, a quarter 3, GDP over 6 years returns []: callers must walk short windows (doc note, like treasury)
    economic-indicators?name= 200 with plain-text body Invalid name (not JSON) -> decode error instead of a typed error. NEW
    house-trades-by-id, sec-filings-search/cik, senate-trades-by-name 200 but always [] with the params tried

    New rows (model: members not in the first-run table) [sdk result today]

    Note: the 38 BalanceSheetStatement and 19 CashFlowStatement null rows all come from RIVN quarterly stubs (BS 2021-03-31 Q1, CF 2020-12-31 Q4 and 2021-09-30 Q3, around the Nov 2021 IPO): a "skip or tolerate all-null stub rows" policy is an alternative to widening 57 members.

    • AftermarketTrade: tradeSize [null|null 20]
    • AsReportedFinancialStatement: reportedCurrency [null|null 16]
    • BalanceSheetStatement: accountPayables [null|null 1]; accountsReceivables [null|null 1]; accruedExpenses [null|null 1]; accumulatedOtherComprehensiveIncomeLoss [null|null 1]; additionalPaidInCapital [null|null 1]; capitalLeaseObligationsCurrent [null|null 1]; capitalLeaseObligationsNonCurrent [null|null 1]; cashAndCashEquivalents [null|null 1]; cashAndShortTermInvestments [null|null 1]; commonStock [null|null 1]; deferredRevenue [null|null 1]; deferredRevenueNonCurrent [null|null 1]; deferredTaxLiabilitiesNonCurrent [null|null 1]; goodwill [null|null 1]; goodwillAndIntangibleAssets [null|null 1]; intangibleAssets [null|null 1]; inventory [null|null 1]; longTermDebt [null|null 1]; longTermInvestments [null|null 1]; netReceivables [null|null 1]; otherPayables [null|null 1]; prepaids [null|null 1]; propertyPlantEquipmentNet [null|null 1]; retainedEarnings [null|null 1]; shortTermDebt [null|null 1]; shortTermInvestments [null|null 1]; taxAssets [null|null 1]; taxPayables [null|null 1]; totalAssets [null|null 1]; totalCurrentAssets [null|null 1]; totalCurrentLiabilities [null|null 1]; totalEquity [null|null 1]; totalLiabilities [null|null 1]; totalLiabilitiesAndTotalEquity [null|null 1]; totalNonCurrentAssets [null|null 1]; totalNonCurrentLiabilities [null|null 1]; totalStockholdersEquity [null|null 1]; treasuryStock [null|null 1]
    • BeneficialOwnershipAcquisition: citizenshipOrPlaceOfOrganization [null|null 49]; cusip [null|null 14]
    • CashFlowStatement: accountsPayables [null|null 2]; accountsReceivables [null|null 2]; cashAtBeginningOfPeriod [null|null 1]; commonDividendsPaid [null|null 2]; commonStockRepurchased [null|null 1]; deferredIncomeTax [null|null 2]; incomeTaxesPaid [null|null 2]; interestPaid [null|null 2]; inventory [null|null 2]; longTermNetDebtIssuance [null|null 1]; netCashProvidedByFinancingActivities [null|null 1]; netCommonStockIssuance [null|null 1]; netDividendsPaid [null|null 2]; netPreferredStockIssuance [null|null 2]; netStockIssuance [null|null 1]; preferredDividendsPaid [null|null 2]; purchasesOfInvestments [null|null 2]; salesMaturitiesOfInvestments [null|null 2]; stockBasedCompensation [null|null 2]
    • CompanyMarketCapitalization: marketCap [null|null 4]
    • CompanyNote: exchange [null|null 1]
    • CompanyScreenerResult: isFund [null|null 4]
    • CompanyShareFloat: source [null|null 5]
    • CongressionalDebtDetails: dateIncurred [sdk ok]
    • CongressionalMemberNetWorth: category [null|null 3]; comment [sdk ok]; debtDetails [sdk ok]; incomeRange [sdk ok]; name [null|null 30]; value [null|null 42, invalid_value|float_frac 208]; valueRange [sdk ok]
    • CongressionalMemberNetWorthAggregate: assetBackedSecurities [missing|absent 11]; businessAndSelfEmployment [missing|absent 11]; businessLiabilities [missing|absent 11]; cashAndCashEquivalents [invalid_value|float_frac 7]; mutualFundsAndETFs [invalid_value|float_frac 5]; options [missing|absent 11]; ownershipInterest [missing|absent 11]; realEstate [invalid_value|float_frac 10]; realEstateLiabilities [missing|absent 11]; revolvingAndCreditLines [missing|absent 11]; stock [invalid_value|float_frac 6]; total [invalid_value|float_frac 5]
    • CongressionalMemberPosition: endDate [sdk ok]
    • CongressionalMemberProfile: image [null|null 5]
    • CongressionalTrade: assetDescription [sdk ok]
    • DividendEvent: paymentDate [invalid_value|empty_string 2993]; recordDate [invalid_value|empty_string 2773]
    • EconomicCalendarEvent: actual [null|null 992]; change [null|null 997]; changePercentage [null|null 172]; estimate [null|null 2618]; previous [null|null 944]; unit [null|null 1361]
    • EsgDisclosure: companyName [null|null 4]; formType [null|null 19]
    • EsgRating: companyName [null|null 1]
    • EtfFundHolding: asset [invalid_value|empty_string 43]; isin [invalid_value|empty_string 46]; securityCusip [invalid_value|empty_string 39]
    • ExchangeVariant: address [null|null 1]; ceo [null|null 1]; city [null|null 1]; fullTimeEmployees [null|null 4]; isin [null|null 3]; state [null|null 9]; website [null|null 1]; zip [null|null 3]
    • ExecutiveCompensation: optionAward [null|null 1]
    • FinancialEstimate: ebitAvg [null|null 131]; ebitHigh [null|null 131]; ebitLow [null|null 131]; ebitdaAvg [null|null 131]; ebitdaHigh [null|null 131]; ebitdaLow [null|null 131]
    • FinancialScore: altmanZScore [null|null 1]
    • FundDisclosure: isin [invalid_value|empty_string 76]; symbol [null|null 874]
    • FundDisclosureHolder: holder [sdk ok]
    • FundDisclosureSearchResult: address [null|null 1239]
    • HistoricalIndexConstituent: addedSecurity [null|null 3]; reason [null|null 7]; removedSecurity [sdk ok]; removedTicker [invalid_value|empty_string 335]
    • HolderIndustryBreakdown: changeInWeightPercentage [null|null 2]; industryTitle [null|null 3]; performancePercentage [null|null 8]
    • IncomeStatement: acceptedDate [null|null 1]; filingDate [null|null 1]; interestIncome [null|null 1]
    • IndexConstituent: dateFirstAdded [sdk ok]; founded [invalid_value|string:9999 474, invalid_value|string:9999/9999 2, invalid_value|string:9999/9999/9999 1]
    • InsiderTrade: securitiesOwned [null|null 1]; typeOfOwner [sdk ok]
    • InstitutionalHolding: putCallShare [sdk ok]; symbol [null|null 395]
    • IpoCalendarEvent: exchange [null|null 1]
    • IpoProspectus: discountsAndCommissionsPerShare [null|null 5]; discountsAndCommissionsTotal [null|null 5]; ipoDate [invalid_value|string:9999 7]
    • MergerAcquisition: targetedCik [null|null 47]; targetedSymbol [null|null 15]
    • NewsArticle: site [null|null 5]
    • Quote: marketCap [sdk ok]
    • QuoteShort: change [null|null 179]; price [null|null 17]; volume [null|null 440]
    • SecCompanyProfile: ceo [sdk ok]; employees [null|null 5]; fiscalYearEnd [null|null 4]; sicCode [sdk ok]; sicDescription [sdk ok]
    • SecFiling: finalLink [sdk ok]
    • StockSplitEvent: splitType [null|null 39]
    • TreasuryRate: month3 [null|null 4]; month6 [null|null 1]; year1 [null|null 1]; year10 [null|null 1]; year2 [null|null 1]; year3 [null|null 1]; year5 [null|null 1]; year7 [null|null 1]

    First-run SDK failures that stayed

    47/47 first-run failing keys fail again; none disappeared (the old table's rows are all present in the new one).

    Key scan

    • grep -rF $FMP_API_KEY over the work dir: 0 hits.
    • apikey= not followed by ***: 887 hits, NOT zero. 886 are the literal provider placeholder apikey=YOUR_API_KEY inside financial-reports-dates bodies (17 raw files), 1 is the scrub regex in fetch.py. No real key.
  4. benbenbang commented on Sep 30, 2026

    @benbenbang
    MemberAuthor

    All 12 units merged (1.2.0, breaking; ships on the next release).

    Unit PR
    F0: empty_or_null codec, gen_go support, ADR 0033 #382
    COMPANY #383
    QUOTE #384
    ECON (TreasuryRate tenors, indicator value, economic calendar) #385
    STMT-A (57 BalanceSheet/CashFlow amounts, RIVN stub quarters) #386
    SEARCH (ExchangeVariant, screener) #388
    CAL #389
    FUNDS #390
    OWNERSHIP #391
    DIR-IDX #392
    STMT-B (income, key metrics, score, as-reported, estimates) #393
    MISC (news, crypto, holidays, ESG, fundraising, SEC, congressional image) #394

    Acceptance:

    • treasury-rates row with "month2": null decodes to None / nil, other tenors set (Rust, Go, Python).
    • A screener page with one "beta": null row decodes (Rust, Go, Python).

    Policy recorded in ADR 0033: members seen null become Option / pointer / Optional; "" on typed codes and dates becomes None; "" on plain strings stays ""; no zero or NaN defaults, no dropped rows.

    Related type fixes filed separately: #376 (bulk CSV), #377, #378, #379, #380, #381, and #387 (Go decode path on nullable members).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions