How to Validate an E.164 Phone Number in Go

September 11, 2023
Written by
Reviewed by

If you're sending SMS, MMS — or performing any other form of communication using Twilio's Programmable Messaging API — you'll know that phone numbers must be in E.164 format. The question is, how can you validate them in Go?

Well, in this short tutorial, you're going to learn how, using Twilio's E.164 regular expression (tl;dr, it's ^\+[1-9]\d{1,14}$).

Actually, you're going to learn a bit more than that. Just showing the core code to validate a phone number would only take about 30 seconds. So, instead, I'll make the tutorial a bit more interesting and fun.

You're going to learn how to create a small Go-powered API that can accept requests with a phone number, and return a small JSON response confirming whether the number is correctly formatted or not.

It's worth mentioning that the E.164 regex is only a simplistic way of catching invalid phone numbers. It doesn't perform more comprehensive searches and validation. Something that does, however, is Twilio's Lookup API, which can, among other things:

  • Get confirmation of ownership for a mobile phone number
  • Get information on the last SIM change for a mobile phone number
  • Get a phone number's line type

Give it a try, if you're looking for a comprehensive solution!

Prerequisites

To follow along with this tutorial, you're going to need the following:

  • Go
  • curl
  • Your favourite text editor or IDE. I recommend Visual Studio Code.
  • Some prior experience with Go would be helpful, though not required

Create the project directory structure

Where you keep your Go projects: create a new project directory, change into it, and track modules, by running the following commands.

mkdir e164_validator_api
cd e164_validator_api
go mod init e164_validator_api

Build the Go API

Now, let's write the Go code. Create a new file named main.go, and in that file paste the code below.

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "regexp"
    "strings"
)

func IsValidPhoneNumber(phone_number string) bool {
    e164Regex := `^\+[1-9]\d{1,14}$`
    re := regexp.MustCompile(e164Regex)
    phone_number = strings.ReplaceAll(phone_number, " ", "")

    return re.Find([]byte(phone_number)) != nil
}

type Response struct {
    Data string
}

func validate(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")

    phone_number := r.FormValue("phone_number")
    if phone_number == "" {
            w.WriteHeader(http.StatusBadRequest)
            json.NewEncoder(w).Encode(Response{
                    Data: "No phone number was provided\n",
            })
            return
    }

    if IsValidPhoneNumber(phone_number) {
            w.Write([]byte(fmt.Sprintf("[%s] is a valid phone number", phone_number)))
            json.NewEncoder(w).Encode(Response{
                    Data: fmt.Sprintf("[%s] is a valid phone number", phone_number),
            })
            return
    }

    w.WriteHeader(http.StatusBadRequest)
    json.NewEncoder(w).Encode(Response{
            Data: fmt.Sprintf("[%s] is NOT a valid phone number", phone_number),
    })
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", validate)

    log.Print("Starting server on :4000")
    err := http.ListenAndServe(":4000", mux)
    log.Fatal(err)
}

The code defines a small function, IsValidPhoneNumber(). It uses Twilio's E.164 regex and Go's regexp package to determine if the phone number, phone_number, is formatted correctly. It returns true if it is or false otherwise.

Then, it defines another function, validate(). The function starts by attempting to retrieve the phone_number parameter from the request. If it was not set or was empty, a response is returned with a message stating this. Otherwise, the retrieved phone number is validated with IsValidPhoneNumber(). The response's body is then set to a message confirming whether the number was valid or not.

Finally the main() function is defined. It sets up the application to handle HTTP requests, with the validate() function handling requests to the application's default route.

It's worth noting that Twilio's E.164 regex doesn't accept valid phone numbers written in other formats. For example, in Germany, you often replace the leading + with 00. And even Twilio APIs accept phone numbers if this part is omitted. However, this regex does not. Please keep that in mind.

Test the code

With the code written, it's now time to test that it works. Start by launching the application with the following command.

go run main.go

Then, run the following command to make a request to the app with curl. Make sure to replace the phone number placeholder with the phone number that you want to test.

curl -i --form "phone_number=<<PHONE NUMBER>>" http://localhost:4000

If the phone number is valid, you should see the following output in the terminal (with the phone number you entered instead of the placeholder).

HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 24 Aug 2023 10:20:38 GMT
Content-Length: 55

{"Data":"[<<PHONE NUMBER>>] is a valid phone number"}

That's how to validate that an E.164 phone number in Go

There's not a lot to do. But it's still helpful to know how. I hope that you found the approach that I took to be fun and interesting.

How would you check whether a phone number is in E.164 format? Let me know on LinkedIn?

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