Back to Blog
API & Developer Guides

Building a Custom Lead Gen Dashboard with the Easy Email Finder API

Published February 10, 2026

Why Build a Custom Dashboard?

While the Easy Email Finder web app is great for individual use, teams often need a shared dashboard with custom workflows, usage tracking, and role-based access. With the API, you can build exactly what your team needs.

What We Are Building

Our dashboard will have three main features:

  • Search panel: Find businesses by keyword and location, then enrich them
  • Results table: View, filter, and export enriched leads
  • Usage tracker: Monitor credit consumption and API usage over time

Backend: Express API Proxy

Never expose your API key to the frontend. Instead, build a thin backend that proxies requests to the Easy Email Finder API.

// server.ts
import express from 'express';
import cors from 'cors';
import 'dotenv/config';

const app = express();
app.use(cors());
app.use(express.json());

const EEF_KEY = process.env.EEF_API_KEY;
const EEF_BASE = "https://easyemailfinder.com/api/v1";

const eefHeaders = {
  "Authorization": `Bearer ${EEF_KEY}`,
  "Content-Type": "application/json"
};

// Search endpoint
app.post('/api/search', async (req, res) => {
  try {
    const resp = await fetch(`${EEF_BASE}/search`, {
      method: "POST",
      headers: eefHeaders,
      body: JSON.stringify(req.body)
    });
    const data = await resp.json();
    res.json(data);
  } catch (err) {
    res.status(500).json({ error: "Search failed" });
  }
});

// Enrich batch endpoint
app.post('/api/enrich', async (req, res) => {
  try {
    const resp = await fetch(`${EEF_BASE}/enrich-batch`, {
      method: "POST",
      headers: eefHeaders,
      body: JSON.stringify(req.body)
    });
    const data = await resp.json();
    res.json(data);
  } catch (err) {
    res.status(500).json({ error: "Enrichment failed" });
  }
});

// Balance endpoint
app.get('/api/balance', async (req, res) => {
  try {
    const resp = await fetch(`${EEF_BASE}/balance`, {
      headers: { "Authorization": `Bearer ${EEF_KEY}` }
    });
    const data = await resp.json();
    res.json(data);
  } catch (err) {
    res.status(500).json({ error: "Could not fetch balance" });
  }
});

// Usage endpoint
app.get('/api/usage', async (req, res) => {
  try {
    const resp = await fetch(`${EEF_BASE}/usage`, {
      headers: { "Authorization": `Bearer ${EEF_KEY}` }
    });
    const data = await resp.json();
    res.json(data);
  } catch (err) {
    res.status(500).json({ error: "Could not fetch usage" });
  }
});

app.listen(3001, () => console.log("API proxy running on port 3001"));

Frontend: React Dashboard

Here is the core React component for the search and results panel:

// SearchPanel.tsx
import { useState } from 'react';

interface Lead {
  name: string;
  email: string;
  website: string;
  phone: string;
  address: string;
}

export function SearchPanel() {
  const [query, setQuery] = useState('');
  const [location, setLocation] = useState('');
  const [leads, setLeads] = useState<Lead[]>([]);
  const [loading, setLoading] = useState(false);
  const [credits, setCredits] = useState<number | null>(null);

  async function handleSearch() {
    setLoading(true);
    try {
      // Step 1: Search
      const searchRes = await fetch('/api/search', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ query, location, mode: 'local' })
      });
      const { results } = await searchRes.json();

      // Step 2: Enrich
      const websites = results
        .filter((r: any) => r.website)
        .map((r: any) => r.website);

      if (websites.length > 0) {
        const enrichRes = await fetch('/api/enrich', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ websites: websites.slice(0, 20) })
        });
        const enriched = await enrichRes.json();
        setLeads(enriched.results || []);
      }

      // Update balance
      const balRes = await fetch('/api/balance');
      const { credits: c } = await balRes.json();
      setCredits(c);
    } catch (err) {
      console.error('Search failed:', err);
    } finally {
      setLoading(false);
    }
  }

  function exportCSV() {
    const header = 'Name,Email,Website,Phone,Address\n';
    const rows = leads
      .filter(l => l.email)
      .map(l => `"${l.name}","${l.email}","${l.website}","${l.phone}","${l.address}"`)
      .join('\n');
    const blob = new Blob([header + rows], { type: 'text/csv' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `leads_${query}_${location}.csv`;
    a.click();
  }

  return (
    <div>
      <h1>Lead Generator</h1>
      {credits !== null && <p>Credits: {credits}</p>}
      <input value={query} onChange={e => setQuery(e.target.value)}
        placeholder="Business type (e.g., dentists)" />
      <input value={location} onChange={e => setLocation(e.target.value)}
        placeholder="Location (e.g., Austin, TX)" />
      <button onClick={handleSearch} disabled={loading}>
        {loading ? 'Searching...' : 'Find Leads'}
      </button>
      {leads.length > 0 && <button onClick={exportCSV}>Export CSV</button>}
      <table>
        <thead><tr>
          <th>Name</th><th>Email</th><th>Phone</th><th>Website</th>
        </tr></thead>
        <tbody>
          {leads.filter(l => l.email).map((lead, i) => (
            <tr key={i}>
              <td>{lead.name}</td>
              <td>{lead.email}</td>
              <td>{lead.phone}</td>
              <td><a href={lead.website}>{lead.website}</a></td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

Adding Usage Analytics

Use the free /usage endpoint to build a chart showing credit consumption over time. Pair this with a charting library like Recharts to visualize trends. See our guide on monitoring API usage for implementation details.

Security Considerations

  • Never expose your Easy Email Finder API key in frontend code
  • Add authentication to your Express proxy (session-based or JWT)
  • Implement per-user rate limiting on your proxy to prevent abuse
  • Log all searches for audit purposes

Deployment

Deploy the Express backend to any Node.js platform (Railway, Render, or a VPS). The React frontend can be served as static files from the same server or deployed to Vercel/Netlify. For more on API architecture, refer to the developer docs. For the backend enrichment patterns, check our Node.js pipeline guide.

Ready to find business emails?

Try Easy Email Finder free — get 5 credits to start.

Start Finding Emails

Related Posts