I closed my transcription business a month ago but still had some outstanding invoices to collect, and they all lived in Freshbooks. However, I canceled my Freshbooks subscription -- prepaid through mid-July 2023 -- and so needed to download all the info, at least about the outstanding invoices1.

I figured, though, that I may as well download the entire archive of clients and invoices -- 10+ years' worth! -- for future marketing purposes, and just to have a nice record of the business.

Luckily, I had published this Python library for interacting with the Freshbooks API, which got me most of the way towards downloading, serializing and storing all the client and invoice data.


> mkdir all_my_invoices && cd $_
> virtualenv env && source env/bin/activate
> pip install ipython simplejson avt-fresh

Get All Clients and Serialize 'Em

import avt_fresh
import json as j
import pathlib as p
cl = avt_fresh.ApiClient(client_secret="...", client_id="...", redirect_uri="...", account_id="...")
clients = cl.get_all_clients()
client_ids = [c.client_id for c in clients]
clients_json = []
for client in clients:
    d = c._asdict()
    del d['email_contact_id_lookup']
    del d['contact_id_email_lookup']
p.Path('all_my_clients.json').write_text(j.dumps(clients_json, indent=2)) 

Get All Invoices For All Clients and Serialize 'Em

import avt_fresh
import simplejson as json
import pathlib as p

cl = avt_fresh.ApiClient(client_secret="...", client_id="...", redirect_uri="...", account_id="...")

def get_and_serialize_all_invoices_for_client(client_id):
    invs = cl.get_all_invoices_for_client_id(client_id)
    serialized = []
    for i in invs:
        d = i._asdict()
        for key, val in d.items():
            if isinstance(val, dt.date):
                d[key] = val.toordinal()
        # some data 
        del d['allowed_gateways']
        del d['line_description_line_id_dict']
        del d['line_id_line_dict']
        new_lines = []
        for l in d['lines']:
            dl = l._asdict()
            for key, val in dl.items():
                if isinstance(val, dt.date):
                    dl[key] = val.toordinal()
        d['lines'] = new_lines
    return serialized

clients = json.loads(p.Path('all_my_clients.json').read_text())
invoices = {}
for client_id in client_ids:
    invoices[client_id] = get_and_serialize_all_invoices_for_client(client_id)
p.Path('all_client_invoices.json').write_text(json.dumps(invoices, indent=2)) 

Future Improvements

I'm not sure how likely I am to circle back and improve this library since I don't expect to use Freshbooks again in the future. However, there are a few low-hanging fruits which would make it a little nicer to work with, especially when it comes to serialization:

  1. I should mention that Freshbooks is the only SaaS I've run into that will immediately shut your account down when you cancel your account, even if you've already paid for a year's subscription! I had to beg them to give me a brief window of access in order to perform the above steps -- five days to be exact, even though I still had 2+ weeks left in the prepaid subscription.

smiley picture of shaven-head Zev

American software engineer in Switzerland. Constant builder, learner, and teacher. 🌼