← back to Norma

agents/onboard-agent/lib/propublica.js

82 lines

/**
 * ProPublica Nonprofit Explorer API Wrapper
 *
 * Searches and retrieves nonprofit organization data from ProPublica's
 * Nonprofit Explorer API v2.
 */

const fetch = require('node-fetch');

const BASE_URL = 'https://projects.propublica.org/nonprofits/api/v2';

/**
 * Search for nonprofits by keyword query.
 *
 * @param {string} query - Search query (org name, keyword, etc.)
 * @param {number} [page=0] - Page number (0-indexed)
 * @returns {Promise<{ organizations: Array, total: number }>}
 */
async function searchNonprofits(query, page = 0) {
  const url = `${BASE_URL}/search.json?q=${encodeURIComponent(query)}&page=${page}`;

  try {
    const res = await fetch(url, {
      headers: { 'User-Agent': 'Norma-OnBoard-Agent/1.0' },
      timeout: 15000,
    });

    if (!res.ok) {
      console.error(`[propublica] Search failed (${res.status}): ${url}`);
      return { organizations: [], total: 0 };
    }

    const data = await res.json();

    return {
      organizations: (data.organizations || []).map(org => ({
        ein: org.ein,
        name: org.name,
        city: org.city,
        state: org.state,
        ntee_code: org.ntee_code,
        subsection_code: org.subsection_code,
        total_revenue: org.total_revenue,
        total_assets: org.total_assets,
      })),
      total: data.total_results || 0,
    };
  } catch (err) {
    console.error(`[propublica] Search error for "${query}":`, err.message);
    return { organizations: [], total: 0 };
  }
}

/**
 * Get detailed info for a specific organization by EIN.
 *
 * @param {string} ein - Employer Identification Number
 * @returns {Promise<Object|null>}
 */
async function getOrgDetails(ein) {
  const url = `${BASE_URL}/organizations/${ein}.json`;

  try {
    const res = await fetch(url, {
      headers: { 'User-Agent': 'Norma-OnBoard-Agent/1.0' },
      timeout: 15000,
    });

    if (!res.ok) {
      console.error(`[propublica] Org details failed (${res.status}): EIN ${ein}`);
      return null;
    }

    return await res.json();
  } catch (err) {
    console.error(`[propublica] Org details error for EIN ${ein}:`, err.message);
    return null;
  }
}

module.exports = { searchNonprofits, getOrgDetails };