Getting Started with the FedEx API – Part 2
Here you will learn how to write a working program to utilize part of the FedEx API. Although the program we will write looks short, it represents hours of trial and error, 5 support requests, and a lively discussion on the Python mailing list! So make sure you savor this advice thouroughly
Firstly, go read part 1 of this guide if you haven’t already. You’ll need to have done everything listed there before any of this will work.
So what I am going to show you is how to write some Python code that will return an estimated shipping cost from the FedEx server depending on your parameters.
Here is what the final function will look like and how it is used:
print rate_request(service='FEDEXGROUND',origin_state='MD',origin_zip='20878',origin_country='US',
dest_state='MD',dest_zip='20905',dest_country='US',
packaging='YOURPACKAGING',weight='2.0',declared_value='100.00')
#it simply prints the price such as u'8.00'.
So without further suspense, here is the code. There are a few comments in there and I go over a few things at the end of this post.
"""
A Simple FedEx API script for Python
-Greg Pinero, February, 2006
"""
import sys
import urllib2
import urllib
import xml.dom.minidom
#You fill in the 4 variables below to make this code work.
FedEx_API_URL="https://example.fedex.com:443/GatewayDC" #email websupport@fedex.com for the correct URL
#to their testing server.
MeterNumber='1111111' #continue reading, you'll have your own soon enough!
FedExAccountNumber='111111111'
YourCompanyName='Hatson'
#We send the string below to the FedEx server just once ever in order to
#get our meter number. NOTE: remember to fill in your own information
#below.
xml_subscription_request="""<?xml version="1.0" encoding="UTF-8" ?>
<FDXSubscriptionRequest xmlns:api="http://www.fedex.com/fsmapi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="FDXSubscriptionRequest.xsd">
<RequestHeader>
<CustomerTransactionIdentifier>String</CustomerTransactionIdentifier>
<AccountNumber>111111111</AccountNumber>
</RequestHeader>
<Contact>
<PersonName>Jim Smith</PersonName>
<CompanyName>Company</CompanyName>
<Department>Shipping</Department>
<PhoneNumber>3019871110</PhoneNumber>
</Contact>
<Address>
<Line1>9121 Washington Blvd.</Line1>
<Line2>Suite 220</Line2>
<City>Gaithersburg</City>
<StateOrProvinceCode>MD</StateOrProvinceCode>
<PostalCode>20878</PostalCode>
<CountryCode>US</CountryCode>
</Address>
</FDXSubscriptionRequest>"""
def rate_request(service,origin_state,origin_zip,origin_country,dest_state,dest_zip,dest_country,
packaging='YOURPACKAGING',weight='10.0',declared_value='100.00'):
"""
Handles all your rate requesting needs.
"""
ValidServices=[
'PRIORITYOVERNIGHT',
'STANDARDOVERNIGHT',
'FIRSTOVERNIGHT',
'FEDEX2DAY',
'FEDEXEXPRESSSAVER',
'INTERNATIONALPRIORITY',
'INTERNATIONALECONOMY',
'INTERNATIONALFIRST',
'FEDEX1DAYFREIGHT',
'FEDEX2DAYFREIGHT',
'FEDEX3DAYFREIGHT',
'FEDEXGROUND',
'GROUNDHOMEDELIVERY',
'INTERNATIONALPRIORITY FREIGHT',
'INTERNATIONALECONOMY FREIGHT',
'EUROPEFIRSTINTERNATIONALPRIORITY',
]
ValidPackaging=[
'FEDEXENVELOPE',
'FEDEXPAK',
'FEDEXBOX',
'FEDEXTUBE',
'FEDEX10KGBOX',
'FEDEX25KGBOX',
'YOURPACKAGING',
]
if not service in ValidServices:print service,'not in',ValidServices
if not packaging in ValidPackaging:print packaging,'not in',ValidPackaging
if service in ['FEDEXGROUND']:carrier_code='FDXG'
else:carrier_code='FDXE'
xml_rate_request="""<?xml version="1.0" encoding="UTF-8" ?>
<FDXRateRequest xmlns:api="http://www.fedex.com/fsmapi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="FDXRateRequest.xsd">
<RequestHeader>
<CustomerTransactionIdentifier>CTIString</CustomerTransactionIdentifier>
<AccountNumber>%s</AccountNumber>
<MeterNumber>%s</MeterNumber>
<CarrierCode>%s</CarrierCode>
</RequestHeader>
<DropoffType>REGULARPICKUP</DropoffType>
<Service>%s</Service>
<Packaging>%s</Packaging>
<WeightUnits>LBS</WeightUnits>
<Weight>%s</Weight>
<OriginAddress>
<StateOrProvinceCode>%s</StateOrProvinceCode>
<PostalCode>%s</PostalCode>
<CountryCode>%s</CountryCode>
</OriginAddress>
<DestinationAddress>
<StateOrProvinceCode>%s</StateOrProvinceCode>
<PostalCode>%s</PostalCode>
<CountryCode>%s</CountryCode>
</DestinationAddress>
<Payment>
<PayorType>SENDER</PayorType>
</Payment>
<DeclaredValue>
<Value>%s</Value>
<CurrencyCode>USD</CurrencyCode>
</DeclaredValue>
<PackageCount>1</PackageCount>
</FDXRateRequest>""" % (FedExAccountNumber,MeterNumber,carrier_code,
service,packaging,weight,origin_state,origin_zip,
origin_country,dest_state,dest_zip,dest_country,
declared_value)
xml_result=make_FedEx_Request(xml_rate_request)
result=parse_FedEx_Reply(xml_result)
return result
def parse_FedEx_Reply(xml_result):
"""A quick function to get my shipping charge"""
d = xml.dom.minidom.parseString(xml_result)
if len(d.getElementsByTagName('BaseCharge'))>0:
base_charge=d.getElementsByTagName('BaseCharge')[0].firstChild.data
else:
#base_charge="FedEx Error"
if len(d.getElementsByTagName('Message'))>0:
base_charge=d.getElementsByTagName('Message')[0].firstChild.data
else:
base_charge=d.toprettyxml()
return base_charge
def make_FedEx_Request(xml_request):
"""Make your XML request and send it through here"""
headers = { 'Referer' : YourCompanyName,
'Accept':'image/gif, image/jpeg, image/pjpeg, text/plain, text/html, */*',
'Content-Type':'image/gif'
}
req = urllib2.Request(FedEx_API_URL,xml_request, headers)
try:
handle = urllib2.urlopen(req)
except IOError, e:
if hasattr(e, 'reason'):
print 'We failed to reach a server.'
print 'Reason: ', e.reason
print e.info()
elif hasattr(e, 'code'):
print 'The server couldn't fulfill the request.'
print 'Error code: ', e.code
print e.info()
raise IOError
else:
the_page = handle.read()
return the_page
if __name__=='__main__':
#Let's make our first subscription request and print the entire result.
#Find your meter number somewhere in the result.
print make_FedEx_Request(xml_subscription_request)
As the comments in the code mention, the first thing you’ll need before any code will work is a meter number. You get this by sending the FedEx API server a special subscription request. You can see my code doing this in the if __name__=='__main__': section. You only need to do this once ever, then you just plug in your meter number into the code.
After that, everything should be pretty self-explanatory. The wonderful Python library urllib2 handles all of the SSL encryption stuff for us automatically. Once you have your code working, I believe there is a reasonably painless FedEx approval process, and then they move you over to their production server. I haven’t gotten to that point yet so I can’t shed much light there.
Let me know how it goes for you. If you improve upon this code or just want to critique it, feel free to do it here. And if you want to post any new Python code for the FedEx API in this website, that is welcome as well.
hi,
Enjoy the fedex rate api. Good luck getting them to provide a proper rate on a multi-piece shipment where each package has a distinct weight.
Same problem for the Ship Api.
It doesn’t work. Please complain to your Fedex sales rep. I certainly need it fixed.
You should change “import XML.dom.minidom” to “import xml.dom.minidom”.
Hmm, the CMS changes the small letter to big ones. What I meant was “x m l” instead of ” X M L”.
You’re right Oliver. It looks like my CMS changed that even in my original code. I’ll fix that right now.
Thanks for letting me know.
This is in response to Brad Clements comments about not being able to get a proper rate on a multi piece shipment where each package has a distinct weight.
I am currently in the process of intergrating the RateAvailableServices and Ship API.
I don’t see anywhere in the RAS API which lets me enter dimensional or weight info for more than one package.
FedEx is very vague and unsure about its multi-peice ratings and shipment.
Has anyone else enountered this problem? Any solutions out there?
Thanks.
Hi Kal,
I don’t have a good answer for you there. Have you tried the FedEx support? Anywhere they make there API ambiguous they deserve to hear about it.