Building a Web Search + AI Summary Tool
How to combine OpenAI and SERP API to create a powerful web search and AI summary tool for stock analysis and research
4th May 2025
Introduction
In today's fast-paced financial markets, analysts need to process vast amounts of information quickly to make informed decisions. Traditional methods of manually searching the web, reading articles, and synthesizing information are time-consuming and prone to missing critical insights. This is where combining AI with web search capabilities can create a powerful tool for stock analysis.
In this blog post, I'll share how I built a web search + AI summary tool for a company's experimental stock analysis project. This tool helped analysts quickly gather and synthesize information about stocks they were researching, saving hours of manual work and providing more comprehensive insights.
The Power of Combining Web Search with AI
Before diving into the implementation, let's understand why this combination is particularly powerful:
- Real-time information access: SERP (Search Engine Results Page) APIs provide access to the latest information from across the web
- Contextual understanding: Large language models like GPT-4 can understand the context and relevance of information
- Synthesis capabilities: AI can summarize, extract key points, and identify trends across multiple sources
- Customizable analysis: The system can be tailored to focus on specific aspects of stock analysis (financials, news sentiment, market trends)
For stock analysis specifically, this combination allows analysts to:
- Quickly gather the latest news and developments about a company
- Analyze market sentiment across multiple sources
- Identify potential risks or opportunities that might be buried in various articles
- Generate comprehensive research summaries in seconds rather than hours
Implementation: Building the Tool
Let's walk through how to build this tool step by step.
1. Setting Up the Environment
First, we need to set up our environment with the necessary dependencies:
// Install required packages
npm install axios openai dotenv
// Create a .env file for API keys
OPENAI_API_KEY=your_openai_api_key
SERP_API_KEY=your_serp_api_key
2. Configuring the SERP API
We'll use a SERP API to fetch search results. There are several providers available, but for this project, I used SerpAPI which provides structured data from search engines:
const axios = require('axios')
require('dotenv').config()
async function searchWeb(query, numResults = 5) {
try {
const response = await axios.get('https://serpapi.com/search', {
params: {
q: query,
api_key: process.env.SERP_API_KEY,
num: numResults,
},
})
// Extract the organic search results
const searchResults = response.data.organic_results.map((result) => ({
title: result.title,
link: result.link,
snippet: result.snippet,
}))
return searchResults
} catch (error) {
console.error('Error searching the web:', error)
throw error
}
}
3. Fetching Content from Search Results
Once we have the search results, we need to fetch the actual content from the web pages:
const axios = require('axios')
const cheerio = require('cheerio')
async function fetchContent(url) {
try {
const response = await axios.get(url)
const $ = cheerio.load(response.data)
// Remove script tags, style tags, and other non-content elements
$('script, style, meta, link').remove()
// Extract the main content (this is a simplified approach)
// For production, you might want to use more sophisticated content extraction
const content = $('body').text().replace(/\s+/g, ' ').trim()
return content
} catch (error) {
console.error(`Error fetching content from ${url}:`, error)
return '' // Return empty string if content can't be fetched
}
}
async function fetchContentsFromSearchResults(searchResults) {
const contents = []
for (const result of searchResults) {
const content = await fetchContent(result.link)
if (content) {
contents.push({
title: result.title,
url: result.link,
content: content.substring(0, 8000), // Limit content length
})
}
}
return contents
}
4. Integrating with OpenAI API
Now, we'll use OpenAI's API to summarize and analyze the content:
const { OpenAI } = require('openai')
require('dotenv').config()
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
})
async function summarizeWithAI(stockSymbol, contents) {
// Prepare the content for the AI
const contentText = contents
.map((item) => `Source: ${item.title} (${item.url})\n${item.content}\n\n`)
.join('')
// Create the prompt for the AI
const prompt = `
You are a financial analyst assistant. Below are web search results about the stock ${stockSymbol}.
Please analyze these results and provide:
1. A summary of key recent developments
2. Analysis of market sentiment (positive, negative, neutral)
3. Potential impact on stock price
4. Key financial metrics mentioned
5. Any risks or opportunities identified
Make your analysis concise, factual, and focused on information that would be relevant to investors.
Search Results:
${contentText}
`
try {
const response = await openai.chat.completions.create({
model: 'gpt-4',
messages: [
{
role: 'system',
content:
'You are a financial analyst assistant that helps analyze stock information from web search results.',
},
{ role: 'user', content: prompt },
],
temperature: 0.2, // Lower temperature for more factual responses
max_tokens: 1500,
})
return response.choices[0].message.content
} catch (error) {
console.error('Error generating AI summary:', error)
throw error
}
}
5. Putting It All Together
Finally, let's create the main function that ties everything together:
async function analyzeStock(stockSymbol) {
try {
console.log(`Analyzing stock: ${stockSymbol}...`)
// Step 1: Search for recent information about the stock
const searchQuery = `${stockSymbol} stock news financial analysis recent developments`
const searchResults = await searchWeb(searchQuery, 8)
// Step 2: Fetch content from search results
const contents = await fetchContentsFromSearchResults(searchResults)
// Step 3: Generate AI summary and analysis
const analysis = await summarizeWithAI(stockSymbol, contents)
return {
stockSymbol,
searchResults,
analysis,
}
} catch (error) {
console.error(`Error analyzing stock ${stockSymbol}:`, error)
throw error
}
}
// Example usage
analyzeStock('AAPL')
.then((result) => {
console.log('Analysis complete:')
console.log(result.analysis)
})
.catch((error) => {
console.error('Analysis failed:', error)
})
Real-World Application: Stock Analysis Dashboard
For our company's experimental project, we integrated this functionality into a dashboard that allowed analysts to:
- Input multiple stock symbols for analysis
- Customize the search parameters (time range, focus areas)
- Compare AI-generated summaries side by side
- Save and track analyses over time to identify trends
The dashboard looked something like this:
// React component example (simplified)
function StockAnalysisDashboard() {
const [stocks, setStocks] = useState([])
const [loading, setLoading] = useState({})
const [analyses, setAnalyses] = useState({})
const addStock = (symbol) => {
if (!stocks.includes(symbol)) {
setStocks([...stocks, symbol])
analyzeStockAndUpdateState(symbol)
}
}
const analyzeStockAndUpdateState = async (symbol) => {
setLoading((prev) => ({ ...prev, [symbol]: true }))
try {
const result = await analyzeStock(symbol)
setAnalyses((prev) => ({ ...prev, [symbol]: result }))
} catch (error) {
console.error(`Error analyzing ${symbol}:`, error)
} finally {
setLoading((prev) => ({ ...prev, [symbol]: false }))
}
}
return (
<div className="dashboard">
<h1>Stock Analysis Dashboard</h1>
<div className="stock-input">
<input
type="text"
placeholder="Enter stock symbol (e.g., AAPL)"
onKeyPress={(e) => e.key === 'Enter' && addStock(e.target.value)}
/>
</div>
<div className="stock-analyses">
{stocks.map((symbol) => (
<div key={symbol} className="stock-card">
<h2>{symbol}</h2>
{loading[symbol] ? (
<p>Loading analysis...</p>
) : analyses[symbol] ? (
<div>
<h3>AI Analysis</h3>
<div className="analysis-content">{analyses[symbol].analysis}</div>
<h3>Sources</h3>
<ul>
{analyses[symbol].searchResults.map((result, i) => (
<li key={i}>
<a href={result.link} target="_blank" rel="noopener noreferrer">
{result.title}
</a>
</li>
))}
</ul>
</div>
) : (
<p>No analysis available</p>
)}
</div>
))}
</div>
</div>
)
}
Challenges and Considerations
While building this tool, we encountered several challenges worth noting:
1. API Rate Limits and Costs
Both SERP APIs and OpenAI's API have rate limits and usage costs. For a production system, you'll need to:
- Implement caching to avoid redundant searches
- Set up usage monitoring and alerts
- Consider batch processing for multiple stocks
2. Content Extraction Quality
Extracting meaningful content from web pages can be challenging due to:
- Paywalls on financial news sites
- Dynamic content loaded via JavaScript
- Varied page structures across different sites
We improved our content extraction by:
- Using more sophisticated libraries like mozilla/readability
- Implementing site-specific extractors for common financial news sources
- Falling back to meta descriptions when full content wasn't available
3. Ensuring Analysis Quality
To improve the quality of AI-generated analyses:
- We fine-tuned prompts based on feedback from financial analysts
- Implemented fact-checking by cross-referencing key claims
- Added source attribution to make it clear where information came from
Conclusion
Combining web search capabilities with AI summarization creates a powerful tool for stock analysis that can save hours of research time and provide more comprehensive insights. The implementation we've outlined here is just a starting point—there are many ways to extend and improve this system.
Some potential extensions include:
- Adding sentiment analysis specifically tuned for financial news
- Incorporating historical stock price data for correlation analysis
- Expanding to include social media sentiment from platforms like Twitter/X
- Creating alerts for significant news that might impact stock prices
As AI capabilities continue to advance, tools like this will become increasingly sophisticated and valuable for financial analysis and decision-making.
Have you built similar tools or have ideas for improvements? I'd love to hear about your experiences in the comments!