Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions .changeset/api-compliance-v2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---
"fmp-node-sdk": major
---

## Breaking Changes

### API Migration: v3/v4 → Stable API

**This is a complete API migration.** The SDK now uses FMP's new stable API instead of the legacy v3/v4 endpoints.

- Base URL changed from `https://financialmodelingprep.com/api` to `https://financialmodelingprep.com/stable`
- All endpoint paths have been updated to match the stable API specification
- Path parameters converted to query parameters (e.g., `/v3/profile/AAPL` → `/profile?symbol=AAPL`)

If you were customizing the `baseUrl` in your config, you'll need to update it.

### Endpoint Changes

All endpoints have been migrated. Examples of changes:

| Resource | Old Endpoint | New Endpoint |
|----------|-------------|--------------|
| Company | `v3/profile/{symbol}` | `profile?symbol=` |
| Company | `v3/quote/{symbol}` | `quote?symbol=` |
| Company | `v3/search` | `search-symbol`, `search-name` |
| Analyst | `v3/analyst-estimates` | `analyst-estimates` |
| Analyst | `v4/price-target` | `price-target` |
| Analyst | `v3/grade` | `grades` |
| Bulk | `v4/profile/all` | `profile-bulk` |
| Bulk | `v4/dcf` | `dcf-bulk` |
| Financials | `v3/income-statement/{symbol}` | `income-statement?symbol=` |
| Market | `v3/historical-price-full/{symbol}` | `historical-price-eod/full?symbol=` |

### Method Signature Changes

- `CompanyResource.search()` is now deprecated, use `searchSymbol()` or `searchName()`
- `CompanyResource.getDelistedCompanies(limit?)` now takes `(page, limit)` parameters
- `BulkResource.getAllProfiles()` now takes optional `part` parameter for pagination
- `BulkResource.getAllETFHoldings()` now takes optional `part` parameter for pagination
- `CommoditiesResource.getHistoricalPrices()` now returns `HistoricalPrice[]` instead of `{ historical: HistoricalPrice[] }`

### Type Changes

#### DCFValuation and LeveredDCF
- Renamed `'Stock Price'` property to `stockPrice`

#### Quote type - Nullable fields
The following fields are now nullable to accurately reflect the API response:
- `marketCap`: `number` → `number | null`
- `priceAvg50`: `number` → `number | null`
- `priceAvg200`: `number` → `number | null`
- `eps`: `number` → `number | null`
- `pe`: `number` → `number | null`
- `earningsAnnouncement`: `string` → `string | null`
- `sharesOutstanding`: `number` → `number | null`

#### EarningsSurprise type
- `actualEarningResult`: `number` → `number | null`
- `estimatedEarning`: `number` → `number | null`

## New Features

### NewsResource
- `getAvailableTranscriptSymbols()` - Get symbols with available earnings call transcripts
- `searchPressReleases(query, limit)` - Search press releases
- `searchStockNews(query, limit)` - Search stock news
- `searchCryptoNews(query, limit)` - Search crypto news
- `searchForexNews(query, limit)` - Search forex news

### InsiderResource
- `getHolderPerformanceSummary(cik, date)` - 13F portfolio holdings performance
- `getIndustryPerformanceSummary(cik, date)` - 13F industry portfolio holdings performance

### AnalystResource
- `getGradesSummary(symbol)` - Analyst grades summary

### ValuationResource
- `getCustomLeveredDCF(symbol)` - Custom levered DCF valuation

### CompanyResource
- `searchSymbol(query, limit, exchange)` - Search by symbol
- `searchName(query, limit, exchange)` - Search by company name
- `getBatchAftermarketTrades(symbols)` - Batch aftermarket trades
- `getETFList()` - List of all ETFs
- `getMutualFundList()` - List of all mutual funds

## New Types
- `PortfolioHoldingsSummary` - 13F portfolio holdings summary
- `IndustryPortfolioHoldingsSummary` - 13F industry portfolio holdings summary
- `FundListItem` - ETF/Mutual fund list item
- `QuoteShort` - Simplified quote data
- `AftermarketTrade` - Aftermarket trade data
- `AftermarketQuote` - Aftermarket quote data
- `PriceChange` - Stock price change data

## Migration Guide

1. **Update base URL** (if customizing): Change from `/api` to `/stable`
2. **Update search calls**: Replace `.search()` with `.searchSymbol()` or `.searchName()`
3. **Handle nullable fields**: Add null checks for Quote fields that are now nullable
4. **Update DCF access**: Change `result['Stock Price']` to `result.stockPrice`
5. **Update commodity historical**: Handle direct array return instead of `{ historical: [] }`
2 changes: 1 addition & 1 deletion src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { FMPAPIError } from './errors/index.js';
* Default configuration values
*/
const DEFAULT_CONFIG = {
baseUrl: 'https://financialmodelingprep.com/api',
baseUrl: 'https://financialmodelingprep.com/stable',
timeout: 30000,
retries: 3,
} as const;
Expand Down
7 changes: 4 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,14 +146,15 @@ export type {
StockScreenerParams,
StockScreenerResult,
ExchangeSymbol,
// Financial statement growth types
IncomeStatementGrowth,
BalanceSheetGrowth,
CashFlowStatementGrowth,
} from './types/index.js';

// Export bulk types
export type {
EarningsSurprise,
IncomeStatementGrowth,
BalanceSheetGrowth,
CashFlowStatementGrowth,
EODPrice,
} from './resources/bulk.js';

Expand Down
63 changes: 41 additions & 22 deletions src/resources/analyst.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,23 @@ export class AnalystResource {
period: Period = Period.Annual,
limit?: number
): Promise<AnalystEstimate[]> {
const params: Record<string, string | number> = { symbol: symbol.toUpperCase(), period };
const params: Record<string, string | number> = {
symbol: symbol.toUpperCase(),
period,
};
if (limit) params.limit = limit;

return this.client.get<AnalystEstimate[]>('v3/analyst-estimates', { searchParams: params });
return this.client.get<AnalystEstimate[]>('analyst-estimates', {
searchParams: params,
});
}

/**
* Get price targets
* @param symbol - Stock symbol
*/
async getPriceTargets(symbol: string): Promise<PriceTarget[]> {
return this.client.get<PriceTarget[]>(`v4/price-target`, {
return this.client.get<PriceTarget[]>('price-target', {
searchParams: { symbol: symbol.toUpperCase() },
});
}
Expand All @@ -49,10 +54,9 @@ export class AnalystResource {
* @param symbol - Stock symbol
*/
async getPriceTargetSummary(symbol: string): Promise<PriceTargetSummary> {
const result = await this.client.get<PriceTargetSummary[]>(
`v4/price-target-summary`,
{ searchParams: { symbol: symbol.toUpperCase() } }
);
const result = await this.client.get<PriceTargetSummary[]>('price-target-summary', {
searchParams: { symbol: symbol.toUpperCase() },
});
return result[0]!;
}

Expand All @@ -61,10 +65,9 @@ export class AnalystResource {
* @param symbol - Stock symbol
*/
async getPriceTargetConsensus(symbol: string): Promise<PriceTargetConsensus> {
const result = await this.client.get<PriceTargetConsensus[]>(
`v4/price-target-consensus`,
{ searchParams: { symbol: symbol.toUpperCase() } }
);
const result = await this.client.get<PriceTargetConsensus[]>('price-target-consensus', {
searchParams: { symbol: symbol.toUpperCase() },
});
return result[0]!;
}

Expand All @@ -73,7 +76,9 @@ export class AnalystResource {
* @param symbol - Stock symbol
*/
async getRecommendations(symbol: string): Promise<AnalystRecommendation[]> {
return this.client.get<AnalystRecommendation[]>(`v3/analyst-stock-recommendations/${symbol.toUpperCase()}`);
return this.client.get<AnalystRecommendation[]>('analyst-stock-recommendations', {
searchParams: { symbol: symbol.toUpperCase() },
});
}

/**
Expand All @@ -85,18 +90,19 @@ export class AnalystResource {
const params: Record<string, string | number> = { symbol: symbol.toUpperCase() };
if (limit) params.limit = limit;

return this.client.get<StockGrade[]>('v3/grade', { searchParams: params });
return this.client.get<StockGrade[]>('grades', {
searchParams: params,
});
}

/**
* Get upgrades and downgrades consensus
* @param symbol - Stock symbol
*/
async getUpgradesDowngradesConsensus(symbol: string): Promise<UpgradesDowngradesConsensus> {
const result = await this.client.get<UpgradesDowngradesConsensus[]>(
`v4/upgrades-downgrades-consensus`,
{ searchParams: { symbol: symbol.toUpperCase() } }
);
const result = await this.client.get<UpgradesDowngradesConsensus[]>('grades-consensus', {
searchParams: { symbol: symbol.toUpperCase() },
});
return result[0]!;
}

Expand All @@ -105,9 +111,9 @@ export class AnalystResource {
* @param symbol - Stock symbol
*/
async getRatingsSnapshot(symbol: string): Promise<AnalystRecommendation> {
const result = await this.client.get<AnalystRecommendation[]>(
`v3/rating/${symbol.toUpperCase()}`
);
const result = await this.client.get<AnalystRecommendation[]>('rating', {
searchParams: { symbol: symbol.toUpperCase() },
});
return result[0]!;
}

Expand All @@ -117,8 +123,21 @@ export class AnalystResource {
* @param limit - Number of results
*/
async getHistoricalGrades(symbol: string, limit?: number): Promise<StockGrade[]> {
const params: Record<string, string | number> = {};
const params: Record<string, string | number> = { symbol: symbol.toUpperCase() };
if (limit) params.limit = limit;
return this.client.get<StockGrade[]>(`v3/historical-rating/${symbol.toUpperCase()}`, { searchParams: params });
return this.client.get<StockGrade[]>('grades-historical', {
searchParams: params,
});
}

/**
* Get stock grades summary
* @param symbol - Stock symbol
*/
async getGradesSummary(symbol: string): Promise<UpgradesDowngradesConsensus> {
const result = await this.client.get<UpgradesDowngradesConsensus[]>('grades-summary', {
searchParams: { symbol: symbol.toUpperCase() },
});
return result[0]!;
}
}
Loading