If you've ever searched "find solar installer near me," you've probably landed on EnergySage. It's the dominant solar marketplace in the US, connecting homeowners with vetted installers, generating competitive quotes, and pulling in millions of data points weekly. The US Department of Energy literally funded its development. And if you're reading this, you probably want to build something like it.

I've spent the last few years building directory-style marketplaces and geo-based service platforms for clients in energy, home services, and B2B verticals. The architecture behind a solar installer directory isn't rocket science, but there are a surprising number of decisions that can make or break the project. Let me walk you through the whole thing -- from data modeling to search infrastructure to the SEO strategy that actually drives organic traffic.

Table of Contents

Understanding the Solar Marketplace Model

Before writing a single line of code, you need to understand what makes platforms like EnergySage and solar.com work as businesses. They're two-sided marketplaces with a specific revenue model:

  1. Homeowners enter their address and utility information to request solar quotes
  2. Installers pay to access these leads and compete on price and quality
  3. The platform earns revenue through installer subscriptions, per-lead fees, or a cut of closed deals

EnergySage reports that their marketplace saves homeowners roughly $0.20–$0.40 per watt compared to going direct -- that's $1,000–$2,000 on a typical 5 kW residential system. That savings comes from competition. When multiple installers bid on the same project, prices drop. That's the core value proposition you need to replicate.

The solar marketplace in 2025 is a $30+ billion industry in the US alone, with residential installations growing at roughly 15-20% year over year. The Inflation Reduction Act's 30% federal tax credit runs through 2032, so this market isn't slowing down.

Here's what the major players look like:

Platform Model Revenue Source Installer Network
EnergySage Quote marketplace Lead fees + data licensing 500+ vetted installers
Solar.com Guided marketplace Preferred pricing commissions Curated network
Enphase Installer Locator Equipment-tied directory Hardware sales Certified Enphase installers
SolarReviews Review directory Lead generation fees 500+ listed companies
Yelp/Google General directory Advertising Unvetted listings

Your directory doesn't need to compete with EnergySage head-on. Niche opportunities exist: state-specific directories, commercial-only marketplaces, battery storage specialists, or directories focused on specific equipment brands.

Core Architecture Decisions

The first real decision is whether you're building a static directory, a dynamic marketplace, or something in between. Each has radically different complexity.

Static Directory vs. Dynamic Marketplace

A static directory lists installers with contact info, reviews, and service areas. Think Yellow Pages for solar. Low complexity, fast to build, monetized through advertising or featured listings.

A dynamic marketplace handles the entire quote-to-close workflow: lead capture, installer matching, quote generation, comparison tools, and sometimes even financing. This is what EnergySage does. High complexity, but much higher revenue potential.

Most teams should start with a directory and layer marketplace features on top. Here's why: the directory pages generate organic traffic (think thousands of "solar installers in [city]" pages), and that traffic feeds the marketplace funnel.

Headless Architecture

For a project like this, I strongly recommend a headless architecture. Here's the setup I'd use:

  • Frontend: Next.js or Astro for the public-facing site
  • CMS: A headless CMS (Sanity, Contentful, or Payload) for installer profiles and editorial content
  • API Layer: Custom Node.js/Express or tRPC API for search, quotes, and transactional logic
  • Database: PostgreSQL with PostGIS for geospatial queries
  • Search: Algolia or Meilisearch for instant installer search

We've built similar architectures for clients using our Next.js development and headless CMS capabilities. The headless approach lets you independently scale the content layer (thousands of location pages) from the application layer (quote engine, user dashboards).

Monorepo Structure

I'd organize this as a monorepo using Turborepo:

├── apps/
│   ├── web/              # Next.js public site
│   ├── installer-portal/  # Installer dashboard (Next.js)
│   └── admin/            # Internal admin (React)
├── packages/
│   ├── database/         # Prisma schema + migrations
│   ├── api/              # Shared API routes/tRPC
│   ├── ui/               # Shared component library
│   └── geo/              # Geospatial utilities
└── turbo.json

This keeps things organized as the project grows. The geo package is worth calling out -- you'll reuse geospatial logic (distance calculations, service area matching, geocoding) across multiple apps.

Database Design for Installer Directories

This is where most directory projects either succeed or create headaches that last for years. Let me share a schema that's worked well.

Core Entities

-- Installer companies
CREATE TABLE installers (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name VARCHAR(255) NOT NULL,
  slug VARCHAR(255) UNIQUE NOT NULL,
  description TEXT,
  website_url VARCHAR(500),
  phone VARCHAR(20),
  email VARCHAR(255),
  logo_url VARCHAR(500),
  founded_year INTEGER,
  employee_count_range VARCHAR(50),
  license_number VARCHAR(100),
  is_verified BOOLEAN DEFAULT FALSE,
  verification_date TIMESTAMP,
  avg_rating DECIMAL(2,1) DEFAULT 0,
  total_reviews INTEGER DEFAULT 0,
  total_installations INTEGER DEFAULT 0,
  status VARCHAR(20) DEFAULT 'pending',
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

-- Service areas (using PostGIS for geo queries)
CREATE TABLE service_areas (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  installer_id UUID REFERENCES installers(id),
  area_name VARCHAR(255),
  geometry GEOMETRY(POLYGON, 4326),
  radius_miles INTEGER,
  center_point GEOMETRY(POINT, 4326),
  is_primary BOOLEAN DEFAULT FALSE
);

-- Certifications and credentials  
CREATE TABLE installer_certifications (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  installer_id UUID REFERENCES installers(id),
  certification_type VARCHAR(100), -- 'NABCEP', 'Tesla Powerwall', 'Enphase', etc.
  certification_number VARCHAR(100),
  issued_date DATE,
  expiry_date DATE,
  verified BOOLEAN DEFAULT FALSE
);

-- Equipment/brands they install
CREATE TABLE installer_equipment (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  installer_id UUID REFERENCES installers(id),
  equipment_type VARCHAR(50), -- 'panel', 'inverter', 'battery'
  brand VARCHAR(100),
  model VARCHAR(255),
  is_preferred BOOLEAN DEFAULT FALSE
);

-- Location pages (for SEO)
CREATE TABLE locations (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  state VARCHAR(2) NOT NULL,
  city VARCHAR(255) NOT NULL,
  county VARCHAR(255),
  slug VARCHAR(255) UNIQUE NOT NULL,
  zip_codes TEXT[], -- Array of covered zip codes
  center_point GEOMETRY(POINT, 4326),
  avg_system_cost DECIMAL(10,2),
  avg_electricity_rate DECIMAL(5,4),
  avg_sun_hours DECIMAL(4,2),
  state_incentives JSONB,
  installer_count INTEGER DEFAULT 0,
  meta_title VARCHAR(70),
  meta_description VARCHAR(160),
  content TEXT, -- Markdown editorial content
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

The Service Area Problem

This is the tricky part. Installers don't serve neat circular areas around a single point. They serve oddly shaped regions -- maybe three counties in one state, plus a few zip codes across the border. You have three options:

  1. Radius-based: Installer sets a center point and max distance. Simple but inaccurate.
  2. Zip code list: Installer selects specific zip codes. Accurate but tedious for large areas.
  3. Polygon-based: Draw actual service area boundaries on a map. Most accurate, best UX, but requires PostGIS and a map drawing UI.

I'd implement option 3 with option 1 as a fallback. During onboarding, let installers either draw their service area on a Mapbox/Google Maps interface or just enter a radius. Use PostGIS's ST_Contains and ST_DWithin functions for matching:

-- Find installers serving a specific point (homeowner's address)
SELECT i.* FROM installers i
JOIN service_areas sa ON sa.installer_id = i.id
WHERE ST_Contains(sa.geometry, ST_SetSRID(ST_MakePoint(-71.4128, 41.8240), 4326))
  AND i.status = 'active'
ORDER BY i.avg_rating DESC;

Geo-Based Search and Location Services

The "find solar installer near me" query is everything. It's the primary user intent, the main SEO target, and the core UX flow. You need to get this right.

Geocoding Pipeline

When a homeowner enters their address or zip code, here's the flow:

  1. Client-side: Autocomplete with Google Places API or Mapbox Geocoding
  2. Geocode: Convert address to lat/lng coordinates
  3. Query: Find installers whose service areas contain that point
  4. Enrich: Pull in local utility data, state incentives, average solar irradiance
  5. Rank: Sort by relevance (rating, distance, response time, verification status)
// Example: Next.js API route for installer search
import { db } from '@/packages/database';
import { geocode } from '@/packages/geo';

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const zipCode = searchParams.get('zip');
  const lat = searchParams.get('lat');
  const lng = searchParams.get('lng');

  let coordinates = { lat: Number(lat), lng: Number(lng) };

  if (zipCode && (!lat || !lng)) {
    coordinates = await geocode(zipCode);
  }

  const installers = await db.$queryRaw`
    SELECT 
      i.*,
      ST_Distance(
        sa.center_point::geography,
        ST_SetSRID(ST_MakePoint(${coordinates.lng}, ${coordinates.lat}), 4326)::geography
      ) / 1609.34 as distance_miles
    FROM installers i
    JOIN service_areas sa ON sa.installer_id = i.id
    WHERE (
      ST_Contains(sa.geometry, ST_SetSRID(ST_MakePoint(${coordinates.lng}, ${coordinates.lat}), 4326))
      OR ST_DWithin(
        sa.center_point::geography,
        ST_SetSRID(ST_MakePoint(${coordinates.lng}, ${coordinates.lat}), 4326)::geography,
        sa.radius_miles * 1609.34
      )
    )
    AND i.status = 'active'
    ORDER BY i.is_verified DESC, i.avg_rating DESC, distance_miles ASC
    LIMIT 20
  `;

  return Response.json({ installers, coordinates });
}

Search Infrastructure

For instant search (filtering by name, certification, equipment brand), PostgreSQL alone will get slow once you pass a few thousand installers. Layer Algolia or Meilisearch on top:

Feature Algolia Meilisearch PostgreSQL Full-Text
Latency <50ms <50ms 100-500ms
Geo filtering Built-in Built-in Requires PostGIS
Faceted search Excellent Good Manual
Typo tolerance Yes Yes No
Pricing (2025) $1/1K requests Free (self-hosted) Free
Setup complexity Low Medium Low

Algolia at $1 per 1,000 search requests is reasonable for most directories. If budget is tight, self-host Meilisearch on a $20/month VPS.

Building the Quote Request System

This is what separates a directory from a marketplace. The quote system is your revenue engine.

Quote Request Flow

  1. Homeowner enters address → auto-detect utility provider and rate
  2. Roof assessment (satellite imagery from Google Solar API or Aurora Solar)
  3. System size recommendation based on usage and roof space
  4. Match with 3-8 qualified installers in the area
  5. Installers submit competing quotes within 48 hours
  6. Homeowner compares quotes on a standardized dashboard

The Google Solar API (launched 2023, now widely available) is a massive shortcut here. It provides solar potential data for 320+ million buildings in the US, including:

  • Roof segment analysis
  • Annual sunshine hours
  • Estimated energy production
  • Optimal panel placement
// Google Solar API - Building Insights
const response = await fetch(
  `https://solar.googleapis.com/v1/buildingInsights:findClosest?` +
  `location.latitude=${lat}&location.longitude=${lng}` +
  `&requiredQuality=HIGH&key=${GOOGLE_SOLAR_API_KEY}`
);

const data = await response.json();
// data.solarPotential.maxArrayPanelsCount
// data.solarPotential.maxSunshineHoursPerYear
// data.solarPotential.financialAnalyses

This eliminates the need for manual site surveys in the initial quoting phase, dramatically reducing time-to-quote.

Quote Data Model

Each quote needs to capture enough detail for meaningful comparison:

interface SolarQuote {
  id: string;
  requestId: string;
  installerId: string;
  systemSizeKw: number;
  panelBrand: string;
  panelModel: string;
  panelCount: number;
  inverterBrand: string;
  inverterType: 'string' | 'micro' | 'hybrid';
  batteryIncluded: boolean;
  batteryBrand?: string;
  batteryCapacityKwh?: number;
  grossCost: number;
  federalTaxCredit: number;
  stateIncentives: number;
  utilityRebates: number;
  netCost: number;
  estimatedAnnualProduction: number;
  warrantyYears: number;
  estimatedPaybackYears: number;
  financingOptions: FinancingOption[];
  validUntil: Date;
}

SEO Architecture for Local Solar Pages

This is where most solar directories either win or lose. EnergySage generates massive organic traffic from programmatic location pages -- they have pages for every city in every state, structured as /local-data/solar-companies/{state}/{county}/{city}/.

URL Structure

Here's the URL hierarchy I'd recommend:

/solar-installers/                        # National hub
/solar-installers/rhode-island/           # State page
/solar-installers/rhode-island/providence/ # City page
/installer/blue-sky-solar/                # Individual installer profile
/solar-costs/rhode-island/                # Cost data by state

Programmatic Page Generation

With Next.js App Router, you can generate thousands of location pages at build time:

// app/solar-installers/[state]/[city]/page.tsx
export async function generateStaticParams() {
  const locations = await db.location.findMany({
    select: { state: true, slug: true }
  });
  
  return locations.map((loc) => ({
    state: loc.state.toLowerCase().replace(/\s+/g, '-'),
    city: loc.slug,
  }));
}

export async function generateMetadata({ params }: Props) {
  const location = await getLocation(params.state, params.city);
  return {
    title: `Best Solar Installers in ${location.city}, ${location.state} (2025)`,
    description: `Compare ${location.installerCount} vetted solar companies in ${location.city}. Average system cost: $${location.avgSystemCost}. Get free quotes today.`,
  };
}

For sites with 10,000+ location pages, consider Astro instead of Next.js. Astro's static generation is significantly faster for content-heavy sites -- we've seen build times drop from 45 minutes to under 5 minutes for large programmatic page sets.

Content Strategy for Location Pages

Don't just generate empty template pages. Each location page needs unique, useful content:

  • Number of active installers in the area
  • Average solar system cost for that city (pull from your quote data)
  • Local utility rates and net metering policies
  • State and local incentives (these vary wildly)
  • Average solar irradiance / sun hours
  • Top-rated installers with real reviews
  • Comparison table of recent quotes

EnergySage's Providence, RI page, for example, lists 8 marketplace installers with details on experience, certifications, and equipment preferences. That's the depth you're competing against.

Installer Verification and Trust Signals

Trust is the whole game. EnergySage verifies that installers have 2-3 years of experience and multiple successful installations before they can join. Solar.com talks about "weeding out the riff-raff." You need a verification pipeline.

Verification Checklist

  • Business license: Verify state contractor license via API (many states have public lookup APIs)
  • Insurance: Require proof of general liability ($1M+) and workers' comp
  • NABCEP certification: The gold standard for solar installers -- verify against NABCEP's database
  • Years in business: Cross-reference with state business registrations
  • Installation history: Request portfolio of completed projects
  • Background check: For companies bidding on residential projects
  • Reviews: Aggregate from Google, Yelp, BBB, and your own platform

Trust UI Elements

Display verification status prominently on installer profiles:

<div className="flex items-center gap-2">
  <VerifiedBadge />
  <span className="text-sm text-green-700">
    Verified: License #12345 • NABCEP Certified • 
    Insured to $2M • {yearsInBusiness} years experience
  </span>
</div>

Include a "Why you can trust us" section on every page -- EnergySage does this and it works. Explain your vetting process transparently.

Tech Stack Recommendations

Here's what I'd actually use to build this in 2025:

Layer Technology Why
Frontend Next.js 15 (App Router) SSR + ISR for dynamic pages, RSC for performance
Styling Tailwind CSS + shadcn/ui Fast iteration, accessible components
CMS Payload CMS (self-hosted) Open source, great for custom collections
Database PostgreSQL 16 + PostGIS Geospatial queries, reliability
ORM Prisma + raw SQL for geo Best of both worlds
Search Meilisearch (self-hosted) Free, fast, geo-aware
Maps Mapbox GL JS Better pricing than Google Maps at scale
Geocoding Mapbox Geocoding API $0.75 per 1,000 requests
Hosting Vercel (frontend) + Railway (API + DB) Easy deploys, reasonable pricing
Auth Clerk or NextAuth.js Separate flows for homeowners and installers
Monitoring Sentry + Plausible Analytics Error tracking + privacy-friendly analytics
Email Resend Transactional emails for quote notifications

Total infrastructure cost for an MVP handling ~10,000 monthly users: roughly $150-300/month. At scale (100K+ monthly users), expect $800-2,000/month before factoring in API costs.

If you want help getting this built properly, check out our pricing or reach out directly. We've built multi-thousand-page directory sites with similar architectures.

Performance Benchmarks and Cost Estimates

Here's what realistic development timelines look like:

Phase Scope Timeline Cost Range
MVP Directory Installer listings, geo search, location pages 8-12 weeks $30K-60K
Quote System Lead capture, installer matching, quote comparison 6-8 weeks $25K-45K
Installer Portal Dashboard, lead management, analytics 4-6 weeks $15K-30K
Homeowner Dashboard Saved quotes, messages, project tracking 4-6 weeks $15K-25K
Payment Integration Installer subscriptions, lead payments 2-3 weeks $8K-15K
Total MVP Marketplace 24-35 weeks $93K-175K

These are realistic numbers for a quality build with an experienced team. I've seen agencies quote $250K+ for similar scope, and I've seen teams try to do it for $15K and end up with something that falls apart under real traffic.

Performance targets you should hit:

  • Location page load: < 1.5s (LCP) -- these are your SEO money pages
  • Search results: < 200ms from keystroke to results
  • Geo query: < 100ms for "installers near [zip code]"
  • Core Web Vitals: All green across the board
  • Build time: < 10 minutes for 5,000+ static pages

FAQ

How much does it cost to build a solar installer directory like EnergySage?

A basic directory with geo search and installer profiles can be built for $30K-60K. A full marketplace with quote comparison, installer dashboards, and payment processing typically runs $93K-175K depending on feature scope. EnergySage itself was funded with a Department of Energy grant and has raised over $10M in venture capital, so don't expect to replicate their full feature set on a shoestring budget.

What tech stack should I use for a solar marketplace?

I recommend Next.js 15 for the frontend (SSR capabilities are critical for SEO), PostgreSQL with PostGIS for geospatial queries, and either Algolia or Meilisearch for instant search. For the CMS layer, Payload CMS or Sanity work well for managing installer profiles and editorial content. Host on Vercel for the frontend and Railway or Render for your database and API.

How do solar marketplace platforms like EnergySage make money?

EnergySage uses a lead-generation model where installers pay to access qualified homeowner leads. They also license their extensive solar pricing data to investment firms, academic researchers, and government agencies. Solar.com takes a slightly different approach with preferred pricing commissions. Most directory sites start with featured listing fees ($50-500/month per installer) and evolve into per-lead pricing ($20-100 per qualified lead) as they scale.

How do I get solar installer data to populate my directory?

Start by scraping public data: state contractor license databases, NABCEP's certified installer directory, and manufacturer locators (like Enphase's installer network). Then build an onboarding flow where installers claim and enhance their profiles. Early on, you'll need to manually reach out to installers -- offer free listings to seed the marketplace. The cold start problem is real; nobody wants to join an empty marketplace.

How important is SEO for a solar installer directory?

It's essentially your entire growth strategy. Programmatic location pages targeting "solar installers in [city]" queries are how EnergySage drives the majority of their traffic. You'll want unique pages for every major city and state, each with genuine local data (average costs, incentive information, installer counts). Without strong organic search performance, you'll burn through paid acquisition budgets trying to compete.

Should I use Google Maps or Mapbox for the mapping features?

Mapbox, in most cases. Google Maps pricing gets expensive fast -- the Maps JavaScript API charges $7 per 1,000 loads after the $200/month free credit. Mapbox charges $0.60 per 1,000 map loads with a generous 50,000 free loads per month. For a directory that might render maps on thousands of location pages, Mapbox can save you thousands of dollars monthly. However, use Google's Solar API for building insights data -- nothing else comes close.

How do I handle installer verification and vetting?

Build a multi-step verification pipeline: check state contractor license databases (many have public APIs), verify NABCEP certifications, require proof of insurance, and cross-reference business registration records. EnergySage requires 2-3 years of experience and multiple completed installations. Automate what you can, but expect some manual review. Display verification badges prominently -- they directly impact homeowner conversion rates.

Can I use the Google Solar API for estimating solar potential?

Yes, and you should. Google's Solar API covers 320+ million buildings across the US and provides roof segment analysis, annual sunshine data, and estimated energy production. It costs $10 per 1,000 requests for building insights. Use it to pre-populate system size estimates when homeowners enter their address -- this dramatically improves the quote request experience and sets realistic expectations before installers even get involved.