Easily sending postcards to your Kickstarter backers with Lob

Recently my friend Joël Franusic was stressing out about sending postcards to his Kickstarter backers and asked me to help him out. He pointed me to the excellent service Lob.com, which is a very developer-friendly API around printing and mailing. We quickly had some code up and running that could take a CSV export of Kickstarter campaign backers, verify addresses, and trigger the sending of customizable, actual physical postcards to the backers.

Screen Shot 2014-04-14 at 2.24.30 PM

We wanted to share the project such that it could help out other Kickstarter campaigns, so we put it on Github: https://github.com/mrooney/kickstarter-lob.

Below I explain how to install and use this script to use Lob to send postcards to your Kickstarter backers. The section after that explains how the script works in detail.

Using the script to mail postcards to your backers

First, you’ll need to sign up for a free account at Lob.com, then grab your “Test API Key” from the “Account Details” section of your account page. At this point you can use your sandbox API key to test away free of charge and view images of any resulting postcards. Once you are happy with everything, you can plug in credit card details and start using your “Live” API key. Second, you’ll need an export from Kickstarter for the backers you wish to send postcards to.

Now you’ll want to grab the kickstarter-lob code and get it set.

These instructions assume that you’re using a POSIX compatible operating system like Mac OS X or Linux. If you’re using Mac OS X, open the “Terminal” program and type the commands below into it to get started:

git clone https://github.com/mrooney/kickstarter-lob.git
cd kickstarter-lob
sudo easy_install pip # (if you don’t have pip installed already)
pip install -r requirements.txt
cp config_example.json config.json
open config.json

At this point, you should have a text editor open with the configuration information. Plug in the correct details, making sure to maintain quotes around the values. You’ll need to provide a few things besides an API key:

  • A URL of an image or PDF to be used for the front of the postcard.
    This means that you need to have your PDF available online somewhere. I suggest using Amazon’s S3 service to host your PDF.

  • A message to be printed on the back of the postcard (the address of the receiver will automatically show up here as well).

  • Your return address.

Now you are ready to give it a whirl. Run it like so. Make sure you include the filename for your Kickstarter export:

$ python kslob.py ~/Downloads/your-kickstarter-backer-report.csv
Fetching list of any postcards already sent...
Verifying addresses of backers...
warning: address verification failed for jsmith@example.com, cannot send to this backer.
Already sent postcards to 0 of 161 backers
Send to 160 unsent backers now? [y/N]: y
Postcard sent to Jeff Bezos! (psc_45df20c2ade155a9)
Postcard sent to Tim Cook! (psc_dcbf89cd1e46c488)
...
Successfully sent to 160 backers with 0 failures

The script will verify all addresses, and importantly, only send to addresses not already sent to. The script queries Lob to keep track of who you’ve already sent a postcard to; this important feature allows you to download new Kickstarter exports as people fill in or update their addresses. After downloading a new export from Kickstarter, just run the script against the new export, and the script will only send postcards to the new addresses.

Before anything actually happens, you’ll notice that you’re informed of how many addresses have not yet received postcards and prompted to send them or not, so you can feel assured it is sending only as many postcards as you expect.

If you were to run it again immediately, you’d see something like this:

$ python kslob.py ~/Downloads/your-kickstarter-backer-report.csv
 Fetching list of any postcards already sent...
 Verifying addresses of backers...
 warning: address verification failed for jsmith@example.com, cannot send to this backer.
 Already sent postcards to 160 of 161 backers
 SUCCESS: All backers with verified addresses have been processed, you're done!

After previewing your sandbox postcards on Lob’s website, you can plug in your live API key in the config.json file and send real postcards at reasonable rates.

How the script works

This section explains how the script actually works. If all you wanted to do is send postcards to your Kickstarter backers, then you can stop reading now. Otherwise, read on!

Before you get started, take a quick look at the “kslob.py” file on GitHub: https://github.com/mrooney/kickstarter-lob/blob/master/kslob.py

We start by importing four Python libraries: “csv”, “json”, “lob”, and “sys”. Of those four libraries, “lob” is the only one that isn’t part of Python’s standard library. The “lob” library is installed by using the “pip install -r requirements.txt” command I suggest using above. You can also install “lob-python” using pip or easy_install.

#!/usr/bin/env python
import csv
import json
import lob
import sys

Next we define one class named “ParseKickstarterAddresses” and two functions “addr_identifier” and “kickstarter_dict_to_lob_dict”

“ParseKickstarterAddresses” is the code that reads in the backer report from Kickstarter and turns it into an array of Python dictionaries.

class ParseKickstarterAddresses:
   def __init__(self, filename):
       self.items = []
       with open(filename, 'r') as csvfile:
           reader = csv.DictReader(csvfile)
           for row in reader:
               self.items.append(row)

The “addr_identifier” function takes an address and turns it into a unique identifier, allowing us to avoid sending duplicate postcards to backers.

def addr_identifier(addr):
   return u"{name}|{address_line1}|{address_line2}|{address_city}|{address_state}|{address_zip}|{address_country}".format(**addr).upper()

The “kickstarter_dict_to_lob_dict” function takes a Python dictionary and turns it into a dictionary we can give to Lob as an argument.

def kickstarter_dict_to_lob_dict(dictionary):
   ks_to_lob = {'Shipping Name': 'name',
                'Shipping Address 1': 'address_line1',
                'Shipping Address 2': 'address_line2',
                'Shipping City': 'address_city',
                'Shipping State': 'address_state',
                'Shipping Postal Code': 'address_zip',
                'Shipping Country': 'address_country'}
   address_dict = {}
   for key in ks_to_lob.keys():
       address_dict[ks_to_lob[key]] = dictionary[key]
   return address_dict

The “main” function is where the majority of the logic for our script resides. Let’s cover that in more detail.

We start by reading in the name of the Kickstarter backer export file. Loading our configuration file (“config.json”) and then configuring Lob with the Lob API key from the configuration file:

def main():
   filename = sys.argv[1]
   config = json.load(open("config.json"))
   lob.api_key = config['api_key']

Next we query Lob for the list of postcards that have already been sent. You’ll notice that the “processed_addrs” variable is a Python “set”, if you haven’t used a set in Python before, a set is sort of like an array that doesn’t allow duplicates. We only fetch 100 results from Lob at a time, and use a “while” loop to make sure that we get all of the results.

print("Fetching list of any postcards already sent...")
processed_addrs = set()
postcards = []
postcards_result = lob.Postcard.list(count=100)
while len(postcards_result):
    postcards.extend(postcards_result)
    postcards_result = lob.Postcard.list(count=100, offset=len(postcards))

One we fetch all of the postcards, we print out how many were found:

print("...found {} previously sent postcards.".format(len(postcards)))

Then we iterate through all of our results and add them to the “processed_addrs” set. Note the use of the “addr_identifier” function, which turns each address dictionary into a string that uniquely identifies that address.

for processed in postcards:
    identifier = addr_identifier(processed.to.to_dict())
    processed_addrs.add(identifier)

Next we set up a bunch of variables that will be used later on, variables with configuration information for the postcards that Lob will send, the addresses from the Kickstarter backers export file, and variables to keep track of who we’ve sent postcards to and who we still need to send postcards to.

postcard_from_address = config['postcard_from_address']
postcard_message = config['postcard_message']
postcard_front = config['postcard_front']
postcard_name = config['postcard_name']
addresses = ParseKickstarterAddresses(filename)
to_send = []
already_sent = []

At this point, we’re ready to start validating addresses, the code below loops over every line in the Kickstarter backers export file and uses Lob to see if the address is valid.

print("Verifying addresses of backers...")
for line in addresses.items:
    to_person = line['Shipping Name']
    to_address = kickstarter_dict_to_lob_dict(line)
    try:
        to_name = to_address['name']
        to_address = lob.AddressVerify.verify(**to_address).to_dict()['address']
        to_address['name'] = to_name
    except lob.exceptions.LobError:
        msg = 'warning: address verification failed for {}, cannot send to this backer.'
        print(msg.format(line['Email']))
        continue

If the address is indeed valid, we check to see if we’ve already sent a postcard to that address. If so, the address is added to the list of addresses we’ve “already_sent” postcards to. Otherwise, it’s added to the list of address we still need “to_send” postcards to.

if addr_identifier(to_address) in processed_addrs:
    already_sent.append(to_address)
else:
    to_send.append(to_address)

Next we print out the number of backers we’ve already sent postcards to and check to see if we need to send postcards to anybody, exiting if we don’t need to send postcards to anybody.

nbackers = len(addresses.items)
print("Already sent postcards to {} of {} backers".format(len(already_sent), nbackers))
if not to_send:
    print("SUCCESS: All backers with verified addresses have been processed, you're done!")
    return

Finally, if we do need to send one or more postcards, we tell the user how many postcards will be mailed and then ask them to confirm that those postcards should be mailed:

query = "Send to {} unsent backers now? [y/N]: ".format(len(to_send), nbackers)
if raw_input(query).lower() == "y":
    successes = failures = 0

If the user enters “Y” or “y”, then we start sending postcards. The call to Lob is wrapped in a “try/except” block. We handle calls to the Lob library that return a “LobError” exception, counting those calls as a “failure”. Other exceptions are not handled and will result in the script exciting with that exception.

for to_address in to_send:
    try:
        rv = lob.Postcard.create(to=to_address, name=postcard_name, from_address=postcard_from_address, front=postcard_front, message=postcard_message)
        print("Postcard sent to {}! ({})".format(to_address['name'], rv.id))
        successes += 1
    except lob.exceptions.LobError:
        msg = 'Error: Failed to send postcard to Lob.com'
        print("{} for {}".format(msg, to_address['name']))
        failures += 1

Lastly, we print a message indicating how many messages were sent and how many failures we had.

    print("Successfully sent to {} backers with {} failures".format(successes, failures))
else:

(If the user pressed a key other than “Y” or “y”, this is the message that they’ll see)

print("Okay, not sending to unsent backers.")

And there you have it, a short script that uses Lob to send postcards to your Kickstarter backers, with code to only send one postcard per address, that gracefully handles errors from Lob.

I hope that you’ve found this useful! Please let us know of any issues you encounter on Github, or send pull requests adding exciting new features. Most importantly, enjoy easily bringing smiles to your backers!

Freelancing: 2 Years of Learning and Evolution

Last year was my second year as a freelance programmer, and I’d like to continue my habit of retrospectives, so here’s another. My primary work philosophy has been to figure out how much I need to earn each month, and then work as part-time as possible to earn that amount.

2012 was a sort of proof-of-concept of that strategy, and demonstrated success in the various aspects of that: I was able to accurately predict how much money I’d need, I was able to land gigs to earn that money, and I greatly enjoyed all the extra free time I had as a result.

Based on the success of 2012, I felt very comfortable in 2013 to dial back my hours even more. Here’s what my monthly hours looked like over the past two years:

2012 (the left half) was erratic while in 2013 (the right half) I worked less, but more consistently. In 2012 I averaged about 13 hours per week, while last year I averaged about 8. What is most interesting is that I was able to reduce my weekly hours by 62% while only reducing my annual income by 12%. That’s because last year I was able to increase my effective hourly rate by about 53% compared to 2012.

I didn’t change the hourly rate I advertise to clients, so how did I increase my effective hourly rate?

  1. Preferring project-based contracts to hourly contracts. These allow me to quote a fixed price, and the faster I get the project done, the more per hour I make. Having had good experience in 2012, I felt confident enough in my estimates to push for more project-based contracts in 2013, which have proven to be much more profitable.
  2. Recurring income. I’m now covering 26% of my monthly budget with recurring payments for hosting and support, whereas I ended 2012 at around 15%. I’ve previously spent some time making sure my hosting and deployment is unified and simple, so while I almost doubled my recurring income year over year, I certainly didn’t have to double the amount of work I have to do each month to keep all the sites up and running well.

It’s also nice to see that my client base is becoming more diversified and that I’m relying less on any particular client as an income source:

  • Number of clients invoiced. 2012: 6, 2013: 9
  • Per-client average. 2012: $12,595, 2013: $7,418

For 2014, I expect I’ll continue working around 8 hours per week, and focus on building Django apps from scratch, which are my favorite projects and lead to recurring revenue. I’ll also continue working on personal incubation projects like BatchedInbox; I’d love to be partially supporting myself with those. Definitely let me know what your contracting or salaried experience has taught you, and if you’ve got any questions or suggestions!

 

Finally, wxBanker 0.9!

Two years since the last minor release of wxBanker, I finally made it a priority to release the next version of wxBanker, 0.9. If you aren’t familiar with it, wxBanker is a personal finance application in the Ubuntu repository, designed to be as simple as possible. Here’s my favorite review in the Ubuntu Software Center:

By far the best money manager I’ve ever used, on Linux or otherwise. I searched long and hard for a simple account tracker for several years until coming across wxBanker. Its interface is clean and easy to use. It supports anything you would expect in a typical money manager: recurring transactions, import/export, account transfers, tagging and many other useful features.

So, what’s new in the latest version?

  • Mint.com integration restored
  • 16 new currencies based on user requests
  • bug fixes: rare startup crashes, CSV export with Unicode characters
  • initial step towards multi-currency: accounts can specify their own currency
  • under the hood work for the beginnings of OSX support
  • minor UX and translation improvements

Now that these are out of the way, I’d love to work on 1.0 and make the following improvements based on what I’ve been running to:

  • ability to archive accounts (their transactions will still exist and show up in graphs, but the accounts won’t show up in the left-side or transfer list)
  • when making a transfer, accounts remember the last account they transferred from/to
  • show the last few Mint.com transactions when hovering or clicking the status
  • add a “yellow” Mint.com status which means a recent balance matches Mint, but you have new transactions which Mint doesn’t know about (yet)
  • better representation of transactions dated in the future

I’d love to hear if you use anything to track your personal finances, and if you have any questions or thoughts about wxBanker!

What Do You Do In Your Spare Time?

If conversation leads into my part-time work situation, the most common question asked is how I spend the rest of my time. Sometimes this is from a perspective of the asker being legitimately unsure of what they themselves would do with an extra 20-30 hours each week, and other times it is to compare values and priorities. I figured I might as well make a post on it, as much for my own introspection as explanation.

First, some quick background information: after working about 20 hours per week in my first year freelancing (2012), I decided I really enjoyed having more free time during the day, and that I’d prefer to work even less and just learn to live on a smaller budget. So in 2013, I’ve been working about 10 hours per week and haven’t had any problems living happily within the budget this creates.

Okay, let’s get down to numbers. The standard work-week in the US is 40 hours, so if I work 10 hours per week, I’ve got 30 hours to account for:

  • first, I get an extra hour of sleep each day. I was generally a bit sleep-deprived and understand this is common, so this is a healthy use of 5 hours per week. Some may consider this silly, but feeling tired sucks and not getting enough sleep is bad for your health, so I consider it quite a useful time investment. 25 hours to go.
  • I try to get 60 minutes of exercise each day, in various forms such as walking, bicycling, yoga, and rock climbing. That’s another 5 hours a week, 20 left.
  • cooking is important to me, and I quite enjoy making a nice breakfast and lunch most days. If each adds around 45 minutes to prepare and eat peacefully, that’s 90 minutes a day or 7.5 hours a week. 12.5 remaining!
  • meditation is also important to me, and I allocate another 60 minutes a day to this. 7.5…
  • I never found much time to read when working full-time, but now I read at least 30 minutes more a day, or 2.5 hours more a week. 5 to go.
  • it is quite common for people to check their personal emails and catch up on news or social media at work, but as a freelancer, I don’t get paid for this. Regardless, it is still something I spend an hour on each day, even if it isn’t truly an hour gained. That’s the rest of them!

So there’s an example of how the math works out for me. I could have just said “I enjoy sleeping in, exercising, cooking, meditation, and reading”, but I think this makes it a lot more understandable.

These are just recurring activities, and there are tons of other things to do during the week! Examples of things I’ve done include building chicken coops, volunteering at farms, cooking lunch weekly at a Zen temple (even living at one for a month), and meeting up with friends and family who happen to be visiting wherever I’m living. These are just some things enjoy; I’m sure you’d find plenty of your own.

I try to keep things pretty flexible and I don’t follow an exact schedule. I had spent the past 18 months or so trying to work a couple hours every day, but recently I’ve found that I really enjoy working 3-4 hours each Monday, Wednesday, Friday, and having 4 days off per week. The added benefit of not working two days in a row is also great!

An important note is that these aren’t things I initially settled on and fell into right away. When I first found myself with an abundance of time, I fell into my previous habits of filling a lot of it with TV and video games, the things I did to “wind down” after work. This is likely unavoidable for a lot of people such as myself, and is perhaps a phase one just needs to exhaust. To get a little off-topic (but that’s okay because I’m done), this is also why taking 2 week (aka too weak) vacations from a full-time job is unlikely to let you discover anything particularly useful about yourself, and may even leave you thinking like I’ve thought before, “I’m bored, I don’t know what to do with all this time, I better go back to work!” Resist this feeling with all your willpower!

Okay, I’d love to hear what you do in your spare time, what you wish you had more time for, and any schedule changes you’ve made, considered, or rejected, to accommodate for such things.

Visualizing BatchedInbox Users

Batchedinbox.com is a free service I created that allows Gmail users to only have emails delivered at certain times throughout the day, instead of being distracted regularly. It, in one user’s words, “cures inbox addiction.”

For some fortuitous reason, the number of daily active batchers has been growing slowly but steadily over the past few weeks. Most of the traffic is direct, so I’m suspecting and hoping that users themselves are recommending the service to others. Anyway, now I’ve got (just) enough data to validate having fun generating a few anonymous graphs.

First, I was interested to see which delivery options people preferred. Batchers can choose to have emails delivered at one, two, or three specific times daily (at 8am, 12pm, and 5pm for example), or hourly on the hour:

The majority of users have configured their email to arrive at three specific times throughout the day, while another quarter receive emails every hour. I was quite surprised to see that deliveries only once daily (my personal setting) was actually more popular than twice-daily deliveries. Neat!

Second, I was curious how many users are @gmail.com, versus other domains:

I was surprised to see that almost a third of users are using custom domains. Of the non-Gmail domains, all were unique.

Random final facts include that the oldest active Batcher (who isn’t me) has been batching for about 6 months, while the median active user age is only 16 days, demonstrating the recent growth.

Is there anything else interesting I should graph? Do you have any questions about BatchedInbox?

Hosting Multiple Websites with the Same Django Project and Processes

I recently completed a website for an organization, and they wanted two more with very similar functionality but different branding. I thought it might make sense to fork the initial repository and maintain and host them as different sites, but this duplicated all the code and required more memory (about 100MB each) and maintenance. I also considered refactoring the common functionality out into a reusable app, but this would require a fair amount of work, and still require more memory and operations maintenance.

As such, I looked into a solution that could meet the following requirements:

  • different domains end up at different Django sub-app’s URLs
  • a single codebase to maintain
  • served by the same gunicorn workers, requiring less memory and processes to maintain

I came across some good solutions, but they involve having separate settings files for each project and having a set of gunicorn processes for each. If that’s what you want, those solutions would be great.

In my case, I just wanted to create a few django sub-applications and have different domains go to the appropriate URLs. Here’s the approach I took.

1. Create a sub-application for each branded version.

This is as simple as running “manage.py startapp”, and creating the URLs, views, models, and such that you want for each app.

For templates, I created a base template for each application that override appropriate branding blocks from a project-wide template. Each individual application template then extended its own branded base template.

2. Include the application URLs in your root URL conf.

I decided to keep each application in its own prefix, as such, in my root urls.py:

url(r'^foo/', include('apps.foo.urls')),
url(r'^bar/', include('apps.bar.urls')),

Now, I could visit example.com/, example.com/foo/, and example.com/bar/ and see the appropriate branded versions with their own templates, views, and models. Almost there.

3. Handle the root URL and error pages for each app.

I didn’t want the applications to be hosted on the same domain or require a specific path to work; I wanted example.com to render the root index and foo.com to show what example.com/foo shows, and so on. I also wanted branded error pages. Here’s the relevant snippet of the root urls.py:

handler404 = "root.views.handler404"
urlpatterns = patterns('',
  url(r'^$', 'root.views.index_meta'),
  ...

So, we define two views in the “root” app: an “index_meta” and a “handler404″. Here’s what those look like:

def index_meta(request):
  from apps.foo.views import index as foo_index
  from apps.bar.views import index as bar_index
  return {"foo.com": foo_index, "bar.com": bar_index}.get(request.get_host(), index)(request)
def handler404(request):
  template = {"foo.com": "foo/404.jinja", "bar.com": "bar/404.jinja"}.get(request.get_host(), "404.jinja")
  rendered = render_to_string(template, {}, RequestContext(request))
  return HttpResponse(rendered, status=404)

These two functions simply look up the correct view / template to render based on request.get_host(), falling back on the root project. My handler404 looks a little different because I’m using Jinja2, but you can get the idea. You could also implement handlers for other errors as you need.

Conclusion

If you’ve got different domains that you want to be hosted by the same codebase, database, and workers, this approach is straightforward and only requires a few lines of special code. On the other hand, if each application needs its own settings.py, database, or differs significantly in functionality, it makes sense to run multiple gunicorn masters pointing at different settings files, or even to create separate projects sharing a reusable app.

8 Years of Income, Graphed and Analyzed! (Or How I Learned to Stop Working Full-Time and Love the Savings)

I’ve been carefully tracking my personal finances for the past 8 years or so (using my own software, wxBanker), and thought it would be a fun exercise to analyze the graph of my savings or “net worth” over this entire period. Each period was surprisingly different!

savings

The green line is my actual total savings, while the blue line is just a 2nd degree best-fit line that you can ignore. Take a look at how the slope noticeably changes for each period! Let’s look at each of the segments.

1: College

This was a very frugal part of my life. I was very fortunate not to have much tuition obligation due to financial aid, a large academic scholarship, and a generous family. So what you see here is me scraping savings together, not always outpacing the cost of rent, books, and food. The two humps were paid internships which RIT called “co-ops”.

 

2: My first job and the Bay Area

After college I took an offer at a startup in California (Genius.com). The graph here is oddly linear, despite a couple raises. Perhaps I just spent the extra money on more frozen yogurt. After 2 years at this job, I along with the majority of the engineering team was laid off. The change was welcome, and I used the severance to interview full-time. I found a much smaller “pre-A” start-up, Canvas Networks, and took the job, which required a move to NYC.

3: Canv.as and the Big Apple

The growth of savings here makes a steep change for the worse. Taking a decent pay cut (the understandable cost of working at 4 person company where I could get >1% equity), plus moving to the most expensive city in the country are the main causes. There’s a lot more volatility as well, likely from things such as broker’s fees and having to pay overlapping rent on two apartments for a month while I moved.

While I did end up getting raises that eventually brought me well beyond my previous salary at Genius, they were put entirely towards moving out of a windowless sublet into my own nice, sunny apartment. After a year at Canv.as I decided the full-time life wasn’t for me, and quit to become a part-time freelancer.

4: Michael Rooney Inc and the part-time life

Here is where things get really interesting: I went from working about 50 hours a week to working no more than 10 hours per week, and I started saving more. After I started freelancing and had the choice to work as much or little as I wanted, I decided I’d prefer to work the “lowest responsible amount”. In other words, figure out my full budget including rent, food, health care, still saving money, etc, and just work enough to earn that much (after taxes). Freelancing 10 hours a week ended up being a 40% pay cut, but it was a great trade for an 80%+ hours cut.

But why am I saving more now? First, my expenses were dramatically reduced because my girlfriend and I moved in together, halving my rent and utilities. This alone is about $1,200/mo, $14,400/year, which used to require an extra $20K in earnings to support after losing a third of it to taxes. Having enough time to cook, which I love, also saved me from buying breakfast and dinner multiple times a week. This was easily costing me an extra $100/week, ~5K/year, again requiring about $7.5K in salary to support due to taxes.

Doing business as an S Corporation also meant a lot of my normal expenses became tax-deductible (a home office deduction was huge), and my effective tax rate itself was also lowered due to being able to (perfectly legally) take much of my profits from the company as a distribution instead of personal pay, avoiding some taxes on that portion of income.

For me, working part-time also meant less burn-out, meaning fewer expenses related to entertainment, drinks, and vacations required to “decompress”, etc. Finally, since about 80% of my money is in stocks / mutual funds, the recent market rally also helped a lot.

UPDATE: By popular request (of one person) here is the same graph that does not include gains or losses in the market. It is fairly similar except that the bumps and dips are larger as the total amount is less, and the freelance slope is not as significant, but luckily still noticeably better. I chose a linear trend curve here to make that a little easier to tell:

minus the market

For what is worth, bank interest currently accounts for 2% of my total savings, and capital gains/losses for 18%.

Wow, you read it all, maybe! Do you do any kind of analytics, budgeting, or financial retrospectives, or am I the only one?

 

On Breaking the Time Barrier

Freshbooks recently released a name-your-own-price ebook titled “Breaking the Time Barrier“. It is a pretty quick ~1 hour read about how to price your freelance / contract services and make more money with more and better clients. I figured I’d write a quick review for my own purposes as well as for anyone who might come across this.

The basic premise is that pricing your product / service based on the amount of hours you contribute is not ideal for you or your client, and that ideally you should get away from ever quoting hourly rates or basing your price directly on your hours.

The alternative to the hourly pricing model suggested is to price based on the value you are providing to your client. Upon first reading this, my initial thought was that it just sounds like they are suggesting that you charge clients as much as their budget allows, which isn’t really in line with my values. The second problem that follows this is that if you are charging more than your typical hourly rate, why wouldn’t they just choose someone else who comes in with a cheaper quote?

The answer to both of these is that you can provide a dramatically more valuable service by understanding the value they hope to get out of your work. For example, a typical web client might say that they need a web page created with specified buttons at specific spots and of specific colors. You can figure out how long that will take, which might be for example $1,000 worth of work, and if your quote is competitive and they like your background, you’ll perhaps land the gig. On the other hand, if you ask what value they are hoping to obtain instead of blindly following directions, they might say that their current home page is only converting at 1% and they’d like to increase that.

With the understanding that the client wants to increase currently low conversions, you can now provide more value by providing your expertise, perhaps explaining why they are currently seeing low conversions and how you think you can improve upon their current design even more.

So now you can come in with, say, a $5,000 or $10,000 quote where you suggest doing the page differently because you believe it will increase conversions significantly more than what they proposed, ideally with some data and previous numbers to back it up. Imagine you are the client, and you’ve got a quote or two coming in at around $1,000 to blindly do exactly what you asked, and another one coming in much higher, but from someone who actually understands your goals and has proposed a way to add significantly more value. I can definitely see how those $1,000 quotes aren’t attractive after seeing that you could leap-frog what you originally wanted.

I’ve seen these principals work their way into my own projects, as I realized that I shouldn’t just be doing everything exactly as clients ask, if I think something else would serve them better. Now I’m not afraid to say “Hey, you asked for X, but if your goal is so-and-so, do you think you might be better served by Y instead, because of A, B, and C?” Often times the answer is “Yes, that’s a great idea!”, and my experience agrees with the book that clients are generally quite happy to pay significantly more money, even if they’ve got a much lower quote from someone else, when they believe you are going to add a lot more value than someone else.

The book goes into a lot more specific details about handling conversations, pricing, and timing, so if this is interesting, give it a read. In short, I definitely agree with the premise that you shouldn’t be afraid to charge what you are worth to the client if you can position yourself as being able to add significant value, and that the best way to do this is to ask questions so that you understand why they are seeking this work in the first place.

Announcing BatchedInbox.com: (Free) Email Batching for Gmail

I’ve become a big fan of email batching recently, which is the act of checking email only at specific intervals and processing them in batches. It has a lot of advantages:

  1. batching saves time (a lot less mental context switches), and
  2. removes distractions throughout the day (no notifications, or distractions when using GChat, searching or composing emails), and eventually
  3. reduces anxiety — you won’t feel the need to check your inbox constantly!

Before creating BatchedInbox, I tried batching “manually” by only checking email at specific times. This didn’t work well; I’d still get distracted by new emails when searching for an existing email, composing a new one, or using GChat. I’d also just obsessively and unconsciously check the tab as a way to procrastinate on the task at hand.

I wanted to fix this for myself and others, so I wrote a service I’ve released as www.batchedinbox.com. You simply authenticate with it (OAuth2) and set up a filter in your Gmail, and then you’ll only receive new emails in your Inbox at times you specify, hourly or at specific hours:

BatchedInbox Configuration

BatchedInbox Configuration

I and a handful of beta testers have been using it successfully for a bit, so I feel comfortable announcing it and having others use it. If it interests you, please check it or the FAQ out, and I’d love your feedback either way. It is intended as a permanently free service, supported by myself and perhaps your generous self via Gittip.

If this service doesn’t sound interesting to you, please do let me know why. It may reveal a marketing bug :)

Freelance Income Sources and the 80/20 Rule

I’ll keep this short and sweet. As a freelancer, jobs come from various sources. I wanted to get a visual handle on where mine personally comes from, so I created this graph. Numbers are percentages of total income since I started freelancing 15 months ago:

MathWarehouse-pieSo, about 60% of my income is from my personal network: “Friends & Family” (my direct network, people I already knew reaching out for software help) plus Referrals (my indirect network, friends of friends & family).

I was surprised to see LinkedIn at almost 40%, but the numbers don’t lie. This is from two individuals (not recruiters) reaching out on LinkedIn for help, that turned into successful gigs.

What was surprising is what didn’t make it on the chart: recruiters and job sites. I’ve spent plenty of time talking with recruiters, as well as contacts from sites like http://djangogigs.com/, but not a single one turned in to an opportunity.  Using Timothy Ferriss’s beloved Pareto Principal, I’d say that about 80% of my time trying to keep my pipeline full is spent talking with recruiters, searching job sites, and applying/following up with the contacts there.

Yet, this time doesn’t even result in 20% of my income, it results in 0%. Zero percent. In other words, 100% of my freelance income comes from sources reaching out to me, which is both wonderful and chaotic. The wonderful part is that I can use this data to invalidate spending time on looking for new opportunities. The chaotic part is that I have no control over my pipeline, so I have to have faith that jobs will keep appearing. For 15 months, they have, but if anything changes, I’ll be sure to let you know!

For anyone else freelancing, I’d definitely recommend this exercise. Personally, I learned that I should generally ignore recruiters and job boards, while making sure to keep my personal network impressed with my work and maintaining a nice LinkedIn profile.