In today's fast-paced digital world, staying updated with the latest news is more important than ever. Whether you are building a personal dashboard, a niche curation site, or a feature for your SaaS, a news aggregator is an incredibly useful tool.
However, if you have ever tried to build one using mainstream search APIs like Google Search API or SerpAPI, you have probably run into a major roadblock: cost. These services can easily cost hundreds of dollars a month, which is a massive barrier for indie hackers, hobbyists, and developers working on side projects.
Fortunately, there is a highly affordable alternative. In this tutorial, we will build a fully functional, real-time news aggregator using the Search API (available at api.joffstrends.co.uk for just £9.99/month). This API provides fast, reliable search results without breaking the bank.
By the end of this guide, you will have a clean, responsive web application that fetches live news headlines, filters them by topic, and displays them beautifully.
Prerequisites
To follow along with this tutorial, you will need:
- A basic understanding of HTML, CSS, and modern JavaScript (ES6+).
- A text editor (like VS Code).
- Access to the Search API at
https://api.joffstrends.co.uk.
Step 1: Setting Up the HTML Structure
First, let's create our basic HTML skeleton. We need a header, a navigation bar with topic filters, and a container where our news articles will be dynamically injected.
Create an index.html file and add the following code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IndieNews - Cheap & Fast News Aggregator</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1>IndieNews</h1>
<p>Real-time news powered by a budget-friendly Search API</p>
</header>
<nav class="filters">
<button class="filter-btn active" data-topic="latest news">All News</button>
<button class="filter-btn" data-topic="technology trends">Technology</button>
<button class="filter-btn" data-topic="finance business">Finance</button>
<button class="filter-btn" data-topic="sports news">Sports</button>
</nav>
<main>
<div id="loading" class="loading">Fetching the latest headlines...</div>
<div id="news-container" class="news-grid"></div>
</main>
<footer>
<p>Powered by <a href="https://api.joffstrends.co.uk" target="_blank">JoffsTrends Search API</a></p>
</footer>
<script src="app.js"></script>
</body>
</html>
Step 2: Styling the Aggregator
Next, let's add some clean, modern CSS to make our aggregator look professional. Create a style.css file:
:root {
--primary-color: #2563eb;
--background-color: #f8fafc;
--card-background: #ffffff;
--text-color: #1e293b;
--text-muted: #64748b;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background-color: var(--background-color);
color: var(--text-color);
margin: 0;
padding: 0;
line-height: 1.5;
}
header {
text-align: center;
padding: 2rem 1rem;
background: linear-gradient(135deg, #1e3a8a, #2563eb);
color: white;
}
header h1 {
margin: 0;
font-size: 2.5rem;
}
.filters {
display: flex;
justify-content: center;
gap: 1rem;
padding: 1rem;
background: white;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.filter-btn {
background: none;
border: 1px solid #cbd5e1;
padding: 0.5rem 1rem;
border-radius: 20px;
cursor: pointer;
font-weight: 500;
transition: all 0.2s;
}
.filter-btn:hover, .filter-btn.active {
background-color: var(--primary-color);
color: white;
border-color: var(--primary-color);
}
main {
max-width: 1200px;
margin: 2rem auto;
padding: 0 1rem;
}
.news-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 2rem;
}
.news-card {
background: var(--card-background);
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
transition: transform 0.2s;
}
.news-card:hover {
transform: translateY(-4px);
}
.news-content {
padding: 1.5rem;
}
.news-title {
font-size: 1.25rem;
margin-top: 0;
margin-bottom: 0.5rem;
}
.news-title a {
color: var(--text-color);
text-decoration: none;
}
.news-title a:hover {
color: var(--primary-color);
}
.news-snippet {
color: var(--text-muted);
font-size: 0.95rem;
margin-bottom: 1rem;
}
.news-meta {
font-size: 0.8rem;
color: var(--text-muted);
}
.loading {
text-align: center;
font-size: 1.2rem;
color: var(--text-muted);
padding: 3rem;
}
Step 3: Fetching Data from the Search API
Now, let's write the core logic in app.js. We will fetch search results from the Search API endpoint. The API returns a list of search results, which we will parse and display as news articles.
Create app.js and add the following code:
const API_URL = 'https://api.joffstrends.co.uk/search';
const newsContainer = document.getElementById('news-container');
const loadingElement = document.getElementById('loading');
const filterButtons = document.querySelectorAll('.filter-btn');
// Fetch news from the budget-friendly Search API
async function fetchNews(query) {
showLoading(true);
try {
// We pass the query parameter to get relevant news articles
const response = await fetch(`${API_URL}?q=${encodeURIComponent(query)}`);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
displayNews(data.results || []);
} catch (error) {
console.error('Error fetching news:', error);
newsContainer.innerHTML = `<p class="error">Failed to load news. Please try again later.</p>`;
} finally {
showLoading(false);
}
}
// Render the news cards dynamically
function displayNews(articles) {
newsContainer.innerHTML = '';
if (articles.length === 0) {
newsContainer.innerHTML = '<p>No news found for this topic.</p>';
return;
}
articles.forEach(article => {
const card = document.createElement('article');
card.className = 'news-card';
// Extracting standard search result fields
const title = article.title || 'No Title Available';
const link = article.link || '#';
const snippet = article.snippet || 'No description available for this article.';
const source = article.source || 'Web';
card.innerHTML = `
<div class="news-content">
<h3 class="news-title">
<a href="${link}" target="_blank" rel="noopener noreferrer">${title}</a>
</h3>
<p class="news-snippet">${snippet}</p>
<div class="news-meta">
<span>Source: ${source}</span>
</div>
</div>
`;
newsContainer.appendChild(card);
});
}
function showLoading(isLoading) {
if (isLoading) {
loadingElement.style.display = 'block';
newsContainer.style.display = 'none';
} else {
loadingElement.style.display = 'none';
newsContainer.style.display = 'grid';
}
}
// Setup event listeners for filtering by topic
filterButtons.forEach(button => {
button.addEventListener('click', (e) => {
// Remove active class from all buttons
filterButtons.forEach(btn => btn.classList.remove('active'));
// Add active class to clicked button
e.target.classList.add('active');
// Fetch news for the selected topic
const topic = e.target.getAttribute('data-topic');
fetchNews(topic);
});
});
// Initial fetch on page load
document.addEventListener('DOMContentLoaded', () => {
fetchNews('latest news');
});
Why This Search API is a Game Changer
When building aggregators, standard news APIs often restrict you to specific pre-approved news sources or charge exorbitant rates for real-time web searches.
By using the JoffsTrends Search API, you get:
- Unbeatable Pricing: At just £9.99/month, it is a fraction of the cost of SerpAPI or Google Custom Search.
- Flexibility: You can query any topic, keyword, or niche, and get up-to-date web results instantly.
- Simplicity: A straightforward JSON response that is easy to integrate into any frontend or backend stack.
Next Steps to Enhance Your Aggregator
Now that you have a working prototype, here are a few ideas to take it to the next level:
- Add Search Functionality: Add a search bar so users can input custom queries.
-
Implement Caching: Cache results in
localStorageor on a simple backend to reduce API calls and improve load times. - Bookmark Feature: Allow users to save articles to read later using browser storage.
Building projects doesn't have to be expensive. With the right budget-friendly tools, you can build powerful, production-ready applications without the high overhead. Happy coding!
Top comments (0)