An AI agent that researches competitors and markets using SerpApi (web, news, jobs) and OpenAI, then produces concise briefings with citations. Optional HubSpot integration adds internal CRM context—companies you track, contacts, and activity history—so briefings combine external intel with what you already know.
Part of a series on agentic workflows with SerpApi (e.g. Sales Assistant Agent); aimed at developers building AI agents that need reliable, real-time search.
git clone <repository-url>
cd competitive-intelligence-agent
uv syncRequirements: Python 3.10+, OpenAI API key, SerpAPI key. Optional: HubSpot Private App token for internal CRM context.
- SerpAPI: serpapi.com — sign up and get an API key.
- OpenAI: platform.openai.com — API key with access to chat completions (e.g. gpt-4o).
To combine external research with internal CRM data (companies, contacts, notes, calls, emails):
- In HubSpot: Settings → Integrations → Private Apps (or Developer Portal → Private Apps).
- Create a private app with these scopes:
crm.objects.contacts.readcrm.objects.companies.readcrm.objects.notes.read- (If you use engagements: ensure read access to emails and calls as required by your HubSpot plan.)
- Copy the Access token and set
HUBSPOT_ACCESS_TOKENin.env.
If HUBSPOT_ACCESS_TOKEN is not set, the agent runs with SerpApi only (web, news, jobs)—no HubSpot tools are registered.
cp .env.example .env
# Edit .env:
OPENAI_API_KEY=your_openai_key
SERPAPI_API_KEY=your_serpapi_key
# Optional — internal context
HUBSPOT_ACCESS_TOKEN=your_hubspot_tokenOptional: OPENAI_MODEL, RESULT_LIMIT, REPORT_DIR, DEBUG.
uv run competitive_intel_agent.pyuv run competitive_intel_agent.py -q "Brief me on Stripe and any recent pricing or positioning changes"With HubSpot configured, you can ask for internal context in the same query:
uv run competitive_intel_agent.py -q "Brief me on acme.com and what we have on them in HubSpot"uv run competitive_intel_agent.py -q "What's new with Acme Corp this month?" -o report.jsonuv run competitive_intel_agent.py -m gpt-4o-mini # Smaller model
uv run competitive_intel_agent.py -n 5 # Fewer results per search
uv run competitive_intel_agent.py -d # Debug loggingExternal research only (SerpApi):
- Brief me on [Competitor] and any pricing or positioning changes.
- What's new with [Company] this month?
- Research [Company] and summarize their positioning and recent news.
- What jobs is [Company] hiring for? What does that suggest about their strategy?
With HubSpot (internal + external):
- Brief me on Acme Corp and what we already have on them in HubSpot (contacts, notes, activity).
- Research Competitor X, then check if we have them in HubSpot and summarize our internal context plus recent news.
- What do we know about example.com in our CRM? Combine that with a short web/news briefing on them.
- Plan: The LLM decides which tools to use (web, news, jobs; if configured, HubSpot company/contact/activity).
- Execute: SerpApi tools return search results; HubSpot tools return company, contact, and activity data from your CRM.
- Synthesize: The model combines external and (when available) internal context into a short briefing with key findings and numbered citations [1], [2], …
Example with HubSpot: "Brief me on Acme and what we have in HubSpot" → agent may call hubspot_search_company_by_domain("acme.com"), hubspot_get_contact_by_email(...), search_news("Acme") → produces one briefing that merges CRM context with recent news.
| Tool | Purpose |
|---|---|
| SerpApi | |
search_web |
Google organic — company info, positioning, reviews, pricing. |
search_news |
Google News — funding, launches, exec moves (with date range). |
search_jobs |
Google Jobs — hiring signals, growth, skills focus. |
| HubSpot (optional) | |
hubspot_search_company_by_domain |
Check if we have this company in HubSpot; return id, name, domain. |
hubspot_get_contact_by_email |
Get contact details by email for internal context. |
hubspot_get_contact_activity_history |
Get our notes, calls, and emails with this contact. |
- CLI: Human-readable briefing in the terminal.
- JSON: Use
-o report.jsonto save the full conversation (messages + tool results). You can parse the final assistant message or build a separate report schema on top.
- Missing environment variables — Ensure
OPENAI_API_KEYandSERPAPI_API_KEYare set in.env(no spaces around=).HUBSPOT_ACCESS_TOKENis optional; without it the agent runs with SerpApi only. - SerpApi errors — Check key and quota; reduce
RESULT_LIMITif you hit rate limits. - OpenAI errors — Verify key and model (e.g.
gpt-4o,gpt-4o-mini). - HubSpot: company or contact not found — Confirm the company/contact exists in HubSpot and that your Private App has the required scopes (
crm.objects.companies.read,crm.objects.contacts.read, etc.). - HubSpot: 403 or scope errors — Re-create the Private App and ensure all read scopes for companies, contacts, and notes (and engagements if used) are granted.
- Debug — Run with
-dfor detailed logs.
uv run pytestThis repo accompanies a blog post on building a Competitive Intelligence Agent with SerpApi and LLMs. For more agentic workflows (e.g. sales assistant with CRM), see SerpApi for developers.