Daily Interactive Brokers Margin Rates Tracker: Building with Cloudflare Workers, D1, and AI
March 9, 2026
Interactive Brokers publishes margin rates on their website, but there's no API or historical data. I built an automated system that scrapes rates daily and visualizes them with interactive charts, running entirely on Cloudflare's edge platform.
Live Margin Rates Dashboard
Loading margin rates...
The Challenge
IBKR margin rates change frequently based on benchmark rates and vary across:
- 8+ currencies (USD, EUR, GBP, CAD, CHF, JPY, etc.)
- Multiple tiers based on loan amount
- Two account types (IBKR Pro vs IBKR Lite)
Without historical data, it's impossible to track trends or compare rates over time.
Architecture
The solution uses four Cloudflare services in a pipeline:
Cron Trigger (daily 9 AM UTC)
↓
Browser Rendering API (renders JavaScript page)
↓
Workers AI (extracts structured data from HTML)
↓
D1 Database (stores time-series data)
↓
Next.js SSR/ISR (serves interactive charts)
Implementation
1. Browser Rendering for JavaScript Pages
IBKR's margin rates page is JavaScript-heavy. A simple fetch() returns incomplete HTML. Cloudflare's Browser Rendering API runs a headless browser that executes JavaScript before returning the fully-rendered HTML.
2. AI-Powered HTML Parsing
Instead of brittle regex or DOM parsing, I use Workers AI with GLM-4.7-Flash to extract structured data:
const prompt = `Extract margin rate tiers from this HTML table.
Return JSON array with: currency, tierName, tierMin, tierMax, rate
Example: [{"currency":"USD","tierName":"100K-1M","tierMin":100000,"tierMax":1000000,"rate":4.64}]`;
const response = await env.AI.run("@cf/zai-org/glm-4.7-flash", {
messages: [
{ role: "system", content: "Extract structured data from HTML tables." },
{ role: "user", content: prompt + html },
],
max_tokens: 8192,
});
Why GLM-4.7-Flash?
- Fast inference with reasoning capabilities
- Handles complex HTML structure variations
- Returns both data and step-by-step reasoning
Before sending to AI, I extract only the relevant table section (12KB) from the full HTML (220KB), reducing token costs by around 95%.
3. Storage in D1 Database
The extracted data is stored in Cloudflare D1 (SQLite) with this schema:
CREATE TABLE margin_rates
(
date TEXT NOT NULL,
currency TEXT NOT NULL,
account_type TEXT NOT NULL,
tier_name TEXT NOT NULL,
tier_min REAL,
tier_max REAL,
rate REAL NOT NULL,
benchmark_margin REAL
);
CREATE INDEX idx_date_currency ON margin_rates (date, currency);
4. Automated Daily Updates
Cloudflare Workers cron triggers run the scraper daily:
export default {
async scheduled(event: ScheduledEvent, env: Env) {
const rates = await fetchMarginRates(
env.AI,
env.CLOUDFLARE_ACCOUNT_ID,
env.CLOUDFLARE_API_TOKEN,
);
await storeRates(env.FINANCE, new Date(), rates);
},
};
5. Interactive Visualization
The Next.js page is generated on the first request on Cloudflare and then revalidated every 12 hours. The chart component uses Recharts with three view modes:
- Current rates comparison across currencies
- Historical trends (90 days)
- Pro vs Lite account comparison