How to View Your Twilio Account Usage Using Go

January 11, 2023
Written by
Reviewed by

How to View Your Twilio Account Usage Using Go

In this tutorial, you're going to learn how to retrieve those details using Go and Twilio's Go Helper Library, along with a few other classes to make the whole process easier and simpler.

If you’re not familiar with the REST API, it supports retrieving actions made by your Twilio account during any time period and by any usage category. So if you want to create some tooling to perform reporting or analytics on your account(s), then this is the API to use.

Prerequisites

Set up the project directory structure

The first thing you need to do is create a project directory, then initialise a new Go project, and then change into the new directory. Do that by running the following commands, wherever you want to store the project directory.

mkdir go-twilio-usage-records
go mod init go-twilio-usage-records
cd go-twilio-usage-records

Add the required dependencies

With the project directory in place, the next thing to do is add the required dependencies, of which there are 4:

  • Twilio's Go Helper Library: This package simplifies interacting with Twilio’s many APIs in Go.
  • Go Dotenv: This package simplifies setting environment variables.
  • Go Pretty: This package simplifies creating command line application output.
  • Bojanz's Currency: This package simplifies formatting currency data.

To install them, run the commands below.

go get github.com/twilio/twilio-go
go get github.com/joho/godotenv
go get github.com/jedib0t/go-pretty/v6
go get github.com/jedib0t/go-pretty/v6/text@v6.3.7
go get github.com/bojanz/currency

Retrieve your Twilio credentials

The next thing to do is to make your Twilio credentials (your Twilio Account SID and Auth Token) available to the application.

To do that, first, create a new file named .env in the top-level directory of the project. Then, in that file paste the code below. It adds environment variables and a placeholder for both which will be replaced with the real values shortly.

TWILIO_ACCOUNT_SID=<TWILIO_ACCOUNT_SID>
TWILIO_AUTH_TOKEN=<TWILIO_AUTH_TOKEN>

Next, from the Twilio Console's Dashboard, copy your Account SID and Auth Token and, in .env, replace the respective placeholder values with them (<TWILIO_ACCOUNT_SID> and <TWILIO_AUTH_TOKEN>).

Create a Go class to retrieve your account usage records

With the project setup completed, it’s now time to write the application’s core code. To do that, create a new file named twilio-usage-records.go in the project’s top-level directory and in it, paste the code below.

package main

import (
    "fmt"
    "log"
    "os"
    "strings"
    "time"

    "github.com/bojanz/currency"
    "github.com/jedib0t/go-pretty/v6/table"
    "github.com/joho/godotenv"
    "github.com/twilio/twilio-go"
    openapi "github.com/twilio/twilio-go/rest/api/v2010"
)

func main() {
    recordLimit := 20
    params := &openapi.ListUsageRecordParams{Limit: &recordLimit}

    err := godotenv.Load()
    if err != nil {
            log.Fatal("Error loading .env file")
    }

    client := twilio.NewRestClientWithParams(twilio.ClientParams{
            Username: os.Getenv("TWILIO_ACCOUNT_SID"),
            Password: os.Getenv("TWILIO_AUTH_TOKEN"),
    })
    usageRecords, err := client.Api.ListUsageRecord(params)
    if err != nil {
            fmt.Println(err.Error())
    } else {
            t := table.NewWriter()
            t.SetOutputMirror(os.Stdout)
            t.AppendHeader(table.Row{"Start Date", "End Date", "Category", "Price"})

            for _, record := range usageRecords {
                    price := fmt.Sprintf("%.2f", *record.Price)
                    amount, err := currency.NewAmount(price, strings.ToUpper(*record.PriceUnit))
                    if err != nil {
                            fmt.Println("Could not create currency amount from usage price.")
                    }
                    locale := currency.NewLocale("en_US")
                    formatter := currency.NewFormatter(locale)

                    startDate, err := time.Parse("2006-01-02", *record.StartDate)
                    if err != nil {
                            fmt.Println("Could not format start date.")
                    }

                    endDate, err := time.Parse("2006-01-02", *record.EndDate)
                    if err != nil {
                            fmt.Println("Could not format end date.")
                    }

                    t.AppendRow([]interface{}{
                            startDate.Format("January 2, 2006"),
                            endDate.Format("January 2, 2006"),
                            *record.Category,
                            formatter.Format(amount),
                    })
            }

            t.AppendSeparator()
            t.AppendFooter(table.Row{"Total records: ", len(usageRecords)}, table.RowConfig{AutoMerge: true})

            t.Render()
    }
}

The code, as always, starts off by importing all of the classes that it needs, before defining the main function.

That function defines the parameters to send to the usage records request in a new ListUsageRecordParams struct. The struct supports seven parameters, which you can see below. However, only the maximum number of records to retrieve will be set in the first example.

  • Category: If set, the records are filtered to those that fit into a specific category.
  • EndDate: If set, only records that occurred on or before this date are included
  • IncludeSubaccounts: Whether to include records from the master account and all its subaccounts
  • Limit: The maximum number of records to return
  • PageSize: How many records to return in each page of records. The default is 50 and the maximum is 1000
  • PathAccountSid: The SID of the Account that created the records
  • StartDate: If set, only records that occurred on or after this date are included

After that, it loads the environment variables set in .env. If not successful, then an error is printed to the terminal. Otherwise, it attempts to retrieve usage records from the user’s account.

If the records were not able to be retrieved, an error is printed to the terminal. Otherwise, the records are iterated over, and the start date, end date, category, and pricing information (price and price unit) are extracted. The start and end dates, along with the pricing information, are formatted to make them just that much easier to read.

The extracted information is added to a go-pretty table so that, when printed, the information is rendered in a consistent and coherent way. After all of the details have been added to the table, the table is printed to the terminal.

Test that the code works

Now, it’s time to test that the code works. To do that, run the command below in the project’s top-level directory.

go run twilio-usage-records.go

All being well, you should see output similar to the example below.

+------------------+------------------+--------------------------+--------+
| START DATE       | END DATE         | CATEGORY                 | PRICE  |
+------------------+------------------+--------------------------+--------+
| January 28, 2021 | January 11, 2023 | calleridlookups          | $0.00  |
| January 28, 2021 | January 11, 2023 | calls                    | $1.82  |
| January 28, 2021 | January 11, 2023 | calls-client             | $0.00  |
| January 28, 2021 | January 11, 2023 | calls-sip                | $0.00  |
| January 28, 2021 | January 11, 2023 | calls-inbound            | $0.15  |
| January 28, 2021 | January 11, 2023 | calls-inbound-local      | $0.15  |
| January 28, 2021 | January 11, 2023 | calls-inbound-mobile     | $0.00  |
| January 28, 2021 | January 11, 2023 | calls-inbound-tollfree   | $0.00  |
| January 28, 2021 | January 11, 2023 | calls-outbound           | $1.67  |
| January 28, 2021 | January 11, 2023 | phonenumbers             | $20.30 |
| January 28, 2021 | January 11, 2023 | phonenumbers-mobile      | $0.00  |
| January 28, 2021 | January 11, 2023 | phonenumbers-local       | $20.30 |
| January 28, 2021 | January 11, 2023 | phonenumbers-tollfree    | $0.00  |
| January 28, 2021 | January 11, 2023 | shortcodes               | $0.00  |
| January 28, 2021 | January 11, 2023 | shortcodes-customerowned | $0.00  |
| January 28, 2021 | January 11, 2023 | shortcodes-random        | $0.00  |
| January 28, 2021 | January 11, 2023 | shortcodes-vanity        | $0.00  |
| January 28, 2021 | January 11, 2023 | sms                      | $22.70 |
| January 28, 2021 | January 11, 2023 | sms-inbound              | $0.17  |
| January 28, 2021 | January 11, 2023 | sms-inbound-longcode     | $0.17  |
+------------------+------------------+--------------------------+--------+
| TOTAL RECORDS:   | 20               |                          |        |
+------------------+------------------+--------------------------+--------+

Filter records by predefined date ranges

Now, let’s build on the first example by filtering the returned usage records by date. The Twilio Go Helper Library makes this reasonably simple because of a series of helper structs, including ListUsageRecordToday, ListUsageRecordThisMonth, and ListUsageRecordLastMonth. These helper structs filter records to only those that occurred today, this month, and last month, respectively.

Let's filter the returned records from the previous month. To do that, change params to be defined as follows.

params := &openapi.ListUsageRecordLastMonthParams{
    Limit: &recordLimit,
}

Then, change usageRecords, to be initialised as follows:

usageRecords, err := client.Api.ListUsageRecordLastMonth(params)

If you run it, you should see terminal output, similar to the example below.

+-----------------+---------------+-------------------------------------------------------+--------+
| START DATE      | END DATE      | CATEGORY                                              | PRICE  |
+-----------------+---------------+-------------------------------------------------------+--------+
| July 1, 2022    | July 31, 2022 | wireless-usage                                        | $0.00  |
| July 1, 2022    | July 31, 2022 | pv-basic-rooms                                        | $0.00  |
| July 1, 2022    | July 31, 2022 | ip-messaging-data-storage                             | $0.00  |
| July 1, 2022    | July 31, 2022 | marketplace-bot-msg.ai-deliveryaware                  | $0.00  |
| July 1, 2022    | July 31, 2022 | wireless-usage-commands                               | $0.00  |
| July 1, 2022    | July 31, 2022 | conversations-participant-events                      | $0.00  |
| July 1, 2022    | July 31, 2022 | group-rooms-media-recorded                            | $0.00  |
| July 1, 2022    | July 31, 2022 | marketplace-algorithmia-named-entity-recognition      | $0.00  |
| July 1, 2022    | July 31, 2022 | wireless-usage-data-northamerica                      | $0.00  |
| July 1, 2022    | July 31, 2022 | marketplace-cadence-transcription                     | $0.00  |
| July 1, 2022    | July 31, 2022 | wireless-super-sim-smscommands-europe                 | $0.00  |
| July 1, 2022    | July 31, 2022 | conversations-endpoint-connectivity                   | $0.00  |
| July 1, 2022    | July 31, 2022 | experiment-india-sms                                  | $0.00  |
| July 1, 2022    | July 31, 2022 | voice-insights-sip-trunking-insights-on-demand-minute | $0.00  |
| July 1, 2022    | July 31, 2022 | calls-inbound                                         | $0.00  |
| July 1, 2022    | July 31, 2022 | sms-inbound-longcode                                  | $0.00  |
| July 1, 2022    | July 31, 2022 | wireless-super-sim-smscommands-africa                 | $0.00  |
| July 1, 2022    | July 31, 2022 | phonenumbers-tollfree                                 | $0.00  |
| July 1, 2022    | July 31, 2022 | autopilot-other                                       | $0.00  |
| July 1, 2022    | July 31, 2022 | sms-outbound-longcode                                 | $10.78 |
+-----------------+---------------+-------------------------------------------------------+--------+
| TOTAL RECORDS:  | 20            |                                                       |        |
+-----------------+---------------+-------------------------------------------------------+--------+

Filter records by start date, end date, and category

Finally, let’s filter records by a broader date range as well as by category. There are nine categories available. These are:

CategoryDescription
callsInbound and outbound voice calls. This does not include SIP or client calls.
smsSMS messages
pfax-minutesProgrammable fax minutes
pfax-pagesProgrammable fax pages
phonenumbersPhone numbers owned by the account
recordingsRecordings of voice calls
transcriptionsTranscriptions of voice calls
pvAll Programmable Video usage
totalpriceThis is the total price of all usage.

Let's filter the usage records to SMS records sent and received in the last quarter. To do that, add the following code block before the definition of params:

recordLimit := 20
inputDateFormat := "2006-01-02"
category := "sms"
startDate := time.Now().AddDate(0, -3, 0).Format(inputDateFormat)
endDate := time.Now().Format(inputDateFormat)

Then, update the definition of params to match the following code:

params := &openapi.ListUsageRecordLastMonthParams{
    Limit: &recordLimit,
    Category: &category,
    StartDate: &startDate,
    EndDate: &endDate,
}

Finally, add "time" to the imports list at the top of the file, if your text editor or IDE hasn't already done it for you.

Now, run the code again. You should see terminal output similar to the example below.

+------------------+-------------------+----------+-------+
| START DATE       | END DATE          | CATEGORY | PRICE |
+------------------+-------------------+----------+-------+
| December 1, 2022 | December 31, 2022 | sms      | $0.09 |
+------------------+-------------------+----------+-------+
| TOTAL RECORDS:   | 1                 |          |       |
+------------------+-------------------+----------+-------+

That’s how to view your Twilio account usage using Go

While there is a lot more on offer than I've covered in this tutorial, you now know the essentials.

Do you prefer to use PHP? Then check out this tutorial by Marcus Battle.

Matthew Setter is a PHP Editor in the Twilio Voices team and a PHP and Go developer. He’s also the author of Mezzio Essentials and Docker Essentials. When he's not writing PHP code, he's editing great PHP articles here at Twilio. You can find him at msetter@twilio.com, and on Twitter, and GitHub.