Apollo
A
Apollo

tap-apollo (meltanolabs variant)

Apollo.io is a sales intelligence and engagement platform. Teams use it to find and manage contacts and accounts, track deals, and run outreach sequences of emails, calls and tasks.

This tap extracts the records your team manages in Apollo, plus workspace reference data such as users, stages, lists and custom fields, through the Apollo REST API. It does not search or enrich Apollo's people and company database, so it uses no Apollo credits.

Prerequisites

API Key: In Apollo, go to Settings → Integrations → API and create an API key. Apollo API keys are scoped to the endpoints you choose when you create them, and a stream fails with a 403 error if the key can't call its endpoint. The simplest option is a master API key, which can call every endpoint. phone_calls always requires a master key. Otherwise, give the key access to the endpoint of each stream you select:

| Stream | Endpoint | |---|---| | contacts | api/v1/contacts/search | | accounts | api/v1/accounts/search | | opportunities | deals/api/v1/opportunities/search | | tasks | api/v1/tasks/search | | phone_calls | Master API key only | | sequences | api/v1/emailer_campaigns/search | | emailer_messages | api/v1/emailer_messages/search | | users | api/v1/users/search | | email_accounts | api/v1/email_accounts/index | | lists | api/v1/labels/index | | custom_fields | api/v1/typed_custom_fields/index | | contact_stages | api/v1/contact_stages/index | | account_stages | api/v1/account_stages/index | | opportunity_stages | deals/api/v1/opportunity_stages/index |

Available Streams

| Stream | Description | Replication | Replication Key | Primary Key | |---|---|---|---|---| | contacts | People your team has saved to Apollo | Incremental | updated_at | id | | accounts | Companies your team has saved to Apollo | Full Table | — | id | | opportunities | Deals and their amounts, stages and outcomes | Full Table | — | id | | tasks | Tasks assigned to users, such as calls, emails and LinkedIn steps | Full Table | — | id | | phone_calls | Calls made or received through Apollo | Full Table | — | id | | sequences | Outreach sequences, with their engagement statistics | Full Table | — | id | | emailer_messages | Emails created and sent by sequences | Full Table | — | id | | users | Users on your Apollo team | Full Table | — | id | | email_accounts | Email inboxes linked to Apollo for sending | Full Table | — | id | | lists | Lists of contacts, accounts or sequences | Full Table | — | id | | custom_fields | Custom field definitions | Full Table | — | id | | contact_stages | Stages a contact can be in | Full Table | — | id | | account_stages | Stages an account can be in | Full Table | — | id | | opportunity_stages | Stages of each deal pipeline | Full Table | — | id |

Limitations

  • 50,000 records per search. Apollo's search endpoints return at most 500 pages of 100 records. When a stream reaches that limit, the tap logs a warning and stops, so larger collections are not fully extracted.
  • Contacts are synced newest first. Apollo has no filter for records updated since a time, so the tap sorts contacts by updated_at, newest first, and stops at the bookmark. After the first sync, a run only reads the contacts that changed since the last one.
  • Rate limits. Apollo limits API calls per minute, hour and day, depending on your plan. The tap waits and retries when it is rate limited.
  • Fields with varying types are text. Some Apollo fields come back as different types from record to record, or are rarely set. These are loaded as text, with objects, lists, numbers and booleans stored as JSON (for example true or {"title": "CTO"}).
  • Statistics Apollo is still calculating are empty. A sequence's rates, such as its click rate, are empty until Apollo has calculated them, and are filled in on a later sync.

Data recovery / backfill

Only contacts keeps a bookmark between syncs: the latest updated_at it has loaded. Every other stream is full table and re-syncs completely on each run, so it needs no backfill.

To backfill or re-sync contacts, reset the pipeline state so the next sync starts again from Start Date, and clear or lower Start Date first if you need older contacts. Lowering Start Date alone has no effect once a bookmark exists. A full re-sync is still limited to the 50,000 most recently updated contacts.

The tables below list the main fields of each stream. Every stream has more fields; the full schemas are in the tap's catalog.

contacts

Supports incremental replication using updated_at.

| Field | Type | Description | |---|---|---| | id | string | Unique contact identifier | | first_name | string | First name | | last_name | string | Last name | | name | string | Full name | | title | string | Job title | | email | string | Email address | | email_status | string | Email verification status | | linkedin_url | string | LinkedIn profile URL | | account_id | string | ID of the contact's account | | organization_id | string | ID of the contact's company in Apollo's database | | organization_name | string | Company name | | contact_stage_id | string | ID of the contact's stage | | owner_id | string | ID of the user who owns the contact | | label_ids | array[string] | IDs of the lists the contact is on | | source | string | How the contact was added | | last_activity_date | string | Date of the last activity with the contact | | typed_custom_fields | object | Custom field values, keyed by custom field ID | | account | object | The contact's account | | created_at | datetime | Creation timestamp | | updated_at | datetime | Last modification timestamp — replication key |

accounts

| Field | Type | Description | |---|---|---| | id | string | Unique account identifier | | name | string | Company name | | domain | string | Company website domain | | phone | string | Company phone number | | linkedin_url | string | LinkedIn company page URL | | account_stage_id | string | ID of the account's stage | | owner_id | string | ID of the user who owns the account | | organization_id | string | ID of the company in Apollo's database | | parent_account_id | string | ID of the parent account | | num_contacts | integer | Number of contacts at the account | | label_ids | array[string] | IDs of the lists the account is on | | source | string | How the account was added | | last_activity_date | string | Date of the last activity with the account | | typed_custom_fields | object | Custom field values, keyed by custom field ID | | created_at | datetime | Creation timestamp |

opportunities

Deals.

| Field | Type | Description | |---|---|---| | id | string | Unique deal identifier | | name | string | Deal name | | account_id | string | ID of the deal's account | | owner_id | string | ID of the user who owns the deal | | amount | number | Deal amount, in the deal's currency | | amount_in_team_currency | number | Deal amount, in your team's currency | | currency | object | Deal currency | | opportunity_stage_id | string | ID of the deal's stage | | stage_name | string | Name of the deal's stage | | opportunity_pipeline_id | string | ID of the deal's pipeline | | is_closed | boolean | Whether the deal is closed | | is_won | boolean | Whether the deal is won | | closed_date | string | Expected or actual close date | | forecast_category | string | Forecast category | | source | string | How the deal was added | | typed_custom_fields | object | Custom field values, keyed by custom field ID | | created_at | datetime | Creation timestamp | | stage_updated_at | datetime | When the deal last changed stage |

tasks

| Field | Type | Description | |---|---|---| | id | string | Unique task identifier | | type | string | Task type, e.g. action_item or linkedin_step_connect | | status | string | Task status | | priority | string | Task priority | | title | string | Task title | | note | string | Task note | | contact_id | string | ID of the related contact | | account_id | string | ID of the related account | | opportunity_id | string | ID of the related deal | | user_id | string | ID of the user the task is assigned to | | emailer_campaign_id | string | ID of the sequence that created the task | | due_at | datetime | Due timestamp | | completed_at | datetime | Completion timestamp | | skipped_at | datetime | When the task was skipped | | created_at | datetime | Creation timestamp |

phone_calls

| Field | Type | Description | |---|---|---| | id | string | Unique call identifier | | start_time | datetime | Call start time | | end_time | datetime | Call end time | | duration | integer | Call duration in seconds | | inbound | boolean | Whether the call was inbound | | status | string | Call status | | from_number | string | Calling phone number | | to_number | string | Called phone number | | contact_id | string | ID of the related contact | | account_id | string | ID of the related account | | opportunity_id | string | ID of the related deal | | user_id | string | ID of the user who made or took the call | | phone_call_outcome_id | string | ID of the call outcome | | phone_call_purpose_id | string | ID of the call purpose | | note_text | string | Call notes | | recording_url | string | URL of the call recording |

sequences

Sequences (Apollo's API calls them emailer campaigns), with their engagement statistics.

| Field | Type | Description | |---|---|---| | id | string | Unique sequence identifier | | name | string | Sequence name | | active | boolean | Whether the sequence is active | | archived | boolean | Whether the sequence is archived | | num_steps | integer | Number of steps | | user_id | string | ID of the user who owns the sequence | | label_ids | array[string] | IDs of the lists the sequence is on | | unique_scheduled | integer | Number of contacts with a scheduled email | | unique_delivered | integer | Number of contacts with a delivered email | | unique_opened | integer | Number of contacts who opened an email | | unique_replied | integer | Number of contacts who replied | | unique_bounced | integer | Number of contacts whose email bounced | | open_rate | number | Open rate | | reply_rate | number | Reply rate | | bounce_rate | number | Bounce rate | | created_at | datetime | Creation timestamp | | last_used_at | datetime | When the sequence was last used |

emailer_messages

Outreach emails created and sent by sequences.

| Field | Type | Description | |---|---|---| | id | string | Unique email identifier | | emailer_campaign_id | string | ID of the sequence | | campaign_name | string | Name of the sequence | | emailer_step_id | string | ID of the sequence step | | contact_id | string | ID of the recipient contact | | account_id | string | ID of the recipient's account | | user_id | string | ID of the sending user | | email_account_id | string | ID of the sending email account | | status | string | Sending status, e.g. failed | | subject | string | Email subject | | from_email | string | Sender email address | | to_email | string | Recipient email address | | to_name | string | Recipient name | | body_text | string | Plain-text email body | | due_at | datetime | When the email is or was scheduled to send | | completed_at | datetime | When the email was sent | | failed_at | datetime | When sending failed | | failure_reason | string | Why sending failed | | not_sent_reason | string | Why the email was not sent | | created_at | datetime | Creation timestamp |

users

Users on your Apollo team.

| Field | Type | Description | |---|---|---| | id | string | Unique user identifier | | name | string | Full name | | first_name | string | First name | | last_name | string | Last name | | email | string | Email address | | title | string | Job title | | team_id | string | ID of the user's team | | permission_set_id | string | ID of the user's permission set | | deleted | boolean | Whether the user is deleted | | created_at | datetime | Creation timestamp |

email_accounts

Email inboxes linked to Apollo for sending.

| Field | Type | Description | |---|---|---| | id | string | Unique email account identifier | | email | string | Email address | | user_id | string | ID of the user who owns the inbox | | type | string | Account type, e.g. gmail | | provider_display_name | string | Email provider name | | active | boolean | Whether the inbox is active | | default | boolean | Whether this is the user's default inbox | | email_daily_threshold | integer | Daily sending limit | | last_synced_at | datetime | When the inbox last synced | | revoked_at | datetime | When access to the inbox was revoked | | created_at | datetime | Creation timestamp |

lists

Lists of contacts, accounts or sequences (Apollo's API calls them labels).

| Field | Type | Description | |---|---|---| | id | string | Unique list identifier | | name | string | List name | | modality | string | What the list holds: contacts, accounts or emailer_campaigns | | cached_count | integer | Number of records on the list | | user_id | string | ID of the user who created the list | | team_id | string | ID of the team | | created_at | datetime | Creation timestamp | | updated_at | datetime | Last modification timestamp |

custom_fields

| Field | Type | Description | |---|---|---| | id | string | Unique custom field identifier, used as the key in typed_custom_fields | | name | string | Field name | | type | string | Field type, e.g. picklist or datetime | | modality | string | Record type the field belongs to, e.g. contact | | picklist_values | array[object] | Allowed values of a picklist field | | is_local | boolean | Whether the field exists only in Apollo, not in a linked CRM | | mirrored | boolean | Whether the field is mirrored from a linked CRM |

contact_stages and account_stages

| Field | Type | Description | |---|---|---| | id | string | Unique stage identifier | | name | string | Stage name | | display_name | string | Stage name as shown in Apollo | | display_order | number | Position of the stage | | category | string | Stage category, e.g. in_progress, succeeded or failed | | team_id | string | ID of the team |

opportunity_stages

Deal stages.

| Field | Type | Description | |---|---|---| | id | string | Unique stage identifier | | name | string | Stage name | | opportunity_pipeline_id | string | ID of the pipeline the stage belongs to | | display_order | number | Position of the stage in the pipeline | | probability | number | Win probability of deals in this stage | | is_closed | boolean | Whether deals in this stage are closed | | is_won | boolean | Whether deals in this stage are won | | forecast_category_cd | string | Forecast category | | type | string | Stage type |

Settings

API Key

Apollo API key, from Settings → Integrations → API in Apollo. Use a master API key, or a key with access to the endpoint of each selected stream. phone_calls requires a master key.

Start Date

Earliest update time to extract for the contacts stream, e.g. 2024-01-01T00:00:00Z. Leave empty to extract all contacts. The other streams are always fully replicated.

Meltano Cloud Connector

Apollo connector is available on Meltano. It is built, maintained, supported, and tested by Meltano.

Why Meltano?
Expert supportDirect access to the team that built and maintains Meltano Cloud. Same-day responses during UK business hours. When something breaks, we fix it fast because we know exactly how it works.
Rigorously testedEvery connector goes through comprehensive testing and quality checks before production. Daily monitoring catches issues before they hit your pipelines. We don't just wrap open-source taps and hope for the best. We validate, we test, we maintain.
No maintenance overheadAPI changes. Connector updates. Schema drift. Breaking changes from upstream sources. We handle it all. Your team focuses on using data. Our team focuses on making sure it's there when you need it.
Access to Meltano Slack communityJoin 5,500+ data engineers and analytics practitioners. The community is active, helpful, and always on. Good for quick questions, sharing patterns, and learning what others are building.