USPS Web Tools guide

Besides its primary role – delivering mail, USPS allows several other functions by means of https://www.usps.com/webtools/. In order to use them you’ll need to make xml in conformance with USPS Web Tools documentation and send it as for example cURL request. Below given is a class that will handle all these cURL requests. It’s the class serving all the requests in this article. We’ll need to pass it only URL, API method name and the XML.

//connect to USPS
class USPS {
	public function connectToUSPS($url, $api, $xml) {
		$ch = curl_init($url);
		curl_setopt($ch, CURLOPT_POST, 1);
		curl_setopt($ch, CURLOPT_POSTFIELDS, $api . $xml);
		curl_setopt($ch, CURLOPT_TIMEOUT, 60);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
 
		$result = curl_exec($ch);
		$error = curl_error($ch);
 
		if (empty($error)) {
			return $result;
		} else {
			return false;
		}
	}
}


Here are the methods and samples that we can use:
1.US address verification (AddressValidateRequest)
First, form the XML with the test address and USERID that we get get by registering at https://www.usps.com/webtools/.

$xml = '<AddressValidateRequest USERID="XXX">
<Address ID="1">
<Address1></Address1>
<Address2>6406 Ivy Lane</Address2>
<City>Greenbelt</City>
<State>MD</State>
<Zip5></Zip5>
<Zip4></Zip4>
</Address>
</AddressValidateRequest>';

USPS address verification API method is called Verify and the URL for it is https://production.shippingapis.com/ShippingAPI.dll.
Let’s pass our XML, URL and method name into USPS::connectToUSPS() :

$usps = new USPS();
$result = $usps->connectToUSPS( 'https://production.shippingapis.com/ShippingAPI.dll', 'API=Verify&amp;XML=', $xml);

Let’s have a look at the resulting XML:


6406 IVY LN GREENBELT MD 20770 1441

We see handful XML where we can obtain validated and normalized address from as well as see it’s valid. We can tweak connectToUSPS() method and parse resulting XML with SimpleXML:

// (...)
if (empty($error)) {
	return new SimpleXMLElement($result);
} else {
	return false;
}
// (...)

Then we can read from returned value as follows:

$usps = new USPS();
$result = $usps->connectToUSPS( 'https://production.shippingapis.com/ShippingAPI.dll', 'API=Verify&amp;XML=', $xml);
 
$address = array(
	'address' => $result->Address[0]->Address2,
	'address2' => $result->Address[0]->Address1,
	'city' => $result->Address[0]->City,
	'state' => $result->Address[0]->State,
	'zip' => $result->Address[0]->Zip5 . (!empty($result->Address[0]->Zip4)
         ? '-' . $result->Address[0]->Zip4 : '')
);

Let’s make the address invalid and see how it looks:

// formation XML
$xml = '<AddressValidateRequest USERID="XXX">
<Address ID="1">
<Address1></Address1>
<Address2> Ivy Lane</Address2>
<City>Greenbelt</City>
<State>MD</State>
<Zip5></Zip5>
<Zip4></Zip4>
</Address>
</AddressValidateRequest>';
$result = $usps->connectToUSPS( 'https://production.shippingapis.com/ShippingAPI.dll', 'API=Verify&amp;XML=', $xml);

As you dump the result you’ll see



-2147219401 API_AddressCleancAddressClean.CleanAddress2;SOLServer.CallAddressDll Address Not Found. 1000440

As we see, the address is not validated. The error is in element. In practice it happens that there is warning in response asking for more detailed info. Often it needs to have suite, apartment etc if Address2 is building. You should populate apt, room, suite in the Address element then.

2. Generating return labels (MerchandiseReturnV4)
USPS Tracking Number – is a unique identifier of a parcel and return label is the label you can send to your customer for return shipment. Here is a sample of Merchandise Return label:

To get it create XML as below:

$xml = '<EMRSV4.0Request USERID="XXXXXX" PASSWORD="XXXXXX">
<Option>RIGHTWINDOW</Option>
<CustomerName>Garrison Johns</CustomerName>
<CustomerAddress1>TEST 40</CustomerAddress1>
<CustomerAddress2>6406 Ivy Lane</CustomerAddress2>
<CustomerCity>Greenbelt</CustomerCity>
<CustomerState>MD</CustomerState>
<CustomerZip5>20770</CustomerZip5>
<CustomerZip4 />
<RetailerName>Reza Dianat</RetailerName>
<RetailerAddress>6406 Ivy Lane</RetailerAddress>
<PermitNumber>293829</PermitNumber>
<PermitIssuingPOCity>Greenbelt</PermitIssuingPOCity>
<PermitIssuingPOState>MD</PermitIssuingPOState>
<PermitIssuingPOZip5>20770</PermitIssuingPOZip5>
<PDUPOBox>6406 Ivy Lane</PDUPOBox>
<PDUCity>Greenbelt</PDUCity>
<PDUState>MD</PDUState>
<PDUZip5>20770</PDUZip5>
<PDUZip4>1234</PDUZip4>
<ServiceType>Bound Printed Matter</ServiceType>
<DeliveryConfirmation>False</DeliveryConfirmation>
<InsuranceValue />
<MailingAckPackageID>ID00001</MailingAckPackageID>
<WeightInPounds>0</WeightInPounds>
<WeightInOunces>10</WeightInOunces>
<RMA>RMA 123456</RMA>
<RMAPICFlag>False</RMAPICFlag>
<ImageType>TIF</ImageType>
<RMABarcode>False</RMABarcode>
<AllowNonCleansedDestAddr>False</AllowNonCleansedDestAddr>
</EMRSV4.0Request>';

We recommend to refer to official USPS doc to understand each of the parameters here.
API USPS Web Tools that allows us to get return label is MerchandiseReturnV4, and the method call for this task will look like:

$usps = new USPS();
$result = $usps->connectToUSPS( 'https://secure.shippingapis.com/ShippingAPI.dll', 'API=MerchandiseReturnV4&XML=', $xml);

To get the image from the response we need to look into

$result->MerchandiseReturnLabel

Note that it is base64 encoded.

file_put_contents('test_tracking.tif',base64_decode($result->MerchandiseReturnLabel));

You can get a copy of label via email (RecipientEMail request parameter):

$xml = '<EMRSV4.0Request USERID="XXXXXX" PASSWORD="XXXXXX">
<Option>RIGHTWINDOW</Option>
<CustomerName>Garrison Johns</CustomerName>
<CustomerAddress1>TEST 40</CustomerAddress1>
<CustomerAddress2>6406 Ivy Lane</CustomerAddress2>
<CustomerCity>Greenbelt</CustomerCity>
<CustomerState>MD</CustomerState>
<CustomerZip5>20770</CustomerZip5>
<CustomerZip4 />
<RetailerName>Reza Dianat</RetailerName>
<RetailerAddress>6406 Ivy Lane</RetailerAddress>
<PermitNumber>293829</PermitNumber>
<PermitIssuingPOCity>Greenbelt</PermitIssuingPOCity>
<PermitIssuingPOState>MD</PermitIssuingPOState>
<PermitIssuingPOZip5>20770</PermitIssuingPOZip5>
<PDUPOBox>6406 Ivy Lane</PDUPOBox>
<PDUCity>Greenbelt</PDUCity>
<PDUState>MD</PDUState>
<PDUZip5>20770</PDUZip5>
<PDUZip4>1234</PDUZip4>
<ServiceType>Bound Printed Matter</ServiceType>
<DeliveryConfirmation>False</DeliveryConfirmation>
<InsuranceValue />
<MailingAckPackageID>ID00001</MailingAckPackageID>
<WeightInPounds>0</WeightInPounds>
<WeightInOunces>10</WeightInOunces>
<RMA>RMA 123456</RMA>
<RMAPICFlag>False</RMAPICFlag>
<ImageType>TIF</ImageType>
<RecipientName>Test</RecipientName>
<RecipientEMail>test@gmail.com</RecipientEMail>
<RMABarcode>False</RMABarcode>
<AllowNonCleansedDestAddr>False</AllowNonCleansedDestAddr>
</EMRSV4.0Request>';

You can access the tracking number itself at

$result->MerchandiseReturnNumber

3. Getting shipment tracking status by its tracking number (TrackRequest)
You have to know only parcel’s tracking num in order to get actual status of its location. You should use https://trkcnfrm1.smi.usps.com/PTSInternetWeb/ service for that, but you can also do the same with Web Tools
Let’s form the XML:

$xml = '<TrackRequest USERID="XXX" PASSWORD="XXX">
<TrackID ID="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"&gt</TrackID>
</TrackRequest&gt';

And send it via TrackV2 API method which is available at https://production.shippingapis.com/ShippingAPI.dll, provided 30-digit tracking number of your shipment in TrackID parameter.

$usps = new USPS();
$result = $usps->connectToUSPS( 'https://production.shippingapis.com/ShippingAPI.dll', 'API=TrackV2&XML=', $xml);

Look at the result:



Your item was delivered at 9:45 am on February 14, 2011 in Greenbelt, MD 20770.
Out for Delivery, February 14, 2011, 8:29 am, Greenbelt, MD 2077.
Sorting Complete, February 14, 2011, 8:19 am, Greenbelt, MD 20770
Arrival at Post Office, February 14, 2011, 8:02 am, Greenbelt, MD 20770.
Processed through Sort Facility, February 13, 2011, 12:05 am, Greenbelt, MD 20770.

We see that in TrackSummary there is the shipment current status, its date and also location. In TrackDetail elements there are some more details.

That’s it. This article is only a brief introduction into USPS Web Tools. You can find very detailed documentation and samples here

Author: Alexandr Cvirovsky

  • Derek

    Bless you for providing this info. The usps docs are wrong and after 10 days of back and forth (mostly forth because it took them 5 days to respond) I finally found this website with working examples. Thanks!

  • Sebastian

    Hi, your guide has been really helpful as there’s not a lot of documentation about it online, ¡thanks!

    However i’m having an issue with the created image, it does not work, the image is created but when I try to open it says that it might be damaged.

    Could you give me some insight on why that can happen?

    Thanks again.

    Sebastian

  • Russ

    Sebastian, most web browsers don’t natively support TIF images. Try using the PDF option instead. Or you can use Image Magick and convert it into a JPG, however, you may lose resolution when doing so as the item being returned by USPS is a TIF encoded bitmap.

  • admin

    Russ, we figured out the issue with Sebastian offline, so it wasn’t a problem with TIFF browser support. Thanks anyway 🙂

  • Russ

    No problem. But the PDF option is better. Can’t beat a vector file when it comes to print resolution. And thanks for putting this tut out there. Really helped me out when I tried to decode the USPS webtools user guides. They are seriously lacking in examples.

  • Neil

    Has anyone had any luck getting multiple address results back with the ZipCodeLookup or Verify APIs?

    I keep getting this undocumented gem of a message:

    “Default address: The address you entered was found but more information is needed (such as an apartment, suite, or box number) to match to a specific address.”

    Of course, I can specify an Apt# and get back a good one to one, but I would like to offer my user suggestions like the USPS.com GUI displays.

  • Hadiman

    thanks man,
    i’ve looking at USPS site and there is no working sample code.

    Thank you,
    you save my live 🙂

  • Kim in Michigan

    I LOVE YOU! Like others I have been DAYS on this and my clients were starting to doubt I knew what I was doing. The kindness and generousity of developers is like nothing you see in any other profession.

  • shana

    I seem to be having a problem with the line:

    $usps = new USPS();

    Is there a class I’m missing? I’d appreciate your help

    Thank you.

  • Michael Martin

    This is what I came up with in both CURL and just plain PHP

    I’m pretty much an amateur “hack” coder, but I don’t quit

    <?php
    /// send request id CURL must use CSS to format
    $request1 = <<< XMLREQUEST

    6406 Ivy Lane
    Greenbelt
    MD

    XMLREQUEST;

    $request = “http://production.shippingapis.com/ShippingAPITest.dll?API=Verify&XML=” . rawurlencode($request1);

    $ch = curl_init();

    curl_setopt($ch, CURLOPT_URL, $request);
    curl_setopt($ch, CURLOPT_HEADER, false);
    curl_setopt($ch, CURLOPT_HTTPGET, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SLL_VERIFYHOST, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

    $response = curl_exec($ch);
    curl_close($ch);

    print_r($response);

    ?>

    <?php

    //send using PHP

    $html = "";
    $url = "http://production.shippingapis.com/ShippingAPITest.dll?API=Verify&XML=&quot; . rawurlencode($request1);

    $xml = simplexml_load_file($url);
    for($i = 0; $i Address[$i]->Address2;
    $City = $xml->Address[$i]->City;
    $State = $xml->Address[$i]->State;
    $Zip5 = $xml->Address[$i]->Zip5;
    $Zip4 = $xml->Address[$i]->Zip4;

    $html .= “Address2”;
    $html .= “$City,  “;
    $html .= “$State”;
    $html .= “$Zip5  “;
    $html .= “$Zip4 “;
    }
    echo $html;
    echo ”;
    echo ”;
    echo $Address2;
    echo ”;
    echo $City; echo ‘, ‘; echo $State;
    echo ”;
    echo $Zip5; echo’&nbsp’;
    echo $Zip4;
    ?>

    Untitled Document

    Address2{
    display: block;
    }
    City{
    display: inline;
    }
    State{
    display: inline;
    }
    Zip4{
    display: block;
    }
    Zip5{
    display:inline;
    }
    #midBox{
    width: 98%;
    }

    <?php
    echo '’;
    echo ”;
    echo $response;
    ?>

  • Michael Martin

    forget it, the system keeps deleting the most important part of the code what a friggin joke!!!!!!!!!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.