Financial Evidence Expansion — FRED, Finnhub, FMP

Motivation

The Alpha Vantage plan covers company fundamentals and basic economic indicators, but has two constraints: 25 requests/day free tier, and Alpha Vantage is a secondary source for economic data — it aggregates from primary sources like the Federal Reserve. This plan adds three complementary APIs that fill specific gaps:

  1. FRED — the primary source for US economic data (816K+ series). When a fact claims "GDP grew 5.7%," FRED is where that number originates.
  2. Finnhub — unique data Alpha Vantage doesn't have: congressional trading, ESG scores, patents, supply chains, lobbying.
  3. FMP — capacity relief for company fundamentals (250 req/day vs Alpha Vantage's 25).

Service Overview

FRED (Federal Reserve Economic Data)

AttributeValue
Base URLhttps://api.stlouisfed.org/fred
Auth?api_key=KEY (FRED_API_KEY env var, already in .env.local)
Free tier2 requests/second, no daily cap
Data816,000+ economic data series
HistoryDecades of data, some series back to 1940s
Source authorityFederal Reserve Bank of St. Louis — the primary source for US macro data

Key endpoints:

EndpointReturns
/fred/series/search?search_text=GDPFind series by keyword
/fred/series/observations?series_id=GDPActual data values over time
/fred/series?series_id=CPIAUCSLSeries metadata (title, units, frequency)
/fred/category/children?category_id=0Browse data categories

Key series IDs for fact verification:

SeriesIDDescription
Real GDPGDPQuarterly, billions of dollars
GDP Growth RateA191RL1Q225SBEAQuarterly % change
CPI (All Urban)CPIAUCSLMonthly, index 1982-84=100
Unemployment RateUNRATEMonthly, %
Federal Funds RateFEDFUNDSMonthly, %
10-Year TreasuryDGS10Daily, %
M2 Money SupplyM2SLMonthly, billions
Housing StartsHOUSTMonthly, thousands
Inflation (YoY)FPCPITOTLZGUSAAnnual, %
Trade BalanceBOPGSTBMonthly, millions
Consumer ConfidenceUMCSENTMonthly, index
S&P 500SP500Daily, index

Finnhub

AttributeValue
Base URLhttps://finnhub.io/api/v1
Auth?token=KEY or header X-Finnhub-Token (FINNHUB_API_KEY env var, already in .env.local)
Free tier60 requests/minute
Unique dataCongressional trading, ESG, patents, supply chains, lobbying, insider transactions

Key endpoints:

EndpointReturns
/stock/profile2?symbol=AAPLCompany profile (IPO date, market cap, industry)
/stock/insider-transactions?symbol=AAPLInsider trading activity
/stock/congressional-trading?symbol=AAPLCongressional stock trades
/stock/esg?symbol=AAPLESG scores and ratings
/stock/uspto-patent?symbol=AAPLPatent filings
/stock/supply-chain?symbol=AAPLSupply chain relationships
/stock/lobbying?symbol=AAPLLobbying expenditures
/calendar/earningsEarnings calendar
/calendar/ipoIPO calendar

FMP (Financial Modeling Prep)

AttributeValue
Base URLhttps://financialmodelingprep.com/api/v3
Auth?apikey=KEY (FMP_API_KEY env var, already in .env.local)
Free tier250 requests/day (US exchanges)
RoleCapacity backup for Alpha Vantage company fundamentals

Key endpoints:

EndpointReturns
/profile/{symbol}Company profile (market cap, IPO date, CEO, sector)
/income-statement/{symbol}Revenue, net income, EBITDA
/balance-sheet-statement/{symbol}Assets, liabilities, equity
/key-metrics/{symbol}PE ratio, debt/equity, ROE, book value
/historical-price-full/{symbol}Daily OHLCV history
/search?query=TeslaSymbol search by company name

Implementation

Challenge 1: FRED Client

File: packages/ai/src/fred-client.ts (new)

  • Base URL: https://api.stlouisfed.org/fred
  • Auth: api_key query param from FRED_API_KEY
  • In-memory cache: 24h TTL for series metadata, 1h for observations, 5K max entries
  • Metrics: fred.api_calls, fred.cache_hit, fred.series_found

Key methods:

searchSeries(query: string): Promise<FredSeries | null>
getObservations(seriesId: string, startDate?: string, endDate?: string): Promise<FredObservation[]>
getSeriesMetadata(seriesId: string): Promise<FredSeriesInfo | null>
formatFredContext(series: FredSeriesInfo, observations: FredObservation[]): string

// Convenience lookups for common claims
getGDP(year: number): Promise<FredObservation | null>
getCPI(year: number, month?: number): Promise<FredObservation | null>
getUnemployment(year: number, month?: number): Promise<FredObservation | null>
getInflation(year: number): Promise<FredObservation | null>

Acceptance: Can query "What was US GDP in 2021?" → GDP series → observation for 2021-Q4.

Challenge 2: FRED Evidence Integration

File: packages/ai/src/validation/evidence.ts

FRED is the primary source for economic claims — check it before Alpha Vantage for macro data:

// Economic indicator claims — FRED first (authoritative primary source)
if (hasEconomicClaim(factContext) && fredApiKey) {
  const indicator = detectEconomicIndicator(factContext)
  // Map claim to FRED series ID
  const seriesId = CLAIM_TO_FRED_SERIES[indicator] // e.g., 'gdp_growth' → 'A191RL1Q225SBEA'
  if (seriesId) {
    const year = extractYear(factContext)
    const observations = await getObservations(seriesId, `${year}-01-01`, `${year}-12-31`)
    if (observations.length > 0) {
      findings.push(`FRED (Federal Reserve): ${indicator} = ${observations[0].value} (${observations[0].date})`)
      sources.push({ name: 'FRED', type: 'authoritative' }) // higher weight than secondary sources
    }
  }
}

Confidence: FRED data is authoritative (primary source), not just corroborating:

  • FRED value matches claim → apiConfidence = 0.9 (near-definitive)
  • FRED value contradicts claim → flag as critical with high confidence

Acceptance: "US inflation hit 9.1% in June 2022" → FRED CPIAUCSL → actual value → verified or refuted.

Challenge 3: Finnhub Client

File: packages/ai/src/finnhub-client.ts (new)

  • Base URL: https://finnhub.io/api/v1
  • Auth: X-Finnhub-Token header from FINNHUB_API_KEY
  • In-memory cache: 1h TTL for profiles, 24h for ESG/patents, 3K max entries
  • Metrics: finnhub.api_calls, finnhub.cache_hit

Key methods:

getCompanyProfile(symbol: string): Promise<FinnhubProfile | null>
getInsiderTransactions(symbol: string): Promise<FinnhubInsiderTx[] | null>
getCongressionalTrading(symbol: string): Promise<FinnhubCongressTx[] | null>
getESGScores(symbol: string): Promise<FinnhubESG | null>
getPatents(symbol: string): Promise<FinnhubPatent[] | null>
getSupplyChain(symbol: string): Promise<FinnhubSupplyChain | null>
formatFinnhubContext(profile: FinnhubProfile): string

Acceptance: Can look up "AAPL" → company profile + ESG scores + patent count.

Challenge 4: Finnhub Evidence Integration

File: packages/ai/src/validation/evidence.ts

Finnhub supplements Alpha Vantage company lookups with unique data:

if (finnhubKey && hasCompanyClaim(factContext)) {
  const symbol = resolvedSymbol // from Alpha Vantage SYMBOL_SEARCH or FMP /search
  if (symbol) {
    // ESG claims
    if (hasESGClaim(factContext)) {
      const esg = await getESGScores(symbol)
      if (esg) findings.push(`Finnhub ESG: total ${esg.totalScore}, env ${esg.environmentScore}`)
    }
    // Congressional/insider trading claims
    if (hasTradingClaim(factContext)) {
      const trades = await getCongressionalTrading(symbol)
      if (trades?.length) findings.push(`Finnhub: ${trades.length} congressional trades found`)
    }
    // Patent claims
    if (hasPatentClaim(factContext)) {
      const patents = await getPatents(symbol)
      if (patents?.length) findings.push(`Finnhub: ${patents.length} patent filings`)
    }
  }
}

Acceptance: ESG, congressional trading, and patent claims get corroborated from Finnhub data.

Challenge 5: FMP Client (Capacity Backup)

File: packages/ai/src/fmp-client.ts (new)

  • Base URL: https://financialmodelingprep.com/api/v3
  • Auth: apikey query param from FMP_API_KEY
  • In-memory cache: 1h TTL, 3K max entries
  • Metrics: fmp.api_calls, fmp.cache_hit

Key methods:

searchSymbol(query: string): Promise<FmpSymbolMatch | null>
getCompanyProfile(symbol: string): Promise<FmpProfile | null>
getIncomeStatement(symbol: string): Promise<FmpIncomeStatement | null>
getKeyMetrics(symbol: string): Promise<FmpKeyMetrics | null>
formatFmpContext(profile: FmpProfile): string

Acceptance: Can look up "Tesla" → TSLA → profile with market cap, IPO date, revenue.

Challenge 6: FMP Evidence Fallback

File: packages/ai/src/validation/evidence.ts

FMP fires when Alpha Vantage returns null (rate limited or key missing):

// Company fundamentals — Alpha Vantage first, FMP fallback
let companyData = await alphaVantageOverview(symbol)
if (!companyData && fmpKey) {
  companyData = await fmpCompanyProfile(symbol)
  if (companyData) {
    findings.push(`FMP: ${companyData.companyName}, IPO ${companyData.ipoDate}, MCap $${companyData.mktCap}`)
  }
}

Acceptance: When Alpha Vantage hits 25/day limit, FMP seamlessly provides company data.

Challenge 7: World Bank Indicators Client

File: packages/ai/src/worldbank-client.ts (new)

The World Bank Indicators API provides ~16,000 development indicators across 200+ countries with 50+ years of history. Covers global GDP, population, poverty, life expectancy, emissions, literacy, and other development metrics that FRED (US-focused) doesn't cover.

Base URL: https://api.worldbank.org/v2 Auth: None required (fully open) Rate limits: None documented Cost: Free, forever

Query pattern: /country/{iso2}/indicator/{id}?date={year}&format=json

Key indicators for fact verification:

IndicatorIDDescription
GDP (current US$)NY.GDP.MKTP.CDCountry GDP in dollars
GDP growthNY.GDP.MKTP.KD.ZGAnnual % growth
PopulationSP.POP.TOTLTotal population
Life expectancySP.DYN.LE00.INAt birth, years
Poverty headcountSI.POV.DDAY% at $2.15/day
CO2 emissionsEN.ATM.CO2E.KTKilotons
Literacy rateSE.ADT.LITR.ZSAdult %, 15+
Internet usersIT.NET.USER.ZS% of population
Infant mortalitySP.DYN.IMRT.INPer 1,000 live births
Renewable energyEG.FEC.RNEW.ZS% of total consumption
Military spendingMS.MIL.XPND.GD.ZS% of GDP
UnemploymentSL.UEM.TOTL.ZS% of labor force

Key methods:

getIndicator(countryCode: string, indicatorId: string, year?: number): Promise<WorldBankObservation | null>
searchIndicator(query: string): Promise<WorldBankIndicatorInfo[]>
getCountryData(countryCode: string, indicatorId: string, startYear: number, endYear: number): Promise<WorldBankObservation[]>
formatWorldBankContext(obs: WorldBankObservation): string

// Convenience lookups
getCountryGDP(countryCode: string, year: number): Promise<number | null>
getCountryPopulation(countryCode: string, year: number): Promise<number | null>
getCountryLifeExpectancy(countryCode: string, year: number): Promise<number | null>
  • In-memory cache: 24h TTL, 5K max entries
  • Metrics: worldbank.api_calls, worldbank.cache_hit, worldbank.indicator_found
  • Country name → ISO2 code resolver (e.g., "India" → "IN", "United States" → "US")

Acceptance: Can query "India population 2023" → SP.POP.TOTL for IN → 1,438,069,596.

Challenge 8: World Bank Evidence Integration

File: packages/ai/src/validation/evidence.ts

World Bank supplements FRED for global/international claims:

// Global development claims — World Bank (complements FRED for non-US data)
const globalTopics = ['economics', 'politics', 'geography', 'history', 'health', 'environment']
if (globalTopics.some(t => topicPath.includes(t))) {
  // Country-specific claims with numeric values
  const country = detectCountry(factContext)
  if (country && hasNumericClaim(factContext)) {
    const indicator = detectWorldBankIndicator(factContext)
    // Map claim to indicator ID: "population" → SP.POP.TOTL, "GDP" → NY.GDP.MKTP.CD
    if (indicator) {
      const year = extractYear(factContext)
      const obs = await getIndicator(country.iso2, indicator.id, year)
      if (obs) {
        findings.push(`World Bank: ${country.name} ${indicator.name} = ${obs.value} (${obs.date})`)
        sources.push({ name: 'World Bank', type: 'authoritative' })
      }
    }
  }
}

FRED vs World Bank routing:

  • Claim mentions US-specific data (Federal funds rate, S&P 500, US CPI) → FRED first
  • Claim mentions a non-US country or global comparison → World Bank first
  • Both can fall through to each other as backup

Confidence:

  • World Bank value matches claim within margin → apiConfidence = 0.85 (authoritative)
  • World Bank value contradicts claim → flag as critical
  • No results → fall through

Acceptance: "India surpassed China in population in 2023" → World Bank SP.POP.TOTL for IN and CN → actual values → verified.

Challenge 9: Tests

Files:

  • packages/ai/src/__tests__/fred-client.test.ts (new)
  • packages/ai/src/__tests__/finnhub-client.test.ts (new)
  • packages/ai/src/__tests__/fmp-client.test.ts (new)
  • packages/ai/src/__tests__/worldbank-client.test.ts (new)

Per client:

  • Response parsing
  • Cache behavior
  • Rate limit tracking (FRED, Finnhub, FMP) / no-limit behavior (World Bank)
  • Graceful failure
  • Evidence claim detection helpers

World Bank specific:

  • Country name → ISO2 code resolution
  • Indicator ID detection from fact context
  • Multi-year range queries
  • JSON response parsing (.[1][0].value structure)

Acceptance: bun run test passes.

Evidence Source Priority

For financial/economic/development facts, the evidence pipeline queries sources in priority order:

PrioritySourceRoleDaily BudgetScope
1FREDAuthoritative US economic dataUnlimited (2 req/sec)US macro
2World BankAuthoritative global development dataUnlimited (no limits)200+ countries
3Alpha VantageCompany fundamentals + US economic backup25 req/dayCompanies
4FinnhubUnique data (ESG, congressional, patents)60 req/minCompanies
5FMPCompany fundamentals overflow250 req/dayCompanies

Total daily capacity: unlimited economic/development lookups (FRED + World Bank), ~275+ company lookups.

Cost

All free tier. No paid plans required for validation pipeline volumes.

APIFree TierPaid (if needed)
FRED2 req/sec, no daily capN/A (always free)
World BankNo limits, no key requiredN/A (always free)
Finnhub60 req/min$0 (free plan sufficient)
FMP250 req/day$14/month for 300 req/day

Dependencies

Keys already in .env.local:

  • FRED_API_KEY
  • FINNHUB_API_KEY
  • FMP_API_KEY
  • World Bank: no key needed

Add FRED, Finnhub, FMP to packages/config/src/index.ts env schema and .env.example.

Relationship to Other Evidence Plans

PlanDomainData
API-SportsSportsMatch results, player stats, game data
OpenAlex + Nobel Prize + NASAScience, academia, spaceAuthors, papers, institutions, prize attribution, exoplanets
Alpha VantageFinance (primary)Company fundamentals, stock prices, basic economic data
FRED + Finnhub + FMP + World BankFinance/economics (expansion)US macro, global development, ESG, congressional trading, capacity backup
DBpediaGeneral-purpose fallbackStructured Wikipedia infobox properties