I Built a "Today in History" Feature Using 3 Free APIs — Here's the Code
Last week I wanted to add a "Today in History" section to a side project — you know, that little widget showing what happened on this date, random space facts, and recent earthquakes. The kind of thing that makes a boring homepage feel alive.
The problem? Every tutorial I found used a single API. I wanted something richer. So I combined three free APIs that nobody talks about together, and the result was surprisingly good.
Here's exactly how I did it.
Why Three APIs?
Most "history on this day" pages are flat — just a list of events. I wanted:
- Historical events — what happened on this date
- Space facts — because who doesn't love random NASA trivia
- Recent earthquakes — real-time natural disaster data
Each of these serves a different purpose, and together they create a daily digest that's actually worth reading.
API 1: Historical Events
The on-this-day API returns events, births, deaths, and holidays for any date. No API key needed, no rate limit headaches.
async function getHistoryEvents() {
const today = new Date();
const month = today.getMonth() + 1;
const day = today.getDate();
const response = await fetch(
`https://api.quotalink.cn/api/v1/on-this-day?month=${month}&day=${day}`
);
const data = await response.json();
return data.events || [];
}
The response includes events, births, deaths, and holidays. I usually grab 3-5 events and 2-3 births to keep the digest concise.
API 2: Space Facts
This one's my favorite. The space-science API gives you random space facts, NASA's picture of the day, and ISS location data. Perfect for that "did you know?" section.
async function getSpaceFact() {
const response = await fetch(
'https://api.quotalink.cn/api/v1/space-science/fact'
);
const data = await response.json();
return data.fact || 'Space is really big.';
}
I also pull NASA's Astronomy Picture of the Day for a visual element:
async function getNasaImage() {
const NASA_KEY = 'YOUR_NASA_API_KEY'; // free at api.nasa.gov
const response = await fetch(
`https://api.nasa.gov/planetary/apod?api_key=${NASA_KEY}`
);
const data = await response.json();
return {
title: data.title,
url: data.url,
explanation: data.explanation
};
}
API 3: Earthquake Data
The earthquake API pulls recent seismic activity from USGS data. It's not something you'd check every day, but when there's a significant event, users appreciate seeing it.
async function getRecentEarthquakes() {
const response = await fetch(
'https://api.quotalink.cn/api/v1/earthquake/recent?limit=5&min_magnitude=4.0'
);
const data = await response.json();
return data.earthquakes || [];
}
Filtering by minimum magnitude (4.0+) keeps the list manageable — nobody needs to know about every tiny tremor.
Putting It All Together
Here's the combined function that builds the daily digest:
async function buildDailyDigest() {
const [events, spaceFact, earthquakes] = await Promise.all([
getHistoryEvents(),
getSpaceFact(),
getRecentEarthquakes()
]);
return {
date: new Date().toLocaleDateString('en-US', {
weekday: 'long', year: 'numeric', month: 'long', day: 'numeric'
}),
history: events.slice(0, 3),
space: spaceFact,
disasters: earthquakes.filter(eq => eq.magnitude >= 4.5)
};
}
// Usage
buildDailyDigest().then(digest => {
console.log(`📅 ${digest.date}`);
console.log('\n📜 On This Day:');
digest.history.forEach(e => console.log(` • ${e.year}: ${e.text}`));
console.log(`\n🚀 Space Fact: ${digest.space}`);
console.log('\n🌍 Recent Earthquakes:');
digest.disasters.forEach(eq =>
console.log(` • M${eq.magnitude} — ${eq.location}`)
);
});
Error Handling (The Part Nobody Talks About)
Free APIs go down. Here's how I handle it without breaking the whole digest:
async function safeFetch(url, fallback = null) {
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timeout);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return await response.json();
} catch (err) {
console.warn(`API call failed: ${url} — ${err.message}`);
return fallback;
}
}
I use this wrapper for every API call. If one service is down, the digest still renders with the other two.
Caching Strategy
You don't want to hit these APIs on every page load. I cache the digest for 6 hours:
const cache = new Map();
async function getCachedDigest() {
const cacheKey = `digest-${new Date().toISOString().slice(0, 10)}`;
if (cache.has(cacheKey)) {
return cache.get(cacheKey);
}
const digest = await buildDailyDigest();
cache.set(cacheKey, digest);
// Auto-expire after 6 hours
setTimeout(() => cache.delete(cacheKey), 6 * 60 * 60 * 1000);
return digest;
}
What I Learned
Three things surprised me:
Combining APIs creates more value than any single one. The historical events alone are boring. Add a space fact and recent earthquakes, and suddenly it's a daily ritual.
Free APIs have personality. The space facts API returns genuinely interesting tidbits. The earthquake data has a certain gravity (pun intended) that makes users check back.
Error handling is the real product. The difference between a hacky demo and something people actually use is how gracefully it handles API failures.
Try It Yourself
All three APIs I used are free and require no registration:
-
Historical events:
GET /api/v1/on-this-day?month=9&day=1 -
Space facts:
GET /api/v1/space-science/fact -
Earthquake data:
GET /api/v1/earthquake/recent?limit=5
You can test them directly in your browser or with curl. No API key, no sign-up, no credit card.
Next Steps
I'm planning to add:
- Weather overlay — show the weather for each historical event's location
- News aggregation — pull today's top headlines for context
- Random name generator — for the "People Born Today" section (instead of just famous people)
If you build something with these APIs, I'd love to see it. Drop a comment below or open an issue on the repo.
Found this useful? I write about API integration and side project engineering. Follow for more tutorials that actually work in production, not just in demos.
Top comments (0)