yahoo-finance2

repository·dev·Indexed 21 days ago

https://github.com/gadicc/yahoo-finance2

An unofficial Yahoo Finance API for server-side environments including Node.js, Bun, Deno, and Cloudflare Workers. It provides comprehensive financial data through a JavaScript/TypeScript library, a CLI, Agent Skills, and a Model Context Protocol (MCP) server. Features include specialized modules for quotes, charts, and historical data, built-in concurrency management, and support for various transport modes (stdio and HTTP) for AI agent integration.

Tokens
24.8K
Snippets
79
Records
106
Agent score
70%

What's inside yahoo-finance2

  1. What is the yahoo-finance2 MCP server?

    dev

    The yahoo-finance2 library includes a Model Context Protocol (MCP) server that exposes its Yahoo Finance modules as a set of read-only tools for AI clients. This allows AI agents to fetch real-time financial data directly.

    Available Tools:

    • quote, quoteCombine, search, quoteSummary, chart, historical, options, trendingSymbols, screener, recommendationsBySymbol, insights, fundamentalsTimeSeries

    Note: Deprecated modules like autoc, dailyGainers, and dailyLosers are not exposed.

  2. How concurrency and rate limiting work in yahoo-finance2

    dev

    To prevent overloading Yahoo's servers or being rate-limited, yahoo-finance2 instances include a built-in concurrency manager. By default, an instance has a concurrency limit of 4, meaning no more than 4 simultaneous network requests will be active at any time.

    Key behaviors:

    • Instance-wide limits: The concurrency and interval limits apply across the entire instance. If you call different methods (e.g., quote() and quoteSummary()) from different parts of your application using the same instance, they all share the same queue and limits.
    • FIFO Queueing: Calls are queued in the order they are invoked.
    • Process-local: Limits are local to the specific process. Multiple Node/Deno processes, worker threads, or serverless instances will each maintain their own independent queues and limits.
    • Safe Parallelism: You can use Promise.all() or forEach with async callbacks to trigger many requests at once; the library will automatically manage the queue to ensure the concurrency limit is never exceeded.
  3. Optimize multiple quote requests with quoteCombine()

    dev
    If your primary goal is to fetch data for multiple symbols using the quote API, use quoteCombine(symbol). This method optimizes network usage by combining multiple individual symbol requests into a single network request to Yahoo, rather than sending multiple separate calls.
  4. Use FakeTime for testing time-dependent logic

    dev

    When testing modules that rely on setTimeout or intervals (like the Queue class), use FakeTime from @std/testing/time to control time progression without waiting for real-world clock cycles.

    Usage Pattern

    1. Import FakeTime from @std/testing/time.
    2. Instantiate new FakeTime().
    3. Use await time.tickAsync(ms) to advance the clock.
    4. Always use a try...finally block to ensure time.restore() is called, preventing side effects in other tests.
    import { FakeTime } from "@std/testing/time";
    
    const time = new FakeTime();
    try {
      // ... perform actions that trigger timeouts
      await time.tickAsync(100);
      // ... assert state after time has passed
    } finally {
      time.restore();
    }
  5. Handle CLI output and streams

    dev

    The CLI is designed for safe scripting with distinct output streams:

    • stdout: Contains successful module results, --help output, and --version output.
      • If stdout is a terminal, results are printed in a human-readable format.
      • If stdout is piped or redirected, results are emitted as JSON. For example, Map results (like quote with { "return": "map" }) are normalized to plain JSON objects.
    • stderr: Contains errors, warnings, and validation diagnostics.

    Example of piping JSON output to jq:

    npx yahoo-finance2 quote AAPL | jq '.regularMarketPrice'
  6. Handle validation and errors

    dev

    Wrap module calls in try/catch blocks. Network errors, Yahoo API errors, delisted symbols, and missing data are expected.

    By default, returned data is validated and coerced (e.g., dates become Date objects).

    • Use { validateResult: false } only if the caller accepts unknown output and performs its own checks.
    • Use { validateOptions: false } only when experimenting with unmodeled Yahoo query parameters.
  7. Cap consent redirect recursion depth in getCrumb

    dev

    The _getCrumb function in src/lib/getCrumb.ts uses recursion to handle the Yahoo EU-consent redirect flow. To prevent unbounded recursion (which can lead to stack overflow or rate-limiting) caused by misbehaving endpoints, implement a depth limit.

    1. Add a depth parameter to _getCrumb (defaulting to 0).
    2. Check the depth against a constant (e.g., MAX_CONSENT_REDIRECT_DEPTH = 5).
    3. Increment the depth in the recursive call: return await _getCrumb(..., depth + 1).

    The public getCrumb wrapper does not need to expose the depth parameter.

    // Inside src/lib/getCrumb.ts
    const MAX_CONSENT_REDIRECT_DEPTH = 5;
    
    async function _getCrumb(
      cookieJar: CookieJar,
      fetch: Fetch,
      // ... other params
      depth = 0
    ) {
      if (depth > MAX_CONSENT_REDIRECT_DEPTH) {
        throw new Error(
          "Too many consent redirects while fetching Yahoo crumb (max " +
            MAX_CONSENT_REDIRECT_DEPTH + "). Please report."
        );
      }
      // ... logic
      return await _getCrumb(
        // ... params
        depth + 1
      );
    }
  8. Understand per-instance state isolation (Plan 004)

    dev

    The project is transitioning from module-level (global) state to per-instance state to prevent side effects between different YahooFinance instances. This affects three core areas:

    1. Crumb State: Scoped to the ExtendedCookieJar. Instances with different cookie jars will no longer share the same crumb or promise cache.
    2. Request Queue: The Queue used for managing request concurrency is now per-instance. A process creating $N$ instances can now issue $N$ times the previous global concurrency limit.
    3. Debounce Map: The slugMap used in quoteCombine is now per-instance, ensuring that debouncing/batching logic for one instance does not interfere with another.
  9. Understand instance-based state isolation in YahooFinance

    dev

    In yahoo-finance2 v3+, the library uses an instance-based API via new YahooFinance(options). Each instance is intended to have its own isolated state, including its own cookieJar, logger, and queue options.

    However, users should be aware of potential state leakage if multiple instances are used in a single process (e.g., in multi-tenant environments where each user has a different session, proxy, or region). Key areas where state might be shared across instances include:

    1. Crumb Cache: The crumb and its associated promise are cryptographically paired with cookies in a cookieJar. If instances share a crumb but use different cookie jars, Yahoo may reject requests with a 401 "Invalid Crumb" error.
    2. Request Queue: All instances may funnel through a single global _queue. If different instances have different concurrency or interval settings, they may conflict as the last instance to run assertQueueOptions wins.
    3. Quote Combine Debounce Map: The slugMap used for debouncing calls might be shared. This can cause requests from one instance to be merged into a batch executed using the context (cookie jar, fetch options, logger) of a different instance.

    To ensure proper isolation, ensure each YahooFinance instance is provided with its own unique cookieJar in the options object.

  10. How quoteCombine handles symbol mismatches

    dev

    The quoteCombine() function batches multiple single-symbol quote() requests into a single network call to improve efficiency. It then distributes the results back to the individual callers using a symbol-based lookup.

    The Symbol Mismatch Problem: Yahoo Finance may normalize symbols (e.g., converting aapl to AAPL). If the symbol returned by Yahoo does not exactly match the symbol string provided by the caller, the internal distribution loop may attempt to access an undefined entry in the symbol map. In older versions, this would throw a TypeError, causing the entire batch of pending requests to be rejected with a confusing error instead of just the mismatched symbol failing.

    Current/Correct Behavior:

    • If a returned symbol does not match any requested symbol (due to normalization or being unrequested), it should be ignored during the distribution phase.
    • The specific caller who requested the mismatched symbol should fall through to a fallback mechanism that resolves their promise with undefined rather than rejecting the whole batch.
    • A successful batch should ensure that matching symbols receive their Quote data, while unmatched or non-existent symbols resolve to undefined.
  11. Select the appropriate Yahoo Finance module

    dev

    Choose a module based on your data requirements:

    • search(query): Use first if a symbol is uncertain.
    • quote(symbolOrSymbols): For current or near real-time data. Accepts a single symbol or an array. Use fields to limit payloads.
    • quoteCombine(symbol): Debounces multiple single-symbol calls into fewer quote() requests; ideal for many independent code paths.
    • chart(symbol, { period1, period2, interval }): For historical chart data (dividends/splits included). Use return: "array" for easier iteration or return: "object" to mirror Yahoo's native shape.
    • historical(): For a simpler OHLCV history interface.
    • quoteSummary(symbol, { modules }): For profile, price, summary detail, filings, ownership, and recommendations. Request only required modules.
    • fundamentalsTimeSeries(): For financial statements.
    • options(symbol): For options chains.
    • screener(): Replaces deprecated gainers/losers modules. Use predefined screeners like day_gainers, day_losers, or most_actives.
    • trendingSymbols(region), recommendationsBySymbol(), and insights(): For specialized endpoints.
  12. Handle errors in yahoo-finance2

    dev

    Because the library relies on external services, you should wrap API calls in try...catch blocks. Common failure scenarios include network errors, HTTP errors, missing resources, validation errors, and delisted stocks.

    Specific error types are accessible via yahooFinance.errors:

    • FailedYahooValidationError: Thrown when the response does not match the expected schema. The error.result property may contain a partially validated or coerced result.
    • HTTPError: Thrown for HTTP failures; the message property contains the HTTP Response statusText.
    • Generic Error: Thrown when a successful HTTP request returns a JSON body containing an error shape (e.g., { error: { name: "ErrorName", description: "string" } }) that doesn't map to a specific class. The message property will contain the description.
    import YahooFinance from "yahoo-finance2";
    const yahooFinance = new YahooFinance();
    
    let result;
    try {
      result = await yahooFinance.quote(symbol);
    } catch (error) {
      if (error instanceof yahooFinance.errors.FailedYahooValidationError) {
        // error.result will be a partially validated / coerced result.
      } else if (error instanceof yahooFinance.errors.HTTPError) {
        // Log and skip
        console.warn(`Skipping yf.quote("${symbol}"): [${error.name}] ${error.message}`);
        return;
      } else {
        // Log and skip
        console.warn(`Skipping yf.quote("${symbol}"): [${error.name}] ${error.message}`);
        return;
      }
    }
    
    doSomethingWith(result);