INTEGRATIONSLEAD GENERATION

HubSpot Lead Pusher - CRM Contact Import Automation

Push scraped leads directly into HubSpot CRM as contacts, companies, and deals. Feed in lead data from any Apify actor or paste it manually, and HubSpot Lead Pusher handles the mapping, deduplication, and batch upsert via the HubSpot API. A built-in dry-run mode lets you preview exactly what will be created before committing anything to your CRM.

Try on Apify Store
$0.10per event
1
Users (30d)
29
Runs (30d)
90
Actively maintained
Maintenance Pulse
$0.10
Per event

Maintenance Pulse

90/100
Last Build
Today
Last Version
1d ago
Builds (30d)
8
Issue Response
N/A

Cost Estimate

How many results do you need?

lead-pusheds
Estimated cost:$10.00

Pricing

Pay Per Event model. You only pay for what you use.

EventDescriptionPrice
lead-pushedCharged per lead pushed to HubSpot. Includes company creation, contact mapping, deal association, and verification.$0.10

Example: 100 events = $10.00 · 1,000 events = $100.00

Documentation

Push scraped leads directly into HubSpot CRM as contacts, companies, and deals. Feed in lead data from any Apify actor or paste it manually, and HubSpot Lead Pusher handles the mapping, deduplication, and batch upsert via the HubSpot API. A built-in dry-run mode lets you preview exactly what will be created before committing anything to your CRM.

HubSpot Lead Pusher bridges the gap between web scraping and CRM action. Instead of manually exporting CSV files and importing them into HubSpot, this actor automates the entire pipeline. It accepts lead data from Apify datasets (output from actors like Website Contact Scraper, B2B Lead Gen Suite, or Google Maps Lead Enricher) or inline JSON, maps each field to the correct HubSpot property, and pushes everything in optimized batches of up to 100 records per API call.

What Does It Do

HubSpot Lead Pusher takes structured lead data and creates or updates three types of HubSpot CRM objects:

  • Companies -- Created from domain names. The actor normalizes URLs, extracts the root domain, and maps fields like company name, phone, industry, address, city, state, country, LinkedIn page, and Facebook page to HubSpot company properties. Companies are upserted by domain, so running the actor twice with the same data updates existing records rather than creating duplicates.

  • Contacts -- Created from email addresses found in the lead data. The actor extracts contacts from both the structured contacts array (with name, title, phone) and standalone emails fields. Each contact gets mapped with first name, last name, job title, phone, company name, website, lifecycle stage, and lead status. Contacts are upserted by email address for deduplication.

  • Deals -- Optionally created for each lead. Deals are named after the company (e.g., "Lead: Apify") and placed in the pipeline stage you specify. Lead scores from upstream qualification actors can be mapped to the deal amount field for pipeline prioritization.

After creating all objects, the actor automatically associates contacts with their parent companies using the HubSpot v4 associations API, so your CRM relationships are properly linked from the start.

Why Use This on Apify

Running HubSpot Lead Pusher on Apify gives you a serverless, schedulable CRM integration without writing code or maintaining infrastructure. You can chain it with other Apify actors to build fully automated lead generation pipelines: scrape websites for contacts, qualify and score the leads, then push the best ones directly into your HubSpot pipeline. Apify Schedules let you run these pipelines daily or weekly so your CRM stays current without manual intervention.

Unlike HubSpot's native import tool, this actor handles the field mapping automatically, normalizes domains and emails, splits full names into first and last name fields, and translates lead grades (A/B/C/D) into HubSpot lead statuses (Open, In Progress, Unqualified). It also works entirely through the API, so there is no file upload step and no waiting for background processing.

Key Features

  • Batch upsert -- Companies are upserted by domain and contacts by email, preventing duplicates even across multiple runs
  • Automatic field mapping -- Translates scraped lead data into HubSpot properties with domain normalization, name splitting, and grade-to-status conversion
  • Dry-run preview -- See the exact HubSpot properties that would be created without touching your CRM, useful for verifying mappings before committing
  • Contact-to-company associations -- Automatically links contacts to their parent company in HubSpot after creation
  • Deal creation -- Optionally create pipeline deals for each lead with configurable deal stage
  • Flexible input -- Accept leads inline as JSON or pull them from any Apify dataset by ID
  • Rate limit handling -- Built-in retry logic with exponential backoff for HubSpot API rate limits
  • Pipeline compatibility -- Works with output from Website Contact Scraper, B2B Lead Gen Suite, Google Maps Lead Enricher, and any custom lead format

How to Use

  1. Get your HubSpot access token -- Go to HubSpot Settings > Integrations > Private Apps and create a private app with scopes: crm.objects.contacts.write, crm.objects.companies.write, and crm.objects.deals.write. Copy the access token.

  2. Prepare your lead data -- Either paste leads directly into the "Leads (inline data)" JSON field, or provide an Apify Dataset ID from a previous scraping run. Each lead should have at least a domain or email field.

  3. Configure what to create -- Choose whether to create companies, contacts, and/or deals. Set the lifecycle stage for contacts (default: "lead") and the deal stage for deals (default: "appointmentscheduled").

  4. Run a dry run first -- Leave the "Dry run" checkbox enabled (it is on by default) to preview the mapped HubSpot properties without pushing anything. Review the output dataset to confirm the field mapping looks correct.

  5. Push to HubSpot -- Uncheck "Dry run", enter your HubSpot access token, and run again. The actor will create/update all records and report the results.

  6. Check results -- Each lead produces one output record showing the status (success, partial, or error) for the company, contacts, and deal, along with any error messages.

Input Parameters

ParameterTypeRequiredDefaultDescription
hubspotAccessTokenstringNo--HubSpot private app access token. Leave empty to run in dry-run mode.
leadsarrayNo--Lead data as JSON array. Each object should have at least a domain or email.
datasetIdstringNo--Apify dataset ID to pull leads from (alternative to inline leads).
createCompaniesbooleanNotrueCreate or update HubSpot company records from lead domains.
createContactsbooleanNotrueCreate or update HubSpot contact records from lead emails.
createDealsbooleanNofalseCreate a HubSpot deal for each lead.
dealStagestringNoappointmentscheduledHubSpot pipeline stage for new deals.
lifecycleStagestringNoleadHubSpot lifecycle stage for new contacts (subscriber, lead, marketingqualifiedlead, salesqualifiedlead, opportunity, customer).
dryRunbooleanNotruePreview mapped data without pushing to HubSpot.

You must provide either leads or datasetId. If both are provided, inline leads take priority.

Input Examples

Quick dry-run test with inline leads:

{
    "leads": [
        {
            "domain": "apify.com",
            "companyName": "Apify",
            "emails": ["[email protected]"],
            "contacts": [{"name": "Jan Curn", "title": "CEO", "email": "[email protected]"}],
            "score": 85,
            "grade": "A"
        }
    ],
    "dryRun": true
}

Chain with B2B Lead Gen Suite dataset:

{
    "hubspotAccessToken": "pat-na1-xxxx",
    "datasetId": "abc123XYZ",
    "createCompanies": true,
    "createContacts": true,
    "createDeals": true,
    "dealStage": "qualifiedtobuy",
    "lifecycleStage": "marketingqualifiedlead",
    "dryRun": false
}

Contacts only (skip companies and deals):

{
    "hubspotAccessToken": "pat-na1-xxxx",
    "leads": [
        {"email": "[email protected]", "companyName": "Example Inc"},
        {"email": "[email protected]", "companyName": "Test Corp"}
    ],
    "createCompanies": false,
    "createContacts": true,
    "createDeals": false,
    "dryRun": false
}

Input Tips

  • Omitting the access token automatically triggers dry-run mode, even if the dryRun checkbox is unchecked.
  • The dealStage value must match an existing stage in your HubSpot pipeline. Common values: appointmentscheduled, qualifiedtobuy, presentationscheduled, decisionmakerboughtin, contractsent, closedwon, closedlost.
  • Use lifecycleStage: "salesqualifiedlead" for high-scoring leads (grade A/B) and "lead" for lower grades to segment your CRM automatically.

Output Example

Each item in the output dataset represents one lead that was processed:

{
    "domain": "apify.com",
    "status": "success",
    "dryRun": false,
    "company": {
        "id": "12345678901",
        "name": "Apify",
        "domain": "apify.com",
        "status": "created"
    },
    "contacts": [
        {
            "id": "98765432101",
            "email": "[email protected]",
            "name": "Jan Curn",
            "status": "created"
        },
        {
            "id": "98765432102",
            "email": "[email protected]",
            "name": "",
            "status": "created"
        }
    ],
    "deal": {
        "id": "55566677701",
        "name": "Lead: Apify",
        "status": "created"
    },
    "associations": {
        "contactToCompany": 2
    },
    "errors": []
}

In dry-run mode, all id fields are null and the status is "dry_run". The properties object is included in dry-run output so you can inspect the exact HubSpot fields that would be set.

Output Fields

FieldTypeDescription
domainstringLead domain or "unknown" if no domain provided
statusstringOverall result: success (all objects created), partial (some failed), error (all failed), or dry_run
dryRunbooleanWhether this was a dry-run preview
companyobject/nullCompany upsert result with id, name, domain, status. Null if companies disabled
contactsarrayArray of contact results, each with id, email, name, status
dealobject/nullDeal creation result with id, name, status. Null if deals disabled
associations.contactToCompanynumberNumber of contact-to-company associations created for this lead
errorsarrayError messages for any failed operations

Use via API

You can run HubSpot Lead Pusher programmatically using the Apify API. This is useful for integrating it into automated pipelines where an upstream actor scrapes leads and this actor pushes them to CRM.

Python

from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")

run = client.actor("ryanclinton/hubspot-lead-pusher").call(run_input={
    "hubspotAccessToken": "pat-na1-xxxx",
    "datasetId": "abc123XYZ",  # from a previous scraping run
    "createCompanies": True,
    "createContacts": True,
    "createDeals": True,
    "dealStage": "qualifiedtobuy",
    "lifecycleStage": "marketingqualifiedlead",
    "dryRun": False,
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    status = item["status"]
    domain = item["domain"]
    contacts_count = len(item.get("contacts", []))
    print(f"{domain}: {status} ({contacts_count} contacts)")

JavaScript

import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });

const run = await client.actor('ryanclinton/hubspot-lead-pusher').call({
    hubspotAccessToken: 'pat-na1-xxxx',
    datasetId: 'abc123XYZ',
    createCompanies: true,
    createContacts: true,
    createDeals: true,
    dealStage: 'qualifiedtobuy',
    lifecycleStage: 'marketingqualifiedlead',
    dryRun: false,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach(item => {
    console.log(`${item.domain}: ${item.status} (${item.contacts.length} contacts)`);
});

cURL

curl "https://api.apify.com/v2/acts/ryanclinton~hubspot-lead-pusher/runs?token=YOUR_APIFY_TOKEN" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "hubspotAccessToken": "pat-na1-xxxx",
    "datasetId": "abc123XYZ",
    "createCompanies": true,
    "createContacts": true,
    "createDeals": true,
    "dryRun": false
  }'

How It Works

HubSpot Lead Pusher follows a four-step pipeline to ensure all CRM objects are created in the correct order for association linking:

┌─────────────────────────────────────────────────────────────┐
│  LOAD LEADS (inline JSON or Apify dataset)                  │
└────────────────────────┬────────────────────────────────────┘
                         │
              ┌──────────▼──────────┐
              │  Dry Run?           │
              │  → Map & preview    │─── YES → Output mapped properties
              │  → No API calls     │
              └──────────┬──────────┘
                         │ NO
              ┌──────────▼──────────┐
              │  STEP 1: Companies  │  Batch upsert by domain
              │  /companies/upsert  │  → companyIdByDomain map
              └──────────┬──────────┘
              ┌──────────▼──────────┐
              │  STEP 2: Contacts   │  Batch upsert by email
              │  /contacts/upsert   │  → contactIdByEmail map
              └──────────┬──────────┘
              ┌──────────▼──────────┐
              │  STEP 3: Deals      │  Batch create (no upsert)
              │  /deals/create      │  → dealIdByDomain map
              └──────────┬──────────┘
              ┌──────────▼──────────┐
              │  STEP 4: Associate  │  Link contacts → companies
              │  v4 associations    │  via HubSpot association API
              └──────────┬──────────┘
                         │
              ┌──────────▼──────────┐
              │  OUTPUT RESULTS     │  One record per lead
              └─────────────────────┘

Data Mapping Pipeline

The mapper transforms lead data into HubSpot properties through several conversions:

Domain normalization -- URLs like https://www.apify.com/ are stripped to apify.com. The domain is used as the company's unique identifier for upsert deduplication.

Name splitting -- Full names like "Jan Curn" are split into firstname: "Jan" and lastname: "Curn". Multi-part last names are preserved (e.g., "Jan van der Berg" → firstname: "Jan", lastname: "van der Berg").

Grade-to-status conversion -- Lead grades from the B2B Lead Qualifier are translated into HubSpot's native lead status values:

Lead GradeHubSpot Lead Status
A, A+OPEN
B, CIN_PROGRESS
D, FUNQUALIFIED

Contact priority -- Contacts from the structured contacts[] array are processed first (they have the most detail: name, title, phone). Standalone emails from the emails[] array and single email field are added afterwards, with email-based deduplication to prevent duplicates.

Deal naming -- Deals are named "Lead: {companyName}" using the company name from the lead data, or derived from the domain if no company name is available (e.g., domain apify.com → "Lead: Apify").

HubSpot Field Mapping Reference

Company properties:

Lead FieldHubSpot PropertyNotes
domain / websitedomainNormalized (www stripped, protocol removed)
companyNamenameFalls back to capitalized domain (e.g., "Apify")
phones[0] / phonephoneFirst phone number
industryindustryDirect mapping
citycityDirect mapping
statestateDirect mapping
countrycountryDirect mapping
zipzipDirect mapping
socials.linkedinlinkedin_company_pageFull LinkedIn URL
socials.facebookfacebook_company_pageFull Facebook URL

Contact properties:

Lead FieldHubSpot PropertyNotes
contacts[].email / emails[]emailLowercased, used as upsert key
contacts[].namefirstname, lastnameSplit at first space
contacts[].titlejobtitleDirect mapping
contacts[].phonephoneDirect mapping
companyNamecompanyCompany name string
domainwebsiteNormalized domain
lifecycleStage inputlifecyclestageFrom actor input parameter
gradehs_lead_statusConverted via grade-to-status table

Deal properties:

Lead FieldHubSpot PropertyNotes
--dealname"Lead: {companyName}"
dealStage inputdealstageFrom actor input parameter
--pipelineAlways "default"
scoreamountLead score as deal amount (for pipeline sorting)

Rate Limiting and Batching

The actor processes records in batches of 100 per API call, with a 150ms delay between batches to stay within HubSpot's free-tier rate limits (100 requests per 10 seconds). If HubSpot returns a 429 rate-limit response, the actor retries with exponential backoff:

RetryWait Time
1st1 second
2nd2 seconds
3rd4 seconds

Authentication failures (401) are not retried. All API requests have a 30-second timeout.

How Much Does It Cost

HubSpot Lead Pusher uses only 256 MB of memory and runs quickly because it makes API calls rather than crawling websites. The main cost factor is the number of leads processed.

ScenarioLeadsEstimated TimeFree Plan (0.5 GB)$49 Plan (100 CUs)
Quick test (dry run)5~5 seconds~14,000 runs/month~2,800,000 runs/month
Small batch25~15 seconds~4,700 runs/month~940,000 runs/month
Medium batch100~45 seconds~1,500 runs/month~300,000 runs/month
Large batch500~3 minutes~300 runs/month~60,000 runs/month

One compute unit (CU) equals 1 GB-hour. The actor uses 256 MB (0.25 GB), so you get approximately four times the runs compared to a 1 GB actor. Actual costs depend on HubSpot API response times and retry frequency.

Tips

  1. Always run a dry run first. The default input has dry-run enabled for a reason. Review the mapped properties in the output dataset to catch any field mapping issues before pushing real data to your CRM.

  2. Use dataset chaining for automated pipelines. Run Website Contact Scraper or B2B Lead Gen Suite first, then pass the output dataset ID to HubSpot Lead Pusher. This lets you build multi-step workflows entirely within Apify.

  3. Set lifecycle stage based on lead quality. If you are using B2B Lead Qualifier upstream, map high-grade leads (A/B) as marketingqualifiedlead or salesqualifiedlead and lower grades as lead or subscriber to keep your HubSpot pipeline organized.

  4. Create deals only for qualified leads. Enable deal creation selectively for high-value prospects. Flooding your pipeline with unqualified deals makes it harder for sales reps to prioritize.

  5. Check HubSpot private app scopes. The most common error is a 401 authentication failure caused by missing scopes. Your private app needs crm.objects.contacts.write, crm.objects.companies.write, and crm.objects.deals.write at minimum. Add crm.objects.contacts.read if you want the token verification step to pass.

  6. Re-run safely without duplicates. The actor uses HubSpot's batch upsert endpoint, which matches companies by domain and contacts by email. Running the same data twice updates existing records rather than creating duplicates.

  7. Use lead score as deal amount. When deals are created, the lead score from B2B Lead Qualifier is mapped to the deal amount field. This lets you sort your HubSpot pipeline by lead quality without additional manual scoring.

Limitations

  • Deals cannot be upserted. HubSpot does not support upsert for deals (no unique key). Running the actor twice with deals enabled will create duplicate deals. Disable createDeals for repeat runs on the same data.
  • No custom property creation. The actor maps to standard HubSpot properties only. If you use custom properties in your CRM, they will not be populated automatically.
  • Company name fallback. When no companyName is provided, the actor derives a name from the domain (e.g., apify.com → "Apify"). This works for most domains but may produce odd names for domains like 123corp.io.
  • Single pipeline only. Deals are created in the "default" pipeline. Custom pipelines require modifying the deal stage to match a stage in your desired pipeline.
  • No contact-to-deal associations. The actor creates contact-to-company associations but does not link contacts or companies to their associated deals.
  • Email-only contacts. Standalone emails from the emails[] field are created as contacts with just an email address and company name -- no first name, last name, or job title.
  • Batch size limit. Each API call handles up to 100 records. Very large datasets (10,000+ leads) may take several minutes due to rate limiting.

Responsible Use

  • Obtain consent before adding contacts. Only push email addresses that were collected with consent or that fall under legitimate business interest. Adding scraped personal emails to a CRM without consent may violate GDPR, CAN-SPAM, or other privacy regulations.
  • Respect HubSpot API limits. The actor includes rate limiting, but running many parallel instances against the same HubSpot account may trigger API restrictions.
  • Test with dry runs. Always preview data before pushing to production CRM instances. Incorrect mappings can pollute your CRM with bad data that is time-consuming to clean up.
  • Keep access tokens secure. HubSpot private app tokens grant write access to your CRM. Never share tokens in public datasets or logs. Use Apify's secret input fields.

FAQ

What HubSpot plan do I need? HubSpot Lead Pusher works with any HubSpot plan, including the free CRM. You need to create a Private App under Settings > Integrations > Private Apps and grant it the required CRM write scopes. Private Apps are available on all HubSpot plans.

What format does the lead data need to be in? The actor accepts a flexible lead format. At minimum, each lead needs a domain or email field. It also recognizes companyName, emails (array), phones (array), contacts (array of objects with name, title, email), socials (object with linkedin, twitter, facebook), score, grade, address, city, state, country, zip, and industry. This format is compatible with the output of Website Contact Scraper, B2B Lead Gen Suite, and Google Maps Lead Enricher.

Will it create duplicate records in HubSpot? No, for companies and contacts. Companies are upserted by domain and contacts are upserted by email address. If a record with the same domain or email already exists in HubSpot, it will be updated with the new data rather than duplicated. Deals, however, are always created as new records because HubSpot does not support deal upsert by a unique key.

What happens if the HubSpot API rate limits my requests? The actor includes built-in rate limit handling. It spaces batch requests 150ms apart to stay within HubSpot's free-tier limits (100 requests per 10 seconds). If a 429 rate limit response is received, the actor waits with exponential backoff (1s, 2s, 4s) and retries up to 3 times before failing.

Can I use this with data from non-Apify sources? Yes. Paste your lead data directly into the "Leads (inline data)" JSON input field. As long as each object has a domain or email field, the actor will process it. You do not need to use an Apify dataset.

How are lead grades converted to HubSpot statuses? The actor translates grades from the B2B Lead Qualifier: grade A or A+ becomes "OPEN" (hot lead), grades B and C become "IN_PROGRESS" (needs nurturing), and grades D and F become "UNQUALIFIED". If no grade is provided, no lead status is set.

What happens if some records fail? The actor processes all records and reports individual results. If a company upsert succeeds but a contact fails, the lead is marked as partial. Only if all operations fail is the lead marked as error. Check the errors array in each output record for specific failure messages.

Integrations

Connect HubSpot Lead Pusher to your existing tools and workflows:

  • Apify Actors -- Chain with Website Contact Scraper to scrape contacts and push them to HubSpot in a single pipeline. Use B2B Lead Gen Suite for comprehensive lead data or Google Maps Lead Enricher for location-based leads.

  • Zapier -- Trigger a Zap when the actor completes to notify your sales team, log results to a spreadsheet, or kick off follow-up sequences in your email tool.

  • Make (Integromat) -- Build automated workflows that run lead scraping actors on a schedule, feed the dataset into HubSpot Lead Pusher, and alert your team via Slack or email when new leads are added to HubSpot.

  • Google Sheets -- Export the actor's output dataset to Google Sheets for a log of every push operation, including which records were created, updated, or errored.

  • API -- Call the actor programmatically via the Apify API. Pass dataset IDs from upstream runs and retrieve push results in JSON format. Ideal for building custom CRM sync pipelines.

  • Webhooks -- Configure Apify webhooks to trigger downstream actions when the push completes, such as notifying Slack, updating a dashboard, or starting a follow-up email sequence.

Related Actors

These actors from ryanclinton on the Apify Store work well with HubSpot Lead Pusher:

ActorWhat It DoesHow It Connects
Website Contact ScraperExtract emails, phones, and team members from business websitesFeed its dataset directly into HubSpot Lead Pusher
B2B Lead Gen SuiteOrchestrate multi-source lead generation with scoringOutput includes scores and grades that map to HubSpot statuses
Google Maps Lead EnricherEnrich Google Maps business listings with contact detailsMaps address data maps to HubSpot company properties
B2B Lead QualifierScore and grade leads based on company signalsGrades (A-F) convert to HubSpot lead statuses automatically
Email Pattern FinderDiscover email patterns and predict addressesFind more contacts to push as HubSpot contacts
Bulk Email VerifierVerify email deliverability before CRM pushClean your email list before pushing to HubSpot

How it works

01

Configure

Set your parameters in the Apify Console or pass them via API.

02

Run

Click Start, trigger via API, webhook, or set up a schedule.

03

Get results

Download as JSON, CSV, or Excel. Integrate with 1,000+ apps.

Use cases

Sales Teams

Build targeted lead lists with verified contact data.

Marketing

Research competitors and identify outreach opportunities.

Data Teams

Automate data collection pipelines with scheduled runs.

Developers

Integrate via REST API or use as an MCP tool in AI workflows.

Ready to try HubSpot Lead Pusher - CRM Contact Import Automation?

Start for free on Apify. No credit card required.

Open on Apify Store