Build a CSV of bounced emails using the Postmark API

If you send emails, it makes sense that you would want to analyze which email addresses are bouncing. Postmark has a few ways that you can get this information. You can view the information in the Activity section of the website. You can set up a bounce hook so that Postmark will send a POST request to your web server every time we process a bounce. Additionally, you can use our API endpoints to retrieve the data. With this post, I’ll provide a simple example using Ruby to retrieve bounces using the API and saving the data to a CSV.

Getting the data #

I’ll start with a simple example to see what the requests and responses look like. I’ll be using the rest-client gem. You can install it by running gem install rest-client.

require 'rest-client'
RestClient.get('https://api.postmarkapp.com/bounces', 
  { :params => {:count => 1, :offset => 0}, 
    :accept => :json, 
    :'X-Postmark-Server-Token' => 'SERVER-TOKEN'
  })
#Should output something like
#"{"TotalCount":1,"Bounces":[{"ID":1,"Type":"HardBounce","TypeCode":1,"Name":"Hard bounce","MessageID":"00000000-0000-0000-0000-000000000000","Description":"The server was unable to deliver your message (ex: unknown user, mailbox not found).","Details":"smtp;550 5.1.1 The email account that you tried to reach does not exist. Please try double-checking the recipient's email address for typos or unnecessary spaces. Learn more at http://support.google.com/mail/bin/answer.py?answer=6596 y8si4248192icp.56 - gsmtp","Email":"nothing4@wildbit.com","BouncedAt":"2014-03-05T14:47:43.7297186-05:00","DumpAvailable":true,"Inactive":true,"CanActivate":true,"Subject":"Test"}]}"

Let’s break down the request into its component parts:

  • <a href="https://api.postmarkapp.com/bounces" target="_blank">https://api.postmarkapp.com/bounces</a> - This is the URL route used to get bounces data
  • RestClient.get - This will execute an HTTP GET request against the URL
  • :params => {:count => 1, :offset => 0} - Count and offset are the query string parameters used in this request. This is equivalent to requesting a url like <a href="https://api.postmarkapp.com/bounces?count=1&offset=0" target="_blank">https://api.postmarkapp.com/bounces?count=1&offset=0</a>. Count is the number of records to return and offset is the number of records to skip. You can use these two values to “scroll” or “page” through all of the records.
  • :accept => :json - This will add the request header Accept: application/json. This tells the server we accept responses in JSON format.
  • :'X-Postmark-Server-Token' => 'SERVER-TOKEN' - This will add the X-Postmark-Server-Token header which is used by the Postmark API server for authentication of requests.

The response from the request is a JSON string of data. We can use ruby to convert the string into a more usable data format.

Parsing JSON #

Using part of the Ruby standard library, we can parse the JSON and get easy access to the individual fields of the bounces. (Note: if your version of Ruby is older than 1.9.1, you will have to run gem install json.)

require 'rest-client'
require 'json'
response = RestClient.get('https://api.postmarkapp.com/bounces', 
  { :params => {:count => 10, :offset => 0}, 
    :accept => :json, 
    :'X-Postmark-Server-Token' => 'SERVER-TOKEN'
  })
parsed_response = JSON.parse(response)
bounces = parsed_response["Bounces"]
#We can list out the individual fields that are in each bounce
bounces.first.keys
#Should output:
#["ID", "Type", "TypeCode", "Name", "MessageID", "Description", "Details", "Email", "BouncedAt", "DumpAvailable", "Inactive", "CanActivate", "Subject"]

Building a CSV #

For the columns of the CSV, let’s pick Email, Name and Inactive. Email is the email address that caused the bounce. Name is a human readable bounce type, i.e. hard bounce, soft bounce etc. Inactive is whether or not future emails sent to this address will attempt to get delivered or not. Postmark will deactivate email addresses if we’ve received a spam complaint or a hard bounce from the address.

require 'rest-client'
require 'json'
require 'csv'
response = RestClient.get('https://api.postmarkapp.com/bounces', 
  { :params => {:count => 10, :offset => 0}, 
    :accept => :json, 
    :'X-Postmark-Server-Token' => 'SERVER-TOKEN'
  })
parsed_response = JSON.parse(response)
bounces = parsed_response["Bounces"]
CSV.open('report.csv', 'w') do |csv|
  csv << ["Email Address", "Bounce Type", "Is inactive?"]
  bounces.each do |bounce|
    csv << [bounce["Email"], bounce["Name"], bounce["Inactive"]]
  end
end
# $ cat report.csv
# Email Address,Bounce Type,Is inactive?
# nothing1@wildbit.com,Hard bounce,true
# nothing2@wildbit.com,Hard bounce,true
# nothing3@wildbit.com,Hard bounce,true
# nothing4@wildbit.com,Hard bounce,true
# nothing5@wildbit.com,Hard bounce,true
# nothing6@wildbit.com,Hard bounce,true
# nothing7@wildbit.com,Hard bounce,true
# nothing8@wildbit.com,Hard bounce,true
# nothing9@wildbit.com,Hard bounce,true
# nothing10@wildbit.com,Hard bounce,true

Now the report.csv file will contain the email addresses and bounce types of your server.

Doing things the easy way #

Hopefully, this post worked as a language agnostic view of the Bounces API and the examples are easily ported to other programming languages. If you are using pure Ruby, the official Postmark Ruby gem by my excellent colleague Artem Chistyakov provides a great interface for bounce information.

require 'postmark'
client = Postmark::ApiClient.new('SERVER-TOKEN')
puts client.bounces.
            first(10).
            map { |b| b.values_at(:email, :name, :inactive) }.
            unshift(["Email Address", "Bounce Type", "Is inactive?"]).
            map(&:to_csv).
            join
# Email Address,Bounce Type,Is inactive?
# nothing1@wildbit.com,Hard bounce,true
# nothing2@wildbit.com,Hard bounce,true
# nothing3@wildbit.com,Hard bounce,true
# nothing4@wildbit.com,Hard bounce,true
# nothing5@wildbit.com,Hard bounce,true
# nothing6@wildbit.com,Hard bounce,true
# nothing7@wildbit.com,Hard bounce,true
# nothing8@wildbit.com,Hard bounce,true
# nothing9@wildbit.com,Hard bounce,true
# nothing10@wildbit.com,Hard bounce,true

Wrapping things up #

Bounces are an important part of email deliverability and Postmark provides the API to make sure that information is open and available to our customers. One use case of the bounces API is for creating your own reporting and analytics. Another might be to analyze the inactive addresses to make sure your customers didn’t have any typos in their email addresses. We’d love to hear what you’re using the Bounces API for, send us a message at support@postmarkapp.com.