How to Programmatically Generate Leads & Write Cold Emails
How many cold emails do you receive every day? A dozen? More than that?
Now, how many do you respond to?
Further, how many cold emails have resulted in you exchanging money with a person or business?
I’m willing to bet you receive quite a few cold emails every day, and you’ve ignored nearly all of them.
Yet, nonetheless, you hear all the time cold email can be one of the best channels for sales across several different industries. Practically every B2B business for that matter.
It’s true, too – amidst all of the noise, cold emailing remains a valuable tool for businesses aiming to make genuine connections, foster partnerships, and drive sales.
The key to success at cold emailing, though?
It’s strategic prospecting and effective outreach – which is easier said than done, of course.
However, with tools like Proxycurl (an API that provides fresh and accurate data on people and companies), accessing detailed insights on potential leads becomes much simpler, allowing for more personalized and impactful cold emails.
That being said, in this guide, we'll be walking you through a step-by-step process to not only automate your prospecting efforts – but also create compelling cold emails that actually convert.
Yes, contrary to what you may believe, cold emailing can even work for you too...
We’ll be showing you the entire process of crafting a successful cold email outreach campaign from start to finish.
And it doesn’t matter if you’re:
- In HR, looking to recruit talent
- A VC firm looking to source deals
- A startup looking to source investment money
- A SaaS company looking to land more clients
- An advertising agency looking to land more deals
- Or beyond…
If you’re looking to contact a specific target audience to complete a desired action (booking a call, selling a product, etc.) cold emailing will work for you.
This guide will equip you with all of the tools and knowledge to make cold emailing a powerful asset in your marketing arsenal.
That said, let’s dive in.
The importance of targeted prospecting
Before diving into the mechanics of cold emailing, it's crucial to understand the significance of targeted prospecting.
Sending emails en masse without proper targeting is a recipe for low open rates and even lower conversion rates.
While the temptation might exist to cast a wide net with generic emails, this approach often leads to wasted efforts and missed opportunities.
Templated emails, devoid of any personal touch, often come across as spam and are easily dismissed by prospects – in other words, they don’t work.
Moreover, broad targeting, where emails are sent to a vast audience without consideration of their specific needs or interests, further dilutes the impact of the cold email.
When prospects feel that an email isn't tailored to them or doesn't address their unique challenges and aspirations, they’re more likely to ignore it.
In essence, the success of cold emailing hinges on the balance of precise targeting and personalization.
By ensuring that each email speaks directly to the recipient's needs, you can elevate your cold emailing efforts from mere shots in the dark to strategic, impactful communications.
Automating prospecting with Proxycurl
Searching based on qualifying characteristics
Proxycurl's API is a game-changer for businesses looking to automate their prospecting efforts.
Using the Person Search Endpoint and Person Profile Endpoint, we can automatically search for prospects that meet certain criteria (falling within your ICP), and then enrich the results, and finally export the results into a .CSV.
For example, let’s say we run a real estate showing/booking SaaS, and we want to start conducting a cold email campaign to win over more clients.
The first step is building a list of relevant, and accurate leads – clearly, we don’t want to do this by hand (that takes forever, and there are more profitable activities you and your sales team can spend your time on).
That said, here’s how you can automate your entire lead generation process with a bit of Python and Proxycurl:
import requests
import csv
# Define the API key and headers
api_key = 'Your_API_Key_Here'
headers = {'Authorization': 'Bearer ' + api_key}
# Define the search endpoint and parameters
search_endpoint = 'https://nubela.co/proxycurl/api/search/person/'
search_params = {
'country': 'US',
'current_company_industry': '(?i)real estate',
'current_role_title': '(?i)real estate agent',
'page_size': '100' # To get 100 profiles
}
# Define the profile endpoint
profile_endpoint = 'https://nubela.co/proxycurl/api/v2/linkedin'
# Fetch the LinkedIn profiles using the search endpoint
response = requests.get(search_endpoint, params=search_params, headers=headers)
profiles = response.json().get('results', [])
# Enrich each profile using the profile endpoint
enriched_profiles = []
for profile in profiles:
linkedin_url = profile.get('linkedin_profile_url')
if linkedin_url:
response = requests.get(profile_endpoint, params={'linkedin_profile_url': linkedin_url, 'personal_email': 'include'}, headers=headers)
if response.status_code == 200:
enriched_data = response.json()
enriched_profiles.append(enriched_data)
else:
print(f"Error enriching profile {linkedin_url}: {response.text}")
# Export the enriched profiles to a CSV
with open('enriched_real_estate_agents.csv', 'w', newline='', encoding='utf-8') as csvfile:
writer = csv.writer(csvfile)
# Define the headers based on the fields you want to export
headers = ['Full Name', 'LinkedIn URL', 'Occupation', 'Headline', 'Summary', 'Country', 'City', 'State', 'Experiences', 'Education', 'Languages', 'Inferred Salary', 'Gender', 'Industry', 'Interests', 'Personal Emails']
writer.writerow(headers)
for profile in enriched_profiles:
writer.writerow([
profile.get('full_name'),
profile.get('linkedin_profile_url'),
profile.get('occupation'),
profile.get('headline'),
profile.get('summary'),
profile.get('country_full_name'),
profile.get('city'),
profile.get('state'),
", ".join([(exp.get('title') or '') + " at " + (exp.get('company') or '') for exp in profile.get('experiences', [])]),
", ".join([(edu.get('school') or '') + " - " + (edu.get('degree') or '') for edu in profile.get('education', [])]),
", ".join(profile.get('languages', [])),
(profile.get('inferred_salary') or {}).get('range'),
profile.get('gender'),
profile.get('industry'),
", ".join(profile.get('interests', [])),
", ".join(profile.get('personal_emails', []))
])
print(f"Data exported to enriched_real_estate_agents.csv")
The above searches for a list of 100 real estate agents, then automatically enriches those results, the list of 100 real estate agents, and exports it into a file named “enriched_real_estate_agents.CSV” – by the way, I’ve intentionally limited it to 100 different agents because this will return thousands of results:
You can get more specific with it, too, if you’d like. For example, we could also search by education, skills, current company, and beyond.
This will allow you to build different prospecting lists based on different target audiences, way beyond just real estate agents, all you need to do is change current_role_title
.
Ultimately if there is a job title out there, you can target it, and you can further refine and filter your prospecting results with our various parameters available.
Now, let’s change the scenario and say you already have a list of social media profiles, and you’d like to get a list of email addresses from them.
Extracting email addresses from any given social media profile URL
Using our Personal Email Lookup Endpoint, we can take a given social media profile URL and extract the email address from it.
It’ll work with any Twitter (X), Facebook, or LinkedIn profile URL you give it, returning you the email address.
This endpoint also has an email_validation
parameter that comes in handy here so that we can ensure we’re only sending to verified emails and aren’t endangering our emailing account.
Again using a bit of Python, here’s an example of extracting the email address from a social media profile:
import requests
import csv
import json
api_key = 'Your_API_Key_Here' # Replace with your actual API key
headers = {'Authorization': 'Bearer ' + api_key}
api_endpoint = 'https://nubela.co/proxycurl/api/contact-api/personal-email'
params = {
'email_validation': 'include',
'page_size': '0',
}
# Create or overwrite the output CSV file
with open('output_responses.csv', 'w', newline='') as output_file:
fieldnames = ['linkedin_url', 'valid_emails', 'invalid_emails'] # Define your desired output columns
writer = csv.DictWriter(output_file, fieldnames=fieldnames)
writer.writeheader()
# Open the input CSV file and read the LinkedIn URLs
with open('linkedin_urls.csv', 'r') as input_file:
reader = csv.DictReader(input_file)
for row in reader:
linkedin_url = row['linkedin_profile'] # Adjust this based on your CSV's column name
params['linkedin_profile_url'] = linkedin_url
response = requests.get(api_endpoint, params=params, headers=headers)
# Parse the JSON response
data = json.loads(response.text)
valid_emails = ', '.join(data.get('emails', []))
invalid_emails = ', '.join(data.get('invalid_emails', []))
# Write the LinkedIn URL and its corresponding response to the output CSV
writer.writerow({
'linkedin_url': linkedin_url,
'valid_emails': valid_emails,
'invalid_emails': invalid_emails
})
print("Export completed!")
What that does is take a file named “linkedin_urls.csv” (in the same folder as your Python script), with only one column, linkedin_url at the top, and then a list of LinkedIn profiles below it.
Then, it goes through the list extracting all of the valid emails into a file named “output_responses.csv”, filtering out all invalid email addresses:
Not bad, huh?
That means if you can find their Twitter (X), Facebook, or LinkedIn – you can likely send them cold emails.
I should also mention if you’d like to look up a work email address, you can do that with our Work Email Lookup Endpoint, but it works slightly differently and only works with LinkedIn profile URLs.
Now, let me throw in a freebie…
How to validate any email address for free
Using our Disposable Email Address Check Endpoint, you can check if any email address belongs to a free or disposable email service.
In other words, you can validate if there are any worthless email addresses on any of your prospecting lists before you send – and this endpoint is entirely free.
Here’s an example of how you could use some Python with it to automatically validate a list of email addresses:
import requests
import csv
api_key = 'YOUR_API_KEY' # Replace with your actual API key
headers = {'Authorization': 'Bearer ' + api_key}
api_endpoint = 'https://nubela.co/proxycurl/api/disposable-email'
valid_emails = []
free_emails = []
disposable_emails = []
# Open the input CSV file and read the emails
with open('emails_list.csv', 'r') as input_file:
reader = csv.DictReader(input_file)
for row in reader:
email = row['email']
params = {'email': email}
response = requests.get(api_endpoint, params=params, headers=headers)
data = response.json()
# Handle rate limit error
if 'error' in data:
print(f"Error for {email}: {data['error']}")
continue
# Categorize the email
if data.get('is_disposable_email') == True:
disposable_emails.append(email)
elif data.get('is_free_email') == True:
free_emails.append(email)
else:
valid_emails.append(email)
# Write the emails to a new CSV file in separate columns
with open('categorized_emails.csv', 'w', newline='') as output_file:
writer = csv.writer(output_file)
writer.writerow(['Valid Emails', 'Free Emails', 'Disposable Emails']) # Write the headers
# Write emails row by row
max_rows = max(len(valid_emails), len(free_emails), len(disposable_emails))
for i in range(max_rows):
valid_email = valid_emails[i] if i < len(valid_emails) else ''
free_email = free_emails[i] if i < len(free_emails) else ''
disposable_email = disposable_emails[i] if i < len(disposable_emails) else ''
writer.writerow([valid_email, free_email, disposable_email])
print("Export of categorized emails completed!")
That’ll take a list of emails within “emails_list.csv” (in the same folder as your Python script) and automatically validate them for you, placing them in one of three columns:
- Valid emails
- Free emails
- Disposable emails
Again, entirely for free – there’s no reason to not be doing this before you ever send out any cold emails.
Okay, now that we’ve covered:
- Searching and building prospecting lists based on specific parameters
- Building prospecting lists with social media profiles
- Validating all of your email addresses
It’s time to move on to the fun part.
Crafting the perfect cold email
With a list of prospects in hand, it's time to write your cold email.
First things first: Your email should be concise, relevant, and provide clear value to the recipient.
Furthermore, since we’ve enriched our contacts, we can use the data from Proxycurl to address the recipient by name and reference specific details about their company or role (also known as personalization).
Let’s start with an example of a bad cold email:
Subject: Hey! Check This Out!!!
Hey there *name*,
I'm just reaching out because I wanted to tell you about this thing called Proxycurl. It’s some kind of API or something? Anyway, I think it's supposed to help with data insights or whatever that means. I'm not really sure how it works, but I think you should totally check it out. Everyone's using it, I guess.
So, yeah, if you're into that kind of thing, maybe give it a look? Or don't. Whatever.
Catch you later,
Colton
P.S. I forgot to mention, but it's also good for sales and marketing teams, I think? Not sure. Anyway, bye!
I’ve made it comically bad intentionally, but you get the point.
This is literally how most cold emails sound:
- Un-personal, bulk, and literally emotionally cold
- Vague
- Lack of value proposition
- No clear problem/solution fit
- No reason for them to take you seriously or respond to your email
Which is exactly why they don’t work.
Now, take a look at this cold email:
Hey *name*,
I took a look over your SaaS, and according to your blog post, it seems you’ve attempted cold emailing in the past and failed.
I work for Proxycurl, and we’re an API that provides data on people and companies – many people use us for cold email prospecting.
Do you have 15 minutes of your time to spare for me to show you how you could turn cold emailing from a flop into one of your highest-performing growth channels?
Look forward to hearing back from you,
Colton
P.S. I’m actually an alumnus from *college name* too – small world haha.
See the difference?
The clarity and value proposition is entirely different, and it presents a clear solution to an obvious problem.
It also doesn’t try to sell Proxycurl on the email. It only sells a call, and focuses first on conveying that we can provide value to them – and as concisely as possible.
Of course, the name of the game is all about automation, so instead of doing things the boring, time-consuming, and energy-draining way – aka, by hand – we can automate this process nearly entirely by using LLMs like ChatGPT.
Automate writing your cold emails with ChatGPT
In order to automate this with ChatGPT, we need to craft a prompt that works for your individual business.
Rather than using Proxycurl, I’ll use our real estate showing/booking SaaS example from earlier.
We could use something like this:
My name is Colton from RealEstateBooker. Our software as a service tool makes it much easier for real estate agents to book showings automatically. I want you to write a cold email that's a paragraph or less to the following prospect:
Name: Cassidy Jones
Location: Houston, Texas
Company: Berkshire Hathaway
Education: The University of Texas
Industry: Real estate
Job role: Real estate agent
The goal is to get a call booked. Be as concise as possible and prevent a clear problem/solution fit. The wording should be friendly and conversational. Don't use buzzwords, and use all of the data points provided to personalize the email and help it stand out.
Using the above prompt, here’s what ChatGPT came up with:
Nice. Plus, if you wanted to take this a step further, you could automate more of this using ChatGPT’s API.
This is all possible from our enrichment step earlier, because now you’ll have data points like:
- Full name
- Education
- Summary of the individual
- Current job role
- Current location
- And beyond
So you can automatically take these enriched variables and feed them to ChatGPT to improve the level of personalization in your email and, thus, the response.
You could personalize thousands of emails at once that way.
However, we just published an in-depth article about that here, so I won’t go into that any further in this article.
The main point here, though, is that automation doesn’t have to mean impersonal.
You can use Proxycurl to gather detailed insights about your prospects and use that data to craft personalized outreach emails – but make sure to keep them concise and relevant.
Your cold emails should either directly provide value, or present an obvious solution to a major problem that the prospect is experiencing.
Now for the next step in the process...
What’s the best cold emailing tool?
Over here at Proxycurl, we use Sendy.
We also already have an entire article about our cold emailing setup here that’s worth a read.
But, other than that, I wouldn’t put too much focus on the cold emailing tool. They all more or less do the same thing.
In our case, Sendy allowed us to self-host our emailing tool and use an SMTP relay to send out our emails (AWS SES).
That was the best of both worlds because it enabled us to keep our costs down and our deliverability high, which is the most important part of the equation.
Speaking of deliverability, here are a couple of best practices to get and keep you in the inbox folder instead of the spam folder:
- Make sure you’re using a different domain to email from, you don’t want your main domain being flagged by the likes of Gmail, etc
- Warm up your cold email domain for a week or two prior, there are plenty of solutions out there to do that for you – you shouldn’t immediately go straight to blasting out thousands of emails on a new domain
- Use a relay with well-guarded IPs like AWS SES, don’t try to send out your own cold emails with your own IPs, it’s not worth the hassle
- Keep an eye out for complaint rates, if they’re excessively high you can be kicked off any SMTP relay, AWS SES included, if you have an excessively high complaint rate it’s because your cold emails look spammy and aren’t personalized enough
Cold emailing only works if your emails are getting read, and if they’re ending up in the spam folder it’s very unlikely they’re getting read.
The world's best copy is useless if the email isn’t opened in the first place, so make sure you’re keeping an eye on deliverability, but the tool alone doesn’t inherently matter.
Practically all of them will allow you to build automated sequences nowadays, which is all we need here.
Navigating common cold emailing challenges and objections
Things aren’t over once you start sending cold emails. In fact, things are really just starting.
Once you've started cold emailing, keep the following three things in mind:
1) Keep following up
You’ll find out many people who do convert don’t respond immediately.
Your prospects are busy people, and even though they might act right away, there’s a very big chance they simply won’t get around to fully digesting your message and value proposition the first time around.
That’s where following up comes in.
Don’t be afraid to throw a couple of emails in a sequence, in fact, if you aren’t, you’re doing it wrong.
Just make sure you provide the ability to unsubscribe from your sequence.
2) Usually, you aren’t right the first time
Be comfortable with testing new things, and be prepared to fail.
As I mentioned above, cold emailing is all about reiteration – you should expect to be wrong, but it takes testing, learning, and reiterating to perfect any skill, cold emailing included.
3) You might make a couple of people angry
Without a doubt, you will receive a couple of negative responses to your emails.
That’s understandable and is to be expected.
Of course, the more value-driven and personalized the cold email is, the less likely you are to receive a negative response.
But, nonetheless, it’ll happen… and you’ll need to expect it.
The criticism is usually fair too (not always), and if you actually read and understand the criticism, you can usually take it and use it to make your cold email copy better.
The next steps:
Whew, that was a lot.
First, to put all of this into practice, you'll need to click here and create your Proxycurl account.
Proxycurl is entirely self-service – you can get started using us to build your prospecting lists (and enrich your data) immediately.
Plus, you can get started with as little as $10 for 100 credits, or free if you're just looking to validate your emails.
From that point forward, you have a couple of different options...
Technically, you could use the scripts provided here, along with the scripts provided here to build a custom in-house solution and automate nearly everything involved in the cold emailing process including:
- Searching for leads within your ICP
- Building cold emailing lists
- Creating cold emails at scale with the enriched data provided and ChatGPT
- Sending cold emails
Or, if you're a small business, you could keep things simple by using the provided Python prospecting script above, and then copy and paste into ChatGPT using the example prompt provided above, and finally load them up into a simple solution like Sendy.
Either way, with Proxycurl there are a ton of different options – and it'll make your cold emailing process infinitely easier.
You can click here to get started and create your Proxycurl account today.
Thanks for reading, and keep on cold emailing!
P.S. If you have any questions, don't be afraid to reach out to us at "hello@nubela.co" – we promise we don't bite.