unicorn unicorn

Completely FREE Email Capture Without Limitations

You want to capture people's email addresses, but you don't wanna pay for it.

The popular players in this space charge $100 per month (unbounce) or minimum $300 per year (lead pages). That's way too much for a crazy little side project, even one with big dreams in its eyes.

There's a bunch of 'free' solutions out there, but they come with onerous restrictions: only so many sign-ups a month, you can't access your list of subscribers, you have to put up with other people's crummy branding all over your pristine sign-up form and so on.

After reading the entire internet, I've now settled on the bestest way imaginable to do it for free, and it's completely "serverless" — you don't even need to have your own web server and can host the lead capture page (for free!) in Github pages on your own custom domain.

And best of all, this is a powerful technique you can reuse to store any kind of data.

Why? Why!? Because I hate spending money, but I LOVE YOU.

So here's how it's done.

  • Confidence with html and javascript.
  • A Google drive account.
Technologies covered:
  • javascript (no libraries!)
  • html
  • Google sheets
  • Google script editor
Reading time: Approx. 10 min

It's based entirely on this technique from 'dwyl' (which is in turn inspired by this article from Martin Hawksey)

Here's a diagram of the approach:


The three steps are:

  1. Create a Google sheet that will store your email addresses.
  2. Create a Google script that will receive email addresses and write them to the sheet from 1.
  3. Create a simple page (anywhere!) where people enter their name and email address and configure it to post to the Google script from 2.

Let's get down to business.

1. Create a Google sheet.

Assuming you have an account with Google docs (or Google drive) you can log in and create a new spreadsheet with just a few clicks.


Give it whatever name you like, for example, 'Signups'. Put in a few appropriate column headings. The first column has to be 'timestamp' (if you want to use the example script below) and the next few columns should be the name of the elements you want to capture, for example name and email. Whatever attributes you choose to capture, make a note of them because we'll need to know them down in step 3 when we build a web page.

Also, take note of what you name the first sheet (in this case "responses") — you'll need that for the script below. By default, it's called "Sheet1" and you're welcome to leave it named that.

Now, it's time to build a "Google script" that will write to the Google sheet.

2. Create a Google script.

While looking at your nice new Google sheet, click "Tools" | "Script editor"


Have you ever done that before? I hadn't. This was entirely new territory. I had never even noticed the "script editor" option in Google sheets. I've written my share of Excel macros (the therapy is helping thank you for asking) but until now I've never entered the exciting world of Google scripts, macros, and web apps.

Your initially empty script will look like this:


Because this script was created from a sheet, it is "bound" to that sheet. You can read more about it in container bound scripts.

You don't need to know much about how these things work and can just copy and paste the script I'll provide, changing only one or two things.

But because I suspect you like to think about things, I'll tell you a little about how these Google scripts work:

We only need to write a doPost(e) method, so that (in step 3) our page can HTTP POST the user's name and email to the Google script. (You'll see later). Inside doPost we will receive the data and store it in the spreadsheet.

Once we're done we will be publishing the script so that it is publicly accessible: but our spreadsheet will remain private and secure.

Here's the code for the script:


It's less than 40 lines of code. A single method, doPost(e), that receives a piece of JSON and writes its attributes to a new row in the spreadsheet.

Paste that content into your file at "script.google". Name your script if you wish, but the name doesn't matter at all.


Now, to make it accessible to the outside world you need to publish this script as a "web app". Sounds fancy, and as you'll see, there are a few flaming hoops to jump through on the way.

Go to the "Publish" menu and choose "Deploy as web app..."


You are presented with this little dialog:


You need to pick which 'revision' of your web app you wish to publish. There are no revisions yet, so just choose 'new'. That means you're creating a new revision.

You will execute the app as your own Google account. It might be worth creating a new single-purpose Google account for doing this... if you don't believe me yet, wait until you see the scary dialogs ahead...

You also need to select that this 'web app' can be accessed by 'Anyone, even anonymous'. When I first read that I was worried it meant the spreadsheet itself would be publicly accessible (and thus you'd be leaking your list of people's email addresses with the world!). But don't worry, it just means that the script we've written can be posted to by our public web page. The sheet remains secure.

Go ahead and click 'Deploy.'

Here come the scary warnings. They get worse as we go.

First, they tell us "Authorisation" is required. Because the script will be writing to a sheet you created, it needs your explicit permission.


Now Google gets really worried. It notices that this script is something new and could really do anything at all. (For example, you might have copied and pasted it off some weirdo on the internet like me!) So they want to disclaim any responsibility.


You must click "Advanced" to continue. The 'Advanced' link is practically hidden.


Click on the little gray link that says "Go to yfp-example-submit (unsafe)" — or whatever the name of your web app is. If your web app is called "LOL BBQ" then it will say "Go to LOL BBQ (unsafe)".

Now it tells you all the things that the script will be permitted to do:


The web app will be allowed to do anything it wants with your spreadsheets in Google Sheets.

This is an unfortunate overreach on Google's part. I'd LOVE it if there was an option to say "The only thing this script is permitted to do is write to this one particular sheet."

So, if you want, you can consider creating a whole new Google account separate from your personal one, so that you don't put any of your own spreadsheets at risk.

Anyway, if you trust me, click "Allow" and we're done!

We're now given a URL to use:


For example, above, the URL is:


Hang on to the URL that Google gives you. You'll need it before the end of step 3.

3. The simplest form imaginable.

Now we need to make a simple webpage and stick it somewhere. Anywhere. It will work fine as just a file on your computer (though no one can access it there). I recommend putting it on GitHub and publishing it through Github pages. It's a free system, that allows you to use your own custom domain if you have one.

Here's the content of the webpage itself. The example here is the simplest email collection form imaginable:


There is no styling at all. It's the kind of form only a particularly charitable mother could love.

If you go ahead and apply this technique in the 'real world' you'll need to create a beautiful and enticing form that really blows the customer's sock right off.

And I don't just mean with the cunning selection of fonts, colors, wiggly animations and other CSS shenanigans. You need to work on your 'copy', your words, your powerful "call to action", your tone, your personality, your brand positioning, your unique value proposition... but all of that goes far beyond the scope of this article. Thus, the mega-ugly but very simple form.

Below is the heart of the HTML, you can see it in action here:


<form id="gform" method="POST" action="https://script.google.com/macros/s/A-BIG-LONG-NUMBER/exec">
  <div>name <input id="name" name="name" /></div>
  <div>email <input id="email" name="email" type="email" required ></div>
  <button>Sign up!</button>
<div id="saving_message"><p>Saving...<p></div>
<div id="thankyou_message"><p>Thank you!<p></div>
<script type="text/javascript" src="form-handler.js"></script>

There's an input for a name and an email address, plus a button to click. That's it.

Notice that suspicious looking A-BIG-LONG-NUMBER on the form's target attribute? You'll need to populate yourself, later on (I'll show you how below).

There's also a link to a javascript file, form-handler.js, where all the magic happens.

You can grab the javascript file for yourself here, it's only 70 lines long:


We'll step through it now.


When the button is clicked, we call the function handleFormSubmit which hides the form:

document.getElementById('gform').style.display = 'none';

...and shows a 'saving...' div.

document.getElementById('saving_message').style.display = 'block';

We extract all of the data from the form and pack it into an object, via a handy little function called getFormData.

We post that data, as a JSON string, to the Google script provided as the target of the form. (e.g. https://script.google.com/macros/s/A-BIG-LONG-NUMBER/exec)

When that target script responds, we hide the 'saving...' div and replace it with a 'Thank you' div. (You'll need to customise these saving and thank you divs to suit your message).

But what does this google script do? How does it do it? And where can we get our own?

I'd love to tell you that — and I will shortly — but before we create the google script we have to create the google sheet where our data will end up.

And allegedly we can use that URL as the 'target' of our contact form... except we can't!

For some very good reason that no one can explain to me, we first have to remove the "/u/0" that is just after 'macros'.

I don't know what that is doing in there... just yank that bit out.

If you don't remove that bit, then here's what will happen (in the developer tools console) when the user clicks 'Sign up'


Some kind of "ERR_ABORTED" problem with details about "Access-Control-Allow-Origin"

Here's the full text of the errors:

POST https://script.google.com/macros/u/0/s/AKfycbzxP9YPZJ4Q6AuZVRmy8qVUdd7m-HWB3MwAeglJFVqRNka5iwtY/exec net::ERR_ABORTED
handleFormSubmit @ form-handler.js:66
lead-capture.html:1 Failed to load https://script.google.com/macros/u/0/s/AKfycbzxP9YPZJ4Q6AuZVRmy8qVUdd7m-HWB3MwAeglJFVqRNka5iwtY/exec: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:11682' is therefore not allowed access. The response had HTTP status code 404.

Why!? Why!? Be more constructive with your feedback, please!

Simply remove the "/u/0" and suddenly the form begins to work beautifully! Yay!


<!-- doesn't work -->
<form id="gform" method="POST" action="https://script.google.com/macros/u/0/s/AKfycbzxP9YPZJ4Q6AuZVRmy8qVUdd7m-HWB3MwAeglJFVqRNka5iwtY/exec">


<!-- works fine.... removed the /u/0 -->
<form id="gform" method="POST" action="https://script.google.com/macros/s/AKfycbzxP9YPZJ4Q6AuZVRmy8qVUdd7m-HWB3MwAeglJFVqRNka5iwtY/exec">

Go back to your Google sheet and you can watch the names and email addresses as they pour in! It is a thing of beauty and a source of infinite delight.


money money money

Wrapping up...

And with that, you have a general purpose lead-capture form, you can use again and again on every page you create, over and over, thousands of web pages, no cost at all.


Some further steps to take this to the next level are:

  1. Work out a killer hook to use on your lead capture page and in your call to action (i.e. button text)
  2. Send MailGun emails (10,000 free per month) to people who subscribe.
  3. Customize the styling and branding of the lead capture form (I've heard that big red dorky-looking hand-drawn arrows convert really well)
arrow arrow arrow