A technical deep-dive into building BL-BOMBERG — a real-time financial dashboard powered by a single unified API.
The Problem: API Integration Hell
Building a financial dashboard typically requires stitching together multiple APIs:
- Yahoo Finance or Alpha Vantage for stock prices
- NYT or NewsAPI for news headlines
- Twitter API for trending topics
- OpenAI or Anthropic for AI-generated insights
Each one has its own authentication, rate limits, SDKs, pricing models, and data formats. For a side project, this is an absurd amount of overhead.
The Solution: AIsa.one
AIsa.one is a unified API platform that aggregates financial data, news, social trends, and AI inference behind a single endpoint: https://api.aisa.one/v1
One API key gets you:
| Capability | Endpoint | What It Returns |
|---|---|---|
| Crypto Prices | /crypto/prices/snapshot?symbol=BTC |
Real-time BTC/ETH pricing |
| Stock Data | /financial-metrics/snapshot?ticker=NVDA |
Price, change %, volume, high/low |
| News | /news?limit=10 |
Headlines with timestamps and sources |
| Social Trends | /twitter/trends?id=23424977 |
Trending topics (US) |
| AI Chat | /chat/completions |
OpenAI-compatible LLM inference |
The chat endpoint is particularly powerful: AIsa provides access to Kimi K2.5, Moonshot AI's latest model, through an OpenAI-compatible interface. This means you can use the standard openai npm package with zero code changes — just swap the baseURL.
Architecture Overview
BL-BOMBERG is a Next.js 15 application with the App Router. The architecture is intentionally simple:
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Browser │────▶│ Next.js API │────▶│ AIsa.one │
│ (React Client) │◀────│ Route (Server) │◀────│ Unified API │
└──────────────────┘ └──────────────────┘ └──────────────────┘
│
5-min cache
Why Next.js API Routes?
API keys should never touch the browser. By using Next.js API Routes, the AISA key lives exclusively on the server. The browser only ever calls /api/dashboard, which acts as a secure proxy.
The AisaEngine Class
All AIsa interactions are encapsulated in a single class:
// src/lib/aisa-engine.ts
import OpenAI from "openai";
const API_KEY = process.env.AISA_API_KEY;
const BASE_URL = "https://api.aisa.one/v1";
const client = new OpenAI({
apiKey: API_KEY,
baseURL: BASE_URL,
});
export class AisaEngine {
private headers: HeadersInit;
constructor() {
this.headers = {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json",
};
}
async getMarketData() {
const [btc, eth, nvda, tsla] = await Promise.all([
fetch(`${BASE_URL}/crypto/prices/snapshot?symbol=BTC`,
{ headers: this.headers }),
fetch(`${BASE_URL}/crypto/prices/snapshot?symbol=ETH`,
{ headers: this.headers }),
fetch(`${BASE_URL}/financial-metrics/snapshot?ticker=NVDA`,
{ headers: this.headers }),
fetch(`${BASE_URL}/financial-metrics/snapshot?ticker=TSLA`,
{ headers: this.headers }),
]);
// ... parse and return
}
async generateAiBrief(marketData: any) {
const response = await client.chat.completions.create({
model: "kimi-k2.5",
messages: [
{
role: "system",
content: "You are a Bloomberg Terminal AI. Output a strict, " +
"2-sentence market flash update. No fluff. " +
"Use financial jargon."
},
{
role: "user",
content: `Data: ${JSON.stringify(marketData)}`
},
],
});
return response.choices[0].message.content;
}
}
Notice: the same AIsa API key is used for both REST data calls (fetch) and AI inference (openai SDK). This is the core value proposition — one provider, one key, one bill.
Kimi K2.5: The AI Behind the Brief
The AI Market Brief feature uses Kimi K2.5 from Moonshot AI, accessed through AISA's OpenAI-compatible endpoint.
Why Kimi K2.5?
- Financial comprehension: K2.5 handles numerical data and financial jargon well
- Speed: Sub-second responses for short market updates
- Cost: Competitive pricing through AISA's platform
- Compatibility: Standard OpenAI SDK — zero learning curve
The Prompt Engineering
The system prompt is deliberately terse:
You are a Bloomberg Terminal AI. Output a strict, 2-sentence market
flash update based on data. No fluff. Use financial jargon.
This produces output like:
TECH SECTOR RALLY CONTINUES AMID RATE CUT HOPES. BTC-USD SHOWING STRONG RESISTANCE AT 65K LEVELS.
Short. Dense. Terminal-appropriate.
The "Financial Brutalist" UI
Design System
The UI follows strict rules inspired by actual Bloomberg terminals:
/* No curves anywhere */
* { border-radius: 0 !important; }
/* Pitch black */
body { background: #000000; }
/* Monospace only */
font-family: 'JetBrains Mono', 'Courier New', monospace;
Color Palette
| Color | Hex | Usage |
|---|---|---|
| Background | #000000 |
Everything |
| Amber | #ff9900 |
Ticker symbols, highlights |
| Green | #00ff41 |
Positive changes, connected status |
| Red | #ff3333 |
Negative changes, alerts |
| Cyan | #00cccc |
Section headers, links |
| Muted | #666666 |
Labels, timestamps |
| Border | #333333 |
1px grid lines |
Key UI Components
1. ASCII Logo Header — A <pre> tag with the BL-BOMBERG block letters in amber, plus a live status bar showing update time, connection status, and latency.
2. ASCII Separators — Dashed lines (- - - -) between news articles, created with CSS border-top: 1px dashed #333. This is pure Bloomberg.
3. Flashing [NEW] Indicator — A CSS step-end blink animation on a <span>:
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
.animate-blink { animation: blink 1s step-end infinite; }
4. Sparkline Charts — Inline SVG <polyline> elements that generate a random-walk pattern biased by the stock's direction. Green for up, red for down.
5. Ticker Tape — A continuously scrolling <div> using CSS translateX(-50%) animation, duplicated 4x for seamless looping.
API Caching Strategy
To avoid burning through API credits during development, the API route implements a simple in-memory cache:
let cache: { data: any; timestamp: number } | null = null;
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
export async function GET() {
if (cache && (Date.now() - cache.timestamp) < CACHE_TTL_MS) {
return Response.json(cache.data); // Serve cached
}
// Fetch fresh data from AISA...
const payload = { market, news, trends, ai_brief };
cache = { data: payload, timestamp: Date.now() };
return Response.json(payload);
}
The frontend polls every 2 minutes, but the server only hits AISA every 5 minutes. This means you get 60% of your API calls for free from cache.
Mock Data Fallback
The AisaEngine includes comprehensive mock data generators. If the API key is missing or endpoints return errors, the UI still renders with realistic demo data:
async getMarketData() {
if (!API_KEY) return this.getMockMarketData();
try {
// ... real API calls
} catch (e) {
return this.getMockMarketData(); // Graceful fallback
}
}
This means:
- No API key? App still works with mock data
- API down? App degrades gracefully
- Demo mode? Just don't set the env var
Lessons Learned
1. Unified APIs are the future
Managing 5+ API providers is unsustainable for side projects. AISA's approach — one endpoint, one key, many capabilities - is how APIs should work.
2. OpenAI compatibility matters
AISA's chat endpoint accepts the standard OpenAI SDK. This means you can swap models (GPT-4o → Kimi K2.5) by changing one string, with zero code refactoring.
3. Brutalism is underrated
High information density with monospace fonts and sharp edges is not just an aesthetic choice — it's functional. Financial professionals prefer it because every pixel carries data.
4. Cache everything in dev
A 5-minute cache saves 60% of API calls during rapid development. Don't burn credits while debugging CSS.
Try It Yourself
git clone https://github.com/harishkotra/blbomberg.git
cd blbomberg
npm install
cp .env.example .env.local
# Add your AISA_API_KEY
npm run dev
Get your AISA API key at aisa.one.

Top comments (0)