I’m using Mailbluster for my email marketing / newsletter. It’s simpler and cheaper than Mailchimp, and a bit more user friendly than ‘Email Subscribers’ (for WordPress) that I’ve used in the past. It’s built on top of Amazons Simple Email Service (SES), (so you do need to set that up first), and it has an API to integrate it with your software
What I needed was a simple signup form for an email list that didn’t require a server (there is a Mailbluster WordPress plugin which is probably covers the usecase for a lot of people), so I began making a quick zappa-flask app to do this. Many established APIs have Python wrappers to abstract away the nitty gritty of using the API, but Mailbluster doesn’t – so I though this was a good opportunity to make one that will serve for my purposes and might be useful for others – and hopefully give me an opportunity to learn something. I just focussed on the ‘leads’ API to start with, which had a simple Create Read Update Delete structure.
You can see my code on github
There are a couple of recent learning points I put into practice:
1. Don’t Repeat Yourself and ‘private’ functions.
Rather than writing out the URL and calling the requests function for each of the API parts, I created a ‘private’ function (double underscore by convention denotes this although you can still access it), which performs the request. All the details of the implementation are passed to it by other functions, so we don’t need to write the details out each time.
2. Key Word Argument unpacking with **kwargs
When I’ve looked through other API wrappers previously for hints of the required or optional parameters, often they are not there in the code, but only in the API documentation. This is because the wrapper itself lets you pass through the parameters directly.
BAD: In this first example, because I’ve added everything as positional arguments, they are all required (by Python) but not for the API, (except email). We’d have to add some default arguments to satisfy Python, but we don’t actually need to pass anything to the API for these optional arguments. The code is also quite verbose, and if anything is added to the API later, we’d need to update the code to reflect that.
def create_lead(self, firstName, lastName, timezone, ipAddress, tags ,meta): payload["firstName"] = firstName payload["lastName"] = lastName payload["timezone"] = timezone payload["ipAddress"] = ipAddress payload["tags"] = tags payload["meta"] = meta payload["subscribed"] = 'true' payload["overrideExisting"] = 'true' return self.__perform_request(self.base_url + 'leads', typeofr="post", payload=payload)
BETTER: The second example is much cleaner, as we use the **kwargs to denote that any number of keyword arguments can be passed to the function. We then put these into the payload variable to be sent to the API. I added in a check for the required ’email’ argument, but of course you can still sent the request without it and wait for an error response from the API. The key thing obviously when calling create_lead is that the names of the keyword parameters must be identical to those the API expects, and you can’t find this out from the code itself (I did add a docstring in the actual code to show this, or you could lookup the API docs).
def create_lead(self,**kwargs): payload = locals()["kwargs"] if "email" not in payload: raise TypeError("Missing 'email' argument") payload["subscribed"] = 'true' payload["overrideExisting"] = 'true' return self.__perform_request(self.base_url + 'leads', typeofr="post", payload=payload)