How to Use the Version 10.0 PAN-OS API

Posted by Charles Buege on Feb 12, 2021 4:36:25 PM

Friday, February 12, 2021

By Charles Buege, Fuel Editorial Advisory Committee Member, Chicago Fuel Chapter Leader

When it comes to automation, one of the most important tools to have is knowing how to make API calls. The PAN-OS has several different ways of doing this. In this article, I will concentrate on version 10, since it’s the latest version of the PAN-OS, and I will also be using the REST API instead of the XML API primarily, as that is my personal preference. There is one point where using the XML API is the only option and I will go into that later on.

With any process, you also need to make sure the steps are being done in the proper sequence. Applying that same principle to this article, here is what I will do:

  1. Create an Address Object

  2. Create a second Address Object

  3. Create an Address Group with both Address Objects as members of the newly created Address Group

While I could have created the Address Objects after the Address Group, I want to add the Address Objects to the Address Group at the time of creation, so they need to be created ahead of time. For more complex API calls, there will be many, many more steps than the simple example in this article, so plan ahead of time what your sequence of steps will be, what elements need to be created in what order and when to perform certain steps to make sure they are done properly and correctly so you don’t screw yourself up.

The elements I am going to create in this article are two Address Objects, called “AlphaSite” and “BetaSite,” with values of and, respectively. The Address Group that I will create will be called “OffworldSites” and will contain both “AlphaSite” and “BetaSite.” For those who have read other articles I have written, you may recognize the references to the television show “Stargate SG-1.” Yes, this is another reference to that show.

User Setup

I am also going to do something in this article that is almost never done. I will show you not only the user ID that I use, but also the password for the account and the API key that I will generate for this user. 

Why am I willing to do this? The simplest reason is that this system that I am doing all of this work on is not on the internet, is not accessible by anyone outside of my home lab and will never be accessible. Additionally, as soon as this article is written, I will delete this user, negating both the password and the API key, so it truly becomes irrelevant if I share this information with you or not. Having this exact information shown and not obfuscated or blurred out may make it easier to see everything exactly as it is entered and leave no doubt as to what is being done at the command line.

I started off by creating an “Admin Role Profile” by going to Device -> Admin Roles and clicking “Add.” From here, I entered the name of “API Superuser.” Under the tab for “REST API,” I enabled every section for this role. I won’t show all of the screens as that is unnecessary, but I will show one screenshot. Here’s the first screen full of checkboxes:

Figure 01

By selecting or deselecting “Objects,” you can toggle all of the selections under that hierarchy without selecting each one individually. That will save you a lot of time. But if you want to, you can also control at a very granular level how much or how little access the role has.

As mentioned earlier, there is one item under the XML API that we will need to enable as well. There is currently no way for the REST API to perform a “commit” to the firewall, so we need to enable this user to be able to make the commit via the XML API. Click on the “XML API” tab and enable “Commit.”

Figure 02

We will make an XML API call at the very end to perform the actual commit to the firewall to finish the entire process.

Next, we will create a dedicated user with JUST this access being granted from this role. They will have no access from the Web UI nor from the Command Line, which are both completely disabled in this role.

Going to Device -> Administrators, I click on “Add.” From here, I enter the following information and click “Ok” when complete:

Name: sircharles10api

Password: Lever@ge8!

Confirm Password: Lever@ge8!

Administrator Type: Role Based

Profile: API Superuser

Once this is done, I will go ahead and perform a commit to the firewall to save this Admin Role and this Administrator account.


For this article, I am using a CentOS 7 box issuing this commands at a bash prompt. These commands could be altered for a PowerShell prompt or any other type of command, but this is where I feel most comfortable for the work I am doing. Additionally, the IP address of the management interface of the PA-220 that I will work with in this example is, which we will reference several times in “curl” commands.

Lastly, I want to share that this article would not be possible without the fine work done by Palo Alto Networks and the team that maintains the documentation at All of their hard work is what has allowed me to piece together all of the information I needed to put this article together, so thank you to all of them for all that they do.

I started off with their documentation at the URL of which also lead me to learn that all of the REST API documentation is also available locally for myself at, so I also had that information up in a separate tab.

Step 1: Generate Your API Key

Whenever you’re working with APIs — and this is pretty much standard across APIs — the first thing you need to have is your API key. Depending on the system, you may have it handed to you already or you may need to have it generated for you. In our case, we needed to have it generated for us.

Additionally, for those familiar with curl and wondering why I am using the “-k” parameter with it, I have used an internally generated certificate authority (CA) to self-sign my PA-220, but since the Linux host I’m working on doesn’t know about my CA, I’m using the “-k” parameter to ignore the peer’s certificate issuer. If I don’t use that parameter, I get the following error in curl:

curl: (6) Could not resolve host: GET; Unknown error

curl: (60) Peer's certificate issuer has been marked as not trusted by the user.

More details here:

curl performs SSL certificate verification by default, using a "bundle"

 of Certificate Authority (CA) public keys (CA certs). If the default

 bundle file isn't adequate, you can specify an alternate file

 using the --cacert option.

If this HTTPS server uses a certificate signed by a CA represented in

 the bundle, the certificate verification probably failed due to a

 problem with the certificate (it might be expired, or the name might

 not match the domain name in the URL).

If you'd like to turn off curl's verification of the certificate, use

the -k (or --insecure) option.

Here is the generic structure for the command we will use to generate our API key:

curl -k -X GET 'https://<firewall>/api/?type=keygen&user <username>&password=<password>'

Replacing our settings into the above command, we will end up with the following updated command:

curl -k -X GET '!'

Putting this command into action, here is the output that I received from the PA-220 that I have. I’ve taken the XML output and formatted it to make it easier to read.

<response status = 'success'>







Once you get the API key back from the system, be sure to keep that safe like you would any other password. You cannot retrieve it again.

Also, if at any time you feel that your API key has become compromised, it is very simple to force it to become invalid. To force your API key to become invalid, just change the password on the account for the user that the API key is associated with. For example, if I change sircharles10api’s password from “Lever@ge8!” to something else, the API key listed above will become invalid. It’s that easy.

Step 2: Test the API Key

Now that we have an API key, let’s make sure it works. Here is a command that will return to us some predefined tags on the system. This is the command we will execute:

curl -k -X GET --header 'X-PAN-KEY: LUFRPT1XYmVONHloWVNJYW1aSllYT3J0VHA0bmVNYnc9UG9hZXowQ3drVSszOFRjakdHZXNaRzRNMHJ1bU1TRENmZmllQ3VLblkxUG85Z3RuQm1pSGxGTWM1dm8xeHVGYw=='



Step 3: Create the Address Objects

Using the documentation on my local PA-220 as a starting point, I was able to eventually build out a command to create the first Address Object I wanted to create. The URL from my local PA-220’s documentation is here:

You can replace your own IP address/FQDN with what mine is to get to the same documentation.

After going through this document and many instances of trial and error, I was able to discover several pieces of information:

  1. Since both “name” and “location” are required fields, they require an “@” symbol in front of their names in the parameter designation as part of the URL, as well as in the JSON payload.

  2. If I added anything to the URL query string, the “@name” is a required part of the URL query. You cannot have the “@name” just in the JSON payload.

  3. With regards to the “@name” and the JSON payload — the “@name” needs to be in BOTH places. Even if specified in the URL query string, “@name” still needs to be in the JSON payload as well.

  4. No matter what I did, I was never able to get “@location” to work as part of the JSON payload and I only ever got it to work as part of the URL query string, so I just used it there.

  5. Even though a PA-220 doesn’t officially have a “vsys” available to it, it has a single built-in vsys that has to be used and referenced when making API calls. That was the only way I was able to get the API calls to work.

Here is the final easy-to-read command that I used to create the first Address Object. The “\” at the end of each line in bash/Linux is what allows the command to continue on to the next line:

curl -k \

-X \


'' \


--header 'Content-Type: application/json' \

-d '{"entry":{"@name":"AlphaSite","ip-netmask":""}}'


{"@status":"success","@code":"20","msg":"command succeeded"}

Once I was able to get one Address Object to be created, modifying the command for a second Address Object was quite easy. Here’s the command for the second Address Object, run together on a single line:

curl -k -X POST '' --header 'X-PAN-KEY: LUFRPT1XYmVONHloWVNJYW1aSllYT3J0VHA0bmVNYnc9UG9hZXowQ3drVSszOFRjakdHZXNaRzRNMHJ1bU1TRENmZmllQ3VLblkxUG85Z3RuQm1pSGxGTWM1dm8xeHVGYw==' --header 'Content-Type: application/json' -d '{"entry":{"@name":"BetaSite","ip-netmask":""}}'


{"@status":"success","@code":"20","msg":"command succeeded"}

By going to the PA-220’s web interface, I can look at Objects -> Addresses and see both of them listed there.

Figure 03

Step 4: Create the Address Group

To create the Address Object Group, we will use the “AddressGroups” REST API. We will need to specify the name and location as before, and we will also specify the names of the two groups that we want to be added to the Address Group.

Again, using the documentation on the PA-220’s site, as well as that on, I am able to figure out what I need to string together, so here is the command we will be using for this:

curl -k -X POST '' --header 'X-PAN-KEY: LUFRPT1XYmVONHloWVNJYW1aSllYT3J0VHA0bmVNYnc9UG9hZXowQ3drVSszOFRjakdHZXNaRzRNMHJ1bU1TRENmZmllQ3VLblkxUG85Z3RuQm1pSGxGTWM1dm8xeHVGYw==' --header 'Content-Type: application/json' -d '{"entry":{"@name": "OffworldSites","static":{"member":["AlphaSite","BetaSite"]}}}'


{"@status":"success","@code":"20","msg":"command succeeded"}

By going to the PA-220’s web interface, I can look at Objects -> Address Groups and see the group listed there, as well as both Address Objects listed under “Addresses.”

Figure 04

Step 5: Committing the Changes to the Firewall

Now that all of our changes are complete, we need to commit those changes to the firewall. Here is where we are going to use the XML API call for the commit, as there isn’t a REST API call to do this currently. The command is very simple and looks like this:

curl -H "X-PAN-KEY: LUFRPT1XYmVONHloWVNJYW1aSllYT3J0VHA0bmVNYnc9UG9hZXowQ3drVSszOFRjakdHZXNaRzRNMHJ1bU1TRENmZmllQ3VLblkxUG85Z3RuQm1pSGxGTWM1dm8xeHVGYw==" -k '<commit></commit>'


<response status="success" code="19"><result><msg><line>Commit job enqueued with jobid 10522</line></msg><job>10522</job></result></response>

At this point, you can also go over to the PA-220’s web interface and click on “Tasks” in the bottom right corner of the screen and watch the job until it completes.

Figure 05

Once the currently running job completes, your changes are committed to the firewall and you can go about using those newly created Address Objects and the Address Group however you want.

Conclusion and Future Ideas

As I said at the start of this article, this was purely intended as an introduction to how to use the PAN-OS API system. If I were to actually start using this system on a larger scale, there are a number of ways I would implement this. I’ll illustrate just a few of those ideas here:

  1. Securing the API key: I’d take the API key and never let the end users see it as much as possible. I’d make the API key something that was retrievable from a MySQL database that was changed on a random basis so the end users/automated systems would never know for sure when the API key would and wouldn’t change, for security purposes. Instead of making the API key something that they would retrieve, I’d implement a different process where the API key was stored as a system environment variable that the application(s) could access and wouldn’t even need to worry about retrieving the key themselves and/or having to wait that extra time.

  2. Take the different API functions and make them into their own shell script(s)/parameters: This way, instead of my users needing to remember the API calls themselves, I’d instead have them call a shell script of “ -AGName MyAddressGroup -AGMembers AddressObject1,AddressObject2,AddressObject2” and then let the shell script handle all of the heavy lifting for them.

  3. Add in error handling: Just because you think you’ve got your sequence of steps correct doesn’t mean that you didn’t miss something, overlook something or accidentally call an Address Object by the same name as someone else. So, when the “Output” that you saw returned each time is passed back to the script(s), I’d perform checks to make sure that the Address Object was created correctly and, if it wasn’t, react accordingly so the user isn’t left wondering what happened.

Those are just a few of the ideas I’d do to extend what was introduced in this article. The realm of DevOps is extensive and growing every day, so anything that I can do to make my users’ lives easier, I try to implement as much as I can. In the end, what makes their lives easier also makes my own life easier. Learning how to use, control and access APIs is just one of many tools that has allowed me to do just that.


Charles Buege is the senior DevOps engineer for Temeda, an Industrial IoT company out of Naperville, Illinois. He currently holds a PCNSA certification and is working towards his PCNSE. He also runs an IT-based Meetup group called “The IT Crowd”.

More to Explore

Check out these Fuel blog posts for further reading:

Topics: Charles Buege, 10.0 PAN-OS, API

Posts by Topic

see all

Subscribe to Blog Updates

Recent Posts

Posts by Topic

see all