Package Exports
- market-feed
- market-feed/calendar
- market-feed/consensus
- market-feed/stream
Readme
market-feed
Unified TypeScript client for financial market data. Wraps Yahoo Finance, Alpha Vantage, and Polygon.io under one consistent interface — with caching and automatic fallback built in.
The problem
Every free financial API speaks a different language:
// Yahoo Finance
result.chart.result[0].meta.regularMarketPrice
// Alpha Vantage
data["Global Quote"]["05. price"]
// Polygon.io
data.ticker.lastTrade.pYou write adapters, you add caching, you handle fallback — for every project, every time.
The solution
import { MarketFeed } from "market-feed";
const feed = new MarketFeed();
const quote = await feed.quote("AAPL");
console.log(quote.price); // always a number, always the same keyOne interface. Three providers. Zero API key required for Yahoo Finance.
Features
- Unified types —
Quote,HistoricalBar,CompanyProfile,NewsItem,SearchResultare consistent regardless of which provider answers - Zero production dependencies — uses native
fetch, works everywhere - Built-in LRU cache — configurable TTL per method, pluggable driver (Redis, Upstash, etc.)
- Automatic fallback — if Yahoo is down, tries Alpha Vantage, then Polygon
- Rate-limit aware — won't silently burn your free Alpha Vantage / Polygon quota
- Strict TypeScript — no
any, full autocomplete, compile-time safety - Multi-runtime — Node 18+, Bun 1+, Deno 2+, Cloudflare Workers
- Escape hatch — pass
{ raw: true }to get the original provider response
Install
npm install market-feed
# or
pnpm add market-feed
# or
bun add market-feedQuick Start
import { MarketFeed } from "market-feed";
// Zero-config — uses Yahoo Finance, no API key needed
const feed = new MarketFeed();
// Single quote
const aapl = await feed.quote("AAPL");
console.log(`${aapl.symbol}: $${aapl.price.toFixed(2)}`);
// Multiple quotes (parallel)
const quotes = await feed.quote(["MSFT", "GOOGL", "AMZN"]);
// Historical data
const history = await feed.historical("AAPL", {
period1: "2024-01-01",
period2: "2024-12-31",
interval: "1d",
});
// Search
const results = await feed.search("Tesla");
// Company profile
const profile = await feed.company("AAPL");
console.log(profile.sector); // "Technology"Providers
| Provider | API Key | Quote | Historical | Search | Company | News | Market Status |
|---|---|---|---|---|---|---|---|
| Yahoo Finance | Not required | ✓ | ✓ | ✓ | ✓ | — | — |
| Alpha Vantage | Free (25/day) | ✓ | ✓ | ✓ | ✓ | — | — |
| Polygon.io | Free (delayed) | ✓ | ✓ | ✓ | ✓ | ✓ | — |
Get free keys: Alpha Vantage · Polygon.io
Using multiple providers
import {
MarketFeed,
YahooProvider,
AlphaVantageProvider,
PolygonProvider,
} from "market-feed";
const feed = new MarketFeed({
providers: [
new YahooProvider(),
new AlphaVantageProvider({ apiKey: process.env.AV_KEY }),
new PolygonProvider({ apiKey: process.env.POLYGON_KEY }),
],
fallback: true, // auto-try next provider on failure
});Caching
The default LRU cache stores responses in memory with sensible TTLs:
| Method | Default TTL |
|---|---|
quote |
60s |
historical |
1 hour |
company |
24 hours |
news |
5 minutes |
search |
10 minutes |
marketStatus |
60s |
Override TTLs
const feed = new MarketFeed({
cache: {
ttl: 60, // default fallback TTL
maxSize: 1000, // max entries in memory
ttlOverrides: {
quote: 15, // aggressive refresh for real-time feel
company: 604800, // company profiles change rarely
},
},
});Disable caching
const feed = new MarketFeed({ cache: false });Custom cache driver (Redis, Upstash, filesystem...)
import type { CacheDriver } from "market-feed";
import { createClient } from "redis";
const redis = createClient();
await redis.connect();
const driver: CacheDriver = {
async get<T>(key: string) {
const val = await redis.get(key);
return val ? (JSON.parse(val) as T) : undefined;
},
async set<T>(key: string, value: T, ttl = 60) {
await redis.set(key, JSON.stringify(value), { EX: ttl });
},
async delete(key: string) { await redis.del(key); },
async clear() { await redis.flushDb(); },
};
const feed = new MarketFeed({ cache: { driver } });API Reference
new MarketFeed(options?)
| Option | Type | Default | Description |
|---|---|---|---|
providers |
MarketProvider[] |
[new YahooProvider()] |
Provider chain |
cache |
CacheConfig | false |
LRU, 60s TTL | Cache configuration |
fallback |
boolean |
true |
Auto-failover on provider error |
Methods
// Quotes
feed.quote(symbol: string, options?: QuoteOptions): Promise<Quote>
feed.quote(symbols: string[], options?: QuoteOptions): Promise<Quote[]>
// Historical bars
feed.historical(symbol: string, options?: HistoricalOptions): Promise<HistoricalBar[]>
// Symbol search
feed.search(query: string, options?: SearchOptions): Promise<SearchResult[]>
// Company profile
feed.company(symbol: string, options?: CompanyOptions): Promise<CompanyProfile>
// News
feed.news(symbol: string, options?: NewsOptions): Promise<NewsItem[]>
// Market status
feed.marketStatus(market?: string): Promise<MarketStatus>
// Cache management
feed.clearCache(): Promise<void>
feed.invalidate(key: string): Promise<void>HistoricalOptions
interface HistoricalOptions {
period1?: string | Date; // start date, default: 1 year ago
period2?: string | Date; // end date, default: today
interval?: "1m" | "2m" | "5m" | "15m" | "30m" | "60m" | "1h"
| "1d" | "5d" | "1wk" | "1mo" | "3mo"; // default: "1d"
raw?: boolean;
}Error Handling
import {
MarketFeedError,
ProviderError,
RateLimitError,
AllProvidersFailedError,
} from "market-feed";
try {
const quote = await feed.quote("AAPL");
} catch (err) {
if (err instanceof RateLimitError) {
console.log(`Rate limited. Retry after: ${err.retryAfter?.toISOString()}`);
} else if (err instanceof AllProvidersFailedError) {
console.log("All providers failed:", err.errors.map(e => e.message));
} else if (err instanceof ProviderError) {
console.log(`Provider error (${err.provider}): ${err.message}`);
}
}Building a Custom Provider
Implement the MarketProvider interface to add any data source:
import type { MarketProvider, Quote } from "market-feed";
class MyProvider implements MarketProvider {
readonly name = "my-provider";
async quote(symbols: string[]): Promise<Quote[]> {
// fetch from your API, return normalised Quote objects
}
async historical(symbol: string, options) {
// ...
}
async search(query: string) {
// ...
}
}
const feed = new MarketFeed({ providers: [new MyProvider()] });Runtime Compatibility
| Runtime | Version | Notes |
|---|---|---|
| Node.js | 18+ | Requires native fetch (available since Node 18) |
| Bun | 1+ | Fully supported |
| Deno | 2+ | Fully supported |
| Cloudflare Workers | Latest | Fully supported |
| Browser | — | Not supported — Yahoo Finance blocks CORS. Use a server-side proxy. |
Contributing
See CONTRIBUTING.md. All contributions welcome.
git clone https://github.com/piyushgupta344/market-feed
cd market-feed
pnpm install
pnpm testDisclaimer
This library is not affiliated with or endorsed by Yahoo Finance, Alpha Vantage, or Polygon.io. Data is provided for informational purposes only and should not be used as the sole basis for investment decisions.