Calculating USPS Shipping Rates with PHP
If you have a custom shopping cart and you want to calculate actual shipping rates from USPS this is the post for you. After giving up searching the internet for PHP scripts or examples of implementing USPS rates I decided to create my own. I hope that by the end of this post you will have an understanding of the process and you can begin implementing USPS rates in your own website.
The function discussed below is used for communicating with the United States Postal Service (USPS). If you are looking for the United Postal Service (UPS) function see, Calculating UPS Shipping Rate with PHP.
After looking through the USPS rates manual I begain to try and create a working script. None of the examples in the manual worked for me so I called them up. They ended up just moving my account from testing to production. If you are just starting out I would suggest you call them and start mentioning how their examples didn’t produce the same results and you are attempting to create an application using their API.
When I called USPS the lady on the phone said, “I don’t know what you are talking about. I’ll just move your account to production”. Once I was moved to the production server all of the examples in the user manual worked and I was on my way to creating my very own implementation of the USPS rates API.
I am still not sure why they provide a testing server if it doesn’t work the same way as the production server.
You may wish to grab a copy of the USPS Rates Manual for yourself.
Things you Need
- USPS Webtools Account - free but requires registration
- USPS Username - Comes with the Webtools account
- cURL - Most LAMP (Linux, Apache, Mysql, PHP) web hosts have this installed by default. If you are not sure you have this installed on your server you can use the serverReporter script. Just copy and paste the file upload to your webserver and view it. Go here if you want to know more about the serverReporter.
- PHP - You should know a little bit about using PHP. After all you are about to create a custom USPS shipping calculator.
- XML - If you know what PHP is you should know what XML is. You should at least know how XML works in a nutshell.
- SSH access - Many web hosts offer SSH. You don’t HAVE to have it but it can make troubleshooting easier.
USPS Service Codes
- FIRST CLASS
- PRIORITY
- EXPRESS
- BPM
- PARCEL
- MEDIA
- LIBRARY
- ALL
Required XML Tags to Complete a Rate Request
The USPS API requires cetain XML tags in the request before it will start its rate calculations. If you fail to provide the required tags USPS will return with an error code. I have provided the required XML tags for you.
- RateV3Request - Required Once
- RateV3Request/USERID - Required
- RateV3Request/Package - Required
- RateV3Request/Package/Service - Required
- RateV3Request/Package/ZipOrigination - Required Once
- RateV3Request/Package/Pounds - Required Once (if it weighs less than a lb use ‘0′ Max: 70lbs)
- RateV3Request/Package/Ounces - Required (use ‘0′ if you don’t use ounces)
- RateV3Request/Package/Size - Required (LARGE,REGULAR,OVERSIZE)
Simple XML Request Example
A barebones request was taken straight from the USPS manual. It is a simple XML file that needs to be posted to the USPS server. As you can see it contains all the required XML tags. Of course you will need to change the USERID to your actual USERID that you got from registering with USPS.
<RateV3Request USERID="000AAAAA9999"> <Package ID="1ST"> <Service>FIRST CLASS</Service> <FirstClassMailType>LETTER</FirstClassMailType> <ZipOrigination>44106</ZipOrigination> <ZipDestination>90210</ZipDestination> <Pounds>0</Pounds> <Ounces>0.75</Ounces> <Size>REGULAR</Size> <Machinable>false</Machinable> </Package> </RateV3Request>
If you noticed there was only one package included in this request. The USPS API allows multiple packages in one XML request. To lighten the load and time it takes to generate rates for multiple packages it is recommended that you send a single XML request with multiple packages as opposed to sending multiple XML requests for single packages.
You can add more packages to the code by adding another XML package set:
<Package ID="2nd">
The USPS Rate Calculator PHP Function
Finally, the code that you have all been waiting for. This is the PHP function that will return the USPS rate as a decimal value for use in displaying or calculating the shipping cost and final charge to the customer.
<?php
function USPSParcelRate($weight,$dest_zip) {
// This script was written by Mark Sanborn at http://www.marksanborn.net
// If this script benefits you are your business please consider a donation
// You can donate at http://www.marksanborn.net/donate.
// ========== CHANGE THESE VALUES TO MATCH YOUR OWN ===========
$userName = 'username'; // Your USPS Username
$orig_zip = '12345'; // Zipcode you are shipping FROM
// =============== DON'T CHANGE BELOW THIS LINE ===============
$url = "http://Production.ShippingAPIs.com/ShippingAPI.dll";
$ch = curl_init();
// set the target url
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
// parameters to post
curl_setopt($ch, CURLOPT_POST, 1);
$data = "API=RateV3&XML=<RateV3Request USERID=\"$userName\"><Package ID=\"1ST\"><Service>PRIORITY</Service><ZipOrigination>$orig_zip</ZipOrigination><ZipDestination>$dest_zip</ZipDestination><Pounds>$weight</Pounds><Ounces>0</Ounces><Size>REGULAR</Size><Machinable>TRUE</Machinable></Package></RateV3Request>";
// send the POST values to USPS
curl_setopt($ch, CURLOPT_POSTFIELDS,$data);
$result=curl_exec ($ch);
$data = strstr($result, '<?');
// echo '<!-- '. $data. ' -->'; // Uncomment to show XML in comments
$xml_parser = xml_parser_create();
xml_parse_into_struct($xml_parser, $data, $vals, $index);
xml_parser_free($xml_parser);
$params = array();
$level = array();
foreach ($vals as $xml_elem) {
if ($xml_elem['type'] == 'open') {
if (array_key_exists('attributes',$xml_elem)) {
list($level[$xml_elem['level']],$extra) = array_values($xml_elem['attributes']);
} else {
$level[$xml_elem['level']] = $xml_elem['tag'];
}
}
if ($xml_elem['type'] == 'complete') {
$start_level = 1;
$php_stmt = '$params';
while($start_level < $xml_elem['level']) {
$php_stmt .= '[$level['.$start_level.']]';
$start_level++;
}
$php_stmt .= '[$xml_elem[\'tag\']] = $xml_elem[\'value\'];';
eval($php_stmt);
}
}
curl_close($ch);
// echo '<pre>'; print_r($params); echo'</pre>'; // Uncomment to see xml tags
return $params['RATEV3RESPONSE']['1ST']['1']['RATE'];
}
?>
I have created a working model of the rates calculation that will compare rates between USPS and UPS.
To use the function you use the following format:
USPSParcelRate($weight,$dest_zip)
An example of this being called on your website would be:
echo USPSParcelRate(3,90210);
Troubleshooting
If you are not recieving a rate from the USPS servers you need to check what error message they gave. My script will output the entire XML response in html comment tags. This will allow you to view any errors generated by the USPS API. To view this you will need to uncomment the lines specified in the function and simply click view source in your browser and look for the XML data.
Future Versions and More
I plan to create a more intricate version that will have many more options. Instead of being a simple function it will be a complete rate calculation tool. I plan to eventually create a framework that allows developers an easy and quick way to develop entire full feature shipping modules. The current open source shopping projects in my opinion are very lacking and do not really allow the users complete control to integrate with their own systems or systems that are already in place. The current solutions are more like a pre-built website with a shipping module. My framework would be for those who wish to create their own shipping systems or integrate with an existing one.
So, what would you like to see in a framework? What options and configurations would you like to see? Please leave a comment below.
Also stay tuned for my post about how I created a script that calculates how many widgets will fit in a box before having to add another box to the shipping total. Many sites will end up charging the customer one package for the total weight of the cart. Other times they will charge as if each widget was shipped in its own box. I will discuss why each of these methods are flawed and how I found a solution.






[...] If you are looking for the United States Postal Service (USPS) function see, Calculating USPS Shipping Rates with PHP. [...]
$orig_Zip = ‘12345′; // Zip code you are shipping FROM
change that to
$orig_zip = ‘12345′ // the capital letter (of the Z) will cause problem
Lol, Nice catch.
When I wrote the function I tested everything and then added those variable declarations after the fact and forgot to test it again.
The USPS testing server is still non functional. I called the 800 number and told them that. I then told them that “I had heard that the testing server was broken, but the production server worked.” The gal just enabled my account for the production server.
I just want you share that the test server is still broken.
Dirkus,
Thanks for the status report.
[...] Calculating USPS Shipping Rates with PHP [...]
I must be doing something wrong. I have my document uspstest.php as follows:
This calls on your php function, obviously filled out with my user name and zip code (do I not need my password?). But with the lines uncommented all I get is:
Array
(
)
Perhaps this is due to the server problems you mentioned. I just wanted to make sure I was taking what you have here and implementing it properly.
By the way, you are my hero because I was able to use your UPS shipping calculator successfully. I’ve had a headache all week trying to figure this stuff out. Is there any chance you could email me your “working model” — without your account info obviously — because that’s exactly what I’m trying to do.
Thanks for everything!
Oops. I guess my uspstest.php didn’t show because the enclosing code. Here it is without:
include(”usps.php”);
$weight = ‘5′;
$dest_zip = ‘90210′;
echo USPSParcelRate($weight,$dest_zip);
Hi I am getting this :
Array
(
)
with nothing working ?
you know what is wrong?
Bill,
Are you on production or testing server? do you have the username and password put in there?
Mark,
How would I go about getting rates for both “PARCEL” and “PRIORITY”, the last line which returns the value seems to be set in stone:
return $params['RATEV3RESPONSE']['1ST']['17']['RATE'];
PARCEL would be :
return $params['RATEV3RESPONSE']['1ST']['4']['RATE'];
Mark,
It seems I have found the answer to my own post,
instead of:
return $params['RATEV3RESPONSE']['1ST']['1']['RATE'];
I use:
return $params['RATEV3RESPONSE']['1ST'][$level['3']]['RATE'];
This allows for all params to be returned.
I have heavily modified your script, and it works great!
Carl,
I wrote a seperate function for parcel vs priority.
Glad you were able to figure it out!
I keep getting two “Undefined offset: 1″ errors, both in this line:
list($level[$xml_elem['level']],$extra) = array_values($xml_elem['attributes']);
I have tried unsuccessfully to fix this. I know it has something to do with an undefined set in the arrays. I have not changed any of the code. I also see no one else reporting any errors like this.
Any help at all would be greatly appreciated.