-
Notifications
You must be signed in to change notification settings - Fork 0
external integrations
github-actions[bot] edited this page May 1, 2026
·
2 revisions
How InventoryAlert communicates with third-party APIs and external services.
- Purpose: Real-time stock quote data, company metadata, and analytics (earnings, recommendations, insiders, peers)
-
Auth: API key via
?token={API_KEY}query param (fromFINNHUB_API_KEYenv /appsettings.json → Finnhub:ApiKey) -
Rate Limits: 60 requests/minute on free tier. Internal cap set at 55 rpm via Redis counter
finnhub:ratelimit.
| Finnhub Endpoint | Our Route | Called By |
|---|---|---|
GET /api/v1/quote |
GET /stocks/{symbol}/quote |
StockDataService |
GET /api/v1/stock/profile2 |
GET /stocks/{symbol}/profile |
StockDataService |
GET /api/v1/stock/market-status |
GET /market/status |
StockDataService |
GET /api/v1/news |
GET /market/news |
NewsSyncJob (sync) + StockDataService (read) |
GET /api/v1/company-news |
GET /stocks/{symbol}/news |
NewsSyncJob (sync) + StockDataService (read) |
GET /api/v1/search |
GET /stocks/search |
Discovery flow |
GET /api/v1/stock/metric |
GET /stocks/{symbol}/financials |
SyncMetricsJob |
GET /api/v1/stock/earnings |
GET /stocks/{symbol}/earnings |
SyncEarningsJob |
GET /api/v1/stock/recommendation |
GET /stocks/{symbol}/recommendation |
SyncRecommendationsJob |
GET /api/v1/stock/insider-transactions |
GET /stocks/{symbol}/insiders |
SyncInsidersJob |
GET /api/v1/stock/peers |
GET /stocks/{symbol}/peers |
StockDataService |
GET /api/v1/calendar/earnings |
GET /market/calendar/earnings |
StockDataService |
GET /api/v1/calendar/ipo |
GET /market/calendar/ipo |
StockDataService |
GET /api/v1/stock/market-holiday |
GET /market/holiday |
StockDataService |
// Finnhub /quote Response
{
"c": 172.50, // currentPrice
"d": -2.30, // change
"dp": -1.32, // changePercent
"h": 175.00, // high
"l": 170.10, // low
"o": 173.00, // open
"pc": 174.80, // prevClose
"t": 1713000000 // timestamp
}- If
c(current price) isnullor0→ skip that symbol, log warning, continue loop. - If Finnhub returns an HTTP error → log and skip, never throw.
- Quotes are cached in Redis for 30 seconds (
quote:{symbol}).
// IFinnhubClient contract
if (quote?.CurrentPrice is null or 0) return null; // skip — free tier limitation-
Purpose: Async event bus between
InventoryAlert.Api/InventoryAlert.Worker -
Local Emulation: Moto running in Docker at
http://moto:5000 -
Configuration:
Aws:SqsQueueUrlinappsettings.Docker.json
| Queue | Purpose | Publisher | Consumer |
|---|---|---|---|
inventory-events |
Domain event bus | Api, Worker | Worker (IntegrationMessageRouter) |
inventory-events-dlq |
Dead letter queue | SQS (auto-redrive after 5 failures) | Manual replay via Hangfire / AWS Console |
// ProcessQueueJob — long-polling
var messages = await _sqsHelper.ReceiveMessagesAsync(queueUrl, maxMessages: 10, ct: ct);- Visibility Timeout: 30 seconds (message hidden from other consumers while processing)
- Max Receive Count: 5 — after 5 failures, message is moved to DLQ automatically
- Long-polling Wait: 20 seconds per poll cycle to reduce empty-response API calls
- Purpose: Permanent storage for high-volume news data (never deleted, queryable by range)
-
Local Emulation: Same Moto container as SQS (
http://moto:5000)
| Table | PK | SK | GSI |
|---|---|---|---|
inventoryalert-market-news |
CATEGORY#<category> |
TS#<unix_ms> |
— |
inventoryalert-company-news |
SYMBOL#<ticker> |
TS#<unix_ms> |
BySymbolAndDate (Symbol, SK) |
-
Write:
PutItemAsyncon news fetch. Deduplication byNewsIdattribute before insert. -
Read (Market News):
QueryAsyncwith PKCATEGORY#general, SK range fromTS#{30_days_ago}toTS#{now}. -
Read (Company News):
QueryAsyncwith PKSYMBOL#TSLA, sorted descending, limit 20.
No TTL: News is retained indefinitely as a historical archive. Use SK range filters to retrieve recent articles.
- Purpose: Quote caching, SQS idempotency markers, alert cooldown
-
Local:
localhost:6379(Docker:inventory-cache) - Namespaces:
| Key | TTL | Purpose |
|---|---|---|
quote:{symbol} |
30s | Cached Finnhub quote |
msg:processed:{messageId} |
24h | SQS idempotency marker (written after successful processing) |
inventoryalert:alerts:cooldown:v1:{userId}:{ruleId} |
24h | Alert cooldown gate (per user + rule) |