Skip to content

@memberjunction/actions-apollo

Apollo.io data enrichment action classes for MemberJunction that enable automated enrichment of contact and account records using the Apollo.io API.

This package provides two server-side action classes that integrate with Apollo.io’s data enrichment services to automatically populate account and contact records with company information, social profiles, technology stacks, employment history, and education data. Both actions extend BaseAction from @memberjunction/actions and are registered via @RegisterClass for automatic discovery by the MemberJunction Actions engine.

Key capabilities:

  • Account enrichment — company address, phone, description, social URLs, technology stacks, and associated contacts discovered via organization domain lookup
  • Contact enrichment — bulk email verification, social profile URLs, employment history, and education history via people matching
  • Configurable field mappings — JSON-based parameter configuration maps Apollo.io fields to your custom entity fields
  • Rate limit handling — automatic retry with intelligent backoff for both per-minute and hourly Apollo.io rate limits
  • Batch processing — concurrent group processing with configurable batch sizes and pagination for large datasets

For general Actions framework architecture and design philosophy, see the parent Actions README and Actions CLAUDE.md.

flowchart TB
    subgraph Engine["MemberJunction Actions Engine"]
        AE["ActionEngineServer"]
    end

    subgraph Apollo["@memberjunction/actions-apollo"]
        AccAction["ApolloEnrichmentAccountsAction"]
        ConAction["ApolloEnrichmentContactsAction"]
        Config["Configuration\n(API key, batch sizes)"]
        Types["Apollo Type Definitions"]
    end

    subgraph ApolloAPI["Apollo.io REST API"]
        OrgEnrich["/organizations/enrich"]
        BulkMatch["/people/bulk_match"]
        PeopleSearch["/mixed_people/search"]
    end

    subgraph MJCore["MemberJunction Core"]
        Meta["Metadata"]
        RV["RunView"]
        BE["BaseEntity"]
    end

    AE -->|executes| AccAction
    AE -->|executes| ConAction
    AccAction --> Config
    ConAction --> Config
    AccAction --> Types
    ConAction --> Types
    AccAction -->|HTTP via axios| OrgEnrich
    AccAction -->|HTTP via axios| PeopleSearch
    ConAction -->|HTTP via axios| BulkMatch
    ConAction -->|HTTP via axios| PeopleSearch
    AccAction -->|read/write entities| MJCore
    ConAction -->|read/write entities| MJCore

    style Engine fill:#2d6a9f,stroke:#1a4971,color:#fff
    style Apollo fill:#7c5295,stroke:#563a6b,color:#fff
    style ApolloAPI fill:#b8762f,stroke:#8a5722,color:#fff
    style MJCore fill:#2d8659,stroke:#1a5c3a,color:#fff
flowchart LR
    Start["Load accounts\nmatching filter"] --> OrgAPI["Call /organizations/enrich\nper domain"]
    OrgAPI --> UpdateAcct["Update account\nfields"]
    OrgAPI --> TechRec["Create/update\ntechnology records"]
    OrgAPI --> PeopleAPI["Call /mixed_people/search\nfor domain contacts"]
    PeopleAPI --> CreateContact["Create/update\ncontact records"]
    CreateContact --> History["Create education\nhistory records"]
    UpdateAcct --> Next["Next account"]
    TechRec --> Next
    History --> Next

    style Start fill:#2d6a9f,stroke:#1a4971,color:#fff
    style OrgAPI fill:#b8762f,stroke:#8a5722,color:#fff
    style UpdateAcct fill:#2d8659,stroke:#1a5c3a,color:#fff
    style TechRec fill:#2d8659,stroke:#1a5c3a,color:#fff
    style PeopleAPI fill:#b8762f,stroke:#8a5722,color:#fff
    style CreateContact fill:#2d8659,stroke:#1a5c3a,color:#fff
    style History fill:#2d8659,stroke:#1a5c3a,color:#fff
    style Next fill:#64748b,stroke:#475569,color:#fff
flowchart LR
    Start["Page contacts\nmatching filter"] --> Batch["Batch into groups\nof 10"]
    Batch --> BulkAPI["Call /people/bulk_match\nper batch"]
    BulkAPI --> Update["Update contact\nfields from matches"]
    Update --> EmpHist["Upsert employment\nhistory"]
    Update --> EduHist["Upsert education\nhistory"]
    EmpHist --> NextBatch["Next batch"]
    EduHist --> NextBatch

    style Start fill:#2d6a9f,stroke:#1a4971,color:#fff
    style Batch fill:#64748b,stroke:#475569,color:#fff
    style BulkAPI fill:#b8762f,stroke:#8a5722,color:#fff
    style Update fill:#2d8659,stroke:#1a5c3a,color:#fff
    style EmpHist fill:#7c5295,stroke:#563a6b,color:#fff
    style EduHist fill:#7c5295,stroke:#563a6b,color:#fff
    style NextBatch fill:#64748b,stroke:#475569,color:#fff
Terminal window
npm install @memberjunction/actions-apollo
  1. An active Apollo.io account with API access
  2. Apollo.io API key set as the environment variable APOLLO_API_KEY
  3. MemberJunction framework properly configured with server-side action engine
  4. Target entities configured for storing enriched data (accounts, contacts, technologies, etc.)
Terminal window
APOLLO_API_KEY=your_apollo_api_key_here

The package defines the following defaults in config.ts:

ConstantDefaultDescription
ApolloAPIEndpointhttps://api.apollo.io/v1Apollo.io API base URL
EmailSourceNameApollo.ioSource label applied to enriched emails
GroupSize10Records per API batch request (Apollo max is 10)
ConcurrentGroups1Number of concurrent API request groups
MaxPeopleToEnrichPerOrg500Maximum contacts to enrich per organization
ApolloAPIKeyprocess.env.APOLLO_API_KEYRead from environment at startup

The ApolloEnrichmentAccountsAction enriches account/organization records by looking up company information using domain names. It can optionally discover contacts at the organization, track technology stacks, and create education history records.

ParameterRequiredTypeDescription
AccountEntityFieldMappingsYesJSON stringMaps account entity fields (see AccountEntityFields below)
AccountTechnologyEntityFieldMappingsNoJSON stringMaps technology relationship fields
TechnologyCategoryEntityFieldMappingsNoJSON stringMaps technology category fields
ContactEntityFieldMappingsNoJSON stringMaps contact entity fields for discovered contacts
ContactEducationHistoryEntityFieldMappingsNoJSON stringMaps education history fields
{
EntityName: string; // Target entity name (e.g., "Accounts")
DomainField: string; // Field containing company domain
AccountIDField: string; // Primary key field name
EnrichedAtField: string; // Timestamp field for tracking enrichment
Filter: string; // SQL filter for selecting records to process
AddressField?: string; // Street address
CityField?: string; // City
StateProvinceField?: string; // State/province
PostalCodeField?: string; // Postal code
DescriptionField?: string; // Company description
PhoneNumberField?: string; // Phone number
CountryField?: string; // Country
LinkedInField?: string; // LinkedIn URL
LogoURLField?: string; // Company logo URL
FacebookField?: string; // Facebook URL
TwitterField?: string; // Twitter URL
}
import { ActionEngineServer } from '@memberjunction/actions';
const engine = ActionEngineServer.Instance;
const result = await engine.RunAction({
ActionName: 'ApolloEnrichmentAccountsAction',
Params: [
{
Name: 'AccountEntityFieldMappings',
Value: JSON.stringify({
EntityName: 'Accounts',
DomainField: 'Domain',
AccountIDField: 'ID',
EnrichedAtField: 'LastEnrichedAt',
Filter: 'Domain IS NOT NULL AND LastEnrichedAt IS NULL',
CityField: 'City',
StateProvinceField: 'StateProvince',
LinkedInField: 'LinkedInURL',
DescriptionField: 'Description'
})
},
{
Name: 'AccountTechnologyEntityFieldMappings',
Value: JSON.stringify({
EntityName: 'Account Technologies',
AccountIDField: 'AccountID',
TechnologyIDField: 'TechnologyID',
TechnologyField: 'Technology',
CategoryField: 'Category',
EndedUseAtField: 'EndedUseAt'
})
},
{
Name: 'ContactEntityFieldMappings',
Value: JSON.stringify({
EntityName: 'Contacts',
EmailField: 'Email',
AccountIDField: 'AccountID',
EnrichedAtField: 'LastEnrichedAt',
FirstNameField: 'FirstName',
LastNameField: 'LastName',
TitleField: 'Title',
EmailSourceField: 'EmailSource',
ActivityCountField: 'ActivityCount'
})
}
],
ContextUser: contextUser
});

The ApolloEnrichmentContactsAction enriches existing contact records by matching on name and email combinations through Apollo’s bulk people matching API.

ParameterRequiredTypeDescription
EntityNameYesstringTarget entity containing contacts
EmailFieldYesstringField name for email addresses
FirstNameFieldYesstringField name for first names
LastNameFieldYesstringField name for last names
TitleFieldYesstringField name for job titles
EnrichedAtFieldYesstringField name for enrichment timestamp
FilterYesstringSQL filter to select contacts for enrichment
ProfilePictureURLFieldNostringField for profile picture URLs
AccountNameFieldNostringField for account/company names
DomainFieldNostringField for company domains
LinkedInFieldNostringField for LinkedIn profile URLs
TwitterFieldNostringField for Twitter profile URLs
FacebookFieldNostringField for Facebook profile URLs
EmploymentHistoryFieldMappingsNoJSON stringEmployment history entity field mappings
EducationHistoryFieldMappingsNoJSON stringEducation history entity field mappings
{
EmploymentHistoryEntityName: string; // Employment history entity
EmploymentHistoryContactIDFieldName: string; // Foreign key to contact
EmploymentHistoryOrganizationFieldName: string; // Organization name field
EmploymentHistoryTitleFieldName: string; // Job title field
}
{
EducationHistoryEntityName: string; // Education history entity
EducationHistoryContactIDFieldName: string; // Foreign key to contact
EducationHistoryInstitutionFieldName: string; // Institution name field
EducationHistoryDegreeFieldName: string; // Degree field
}
import { ActionEngineServer } from '@memberjunction/actions';
const engine = ActionEngineServer.Instance;
const result = await engine.RunAction({
ActionName: 'ApolloEnrichmentContactsAction',
Params: [
{ Name: 'EntityName', Value: 'Contacts' },
{ Name: 'EmailField', Value: 'Email' },
{ Name: 'FirstNameField', Value: 'FirstName' },
{ Name: 'LastNameField', Value: 'LastName' },
{ Name: 'TitleField', Value: 'Title' },
{ Name: 'EnrichedAtField', Value: 'LastEnrichedAt' },
{ Name: 'Filter', Value: 'Email IS NOT NULL AND LastEnrichedAt IS NULL' },
{ Name: 'DomainField', Value: 'Domain' },
{ Name: 'LinkedInField', Value: 'LinkedInURL' },
{
Name: 'EmploymentHistoryFieldMappings',
Value: JSON.stringify({
EmploymentHistoryEntityName: 'Contact Employment Histories',
EmploymentHistoryContactIDFieldName: 'ContactID',
EmploymentHistoryOrganizationFieldName: 'Organization',
EmploymentHistoryTitleFieldName: 'Title'
})
},
{
Name: 'EducationHistoryFieldMappings',
Value: JSON.stringify({
EducationHistoryEntityName: 'Contact Education Histories',
EducationHistoryContactIDFieldName: 'ContactID',
EducationHistoryInstitutionFieldName: 'Institution',
EducationHistoryDegreeFieldName: 'Degree'
})
}
],
ContextUser: contextUser
});

Registered as "ApolloEnrichmentAccountsAction" via @RegisterClass(BaseAction). Extends BaseAction.

Processing behavior:

  • Queries accounts matching the configured filter
  • For each account, calls /organizations/enrich with the domain
  • Updates account fields with enriched organization data
  • Optionally creates/updates technology stack records with historical tracking (marks ended technologies)
  • Optionally discovers and creates contact records via /mixed_people/search
  • Processes recursively up to 5 times to handle remaining records
  • Supports concurrent domain processing (configurable via ConcurrentGroups)

Registered as "ApolloEnrichmentContactsAction" via @RegisterClass(BaseAction). Extends BaseAction.

Processing behavior:

  • Pages through contact records matching the configured filter (500 per page)
  • Batches contacts into groups of 10 for Apollo’s /people/bulk_match endpoint
  • Updates matching contacts with enriched social profiles and company data
  • Optionally creates/updates employment and education history records
  • Supports secondary enrichment via /mixed_people/search for organization-level lookups

All types are exported from src/generic/apollo.types.ts:

TypeDescription
ProcessPersonRecordGroupParamsParameters for batch contact group processing
ApolloBulkPeopleRequestRequest payload for /people/bulk_match
ApolloBulkPeopleRequestDetailIndividual person detail within a bulk request
ApolloBulkPeopleResponseResponse from /people/bulk_match
ContactEntityFieldsField mapping configuration for contact entities
ContactEducationHistoryEntityFieldsField mapping for education history entities
TechnologyCategoryEntityFieldsField mapping for technology category entities
AccountTechnologyEntityFieldsField mapping for account-technology relationship entities
AccountEntityFieldsField mapping configuration for account entities
ProcessSingleDomainParamsParameters for processing a single domain enrichment
OrganizationEnrichmentRequestRequest for /organizations/enrich
OrganizationEnrichmentResponseResponse from organization enrichment
OrganizationEnrichmentOrganizationDetailed organization data from Apollo
OrganizationEnrichmentOrganizationAccountAccount data within organization response
TechnologyMapTechnology record with name, category, and UID
SearchPeopleResponseResponse from /mixed_people/search
SearchPeopleResponsePersonIndividual person data from search response
EmploymentHistoryEmployment/education history entry

Both action classes include a WrapApolloCall method that provides:

  • Automatic retry on HTTP 429 (Too Many Requests) responses
  • Per-minute backoff: 60-second wait on standard rate limit responses
  • Hourly backoff: 60-minute wait when Apollo’s hourly rate limit is detected (contact action only)
  • Exception handling: Catches both Axios response errors and thrown exceptions for 429 status codes
  • Comprehensive logging via MemberJunction’s LogError and LogStatus utilities

Both actions automatically exclude contacts with the following job titles to maintain data quality:

  • member
  • student member
  • student
  • volunteer
EndpointHTTP MethodUsed ByPurpose
/organizations/enrichGETAccounts actionOrganization data by domain
/people/bulk_matchPOSTContacts actionBulk contact matching (up to 10 per request)
/mixed_people/searchPOSTBoth actionsPeople search by organization domain
PackagePurpose
@memberjunction/actionsBase action class (BaseAction) and action engine
@memberjunction/actions-baseAction parameter types (ActionParam, ActionResultSimple, RunActionParams)
@memberjunction/coreMetadata, RunView, BaseEntity, logging utilities, UserInfo, CompositeKey
@memberjunction/core-entitiesMemberJunction entity definitions
@memberjunction/global@RegisterClass decorator for action registration
axiosHTTP client for Apollo.io API requests
  • Maximum 10 records per bulk API request (Apollo.io API limitation)
  • Rate limits apply based on your Apollo.io subscription tier (handled automatically with retries)
  • Personal emails may not be revealed in GDPR-compliant regions
  • Account enrichment processes domains sequentially within each concurrent group
  • Contact enrichment paginates with a maximum of 500 contacts per organization
  • Account enrichment recurses up to 5 times to prevent infinite loops
  • Excluded job titles (member, student member, student, volunteer) are automatically skipped