Welcome to Royal Mail Rest API’s documentation!

Royal Mail Rest API

https://img.shields.io/pypi/v/royal_mail_rest_api.svg https://api.travis-ci.org/Bobspadger/royal_mail_rest_api.svg Documentation Status

A small helper for integrating python with Royal Mails Rest API

This is to help with getting started integrating Royal Mails shipping and Tracking API’s into your project.

They do have an Open API to build this with Swagger-Codegen, but I found there were a few issues with this. 1. Horrible final code - overly verbose and complicated. 1. Half the time it would not build (needed beta versions of swagger-codegen) and then it would still not work.

This is cleaner, easier to modify and extend, and a lot lighter weight, only using the standard library.

It is VERY MUCH a work in progress, so help is hugely appreciated, and be careful, it may change as I implement more features / improve and enhance what we already have.

Features

Create Labels Update Labels Create Manifests Post Manifests

Credits

This package was created with Cookiecutter and the audreyr/cookiecutter-pypackage project template.

Installation

Stable release

To install Royal Mail Rest API, run this command in your terminal:

$ pip install royal_mail_rest_api

This is the preferred method to install Royal Mail Rest API, as it will always install the most recent stable release.

If you don’t have pip installed, this Python installation guide can guide you through the process.

From sources

The sources for Royal Mail Rest API can be downloaded from the Github repo.

You can either clone the public repository:

$ git clone git://github.com/bobspadger/royal_mail_rest_api

Or download the tarball:

$ curl  -OL https://github.com/bobspadger/royal_mail_rest_api/tarball/master

Once you have a copy of the source, you can install it with:

$ python setup.py install

Usage

To use Royal Mail Rest API in a project:

Note

(this is from the example.py file which will be updated, its worth looking there)

import json
from royal_mail_rest_api.tools import RoyalMailBody
from royal_mail_rest_api.shipping import ShippingApi
from royal_mail_rest_api.tracking import TrackingApi
from royal_mail_rest_api.get_credentials import return_credentials

if __name__ == '__main__':
    # Get our API credentials (from credentials.ini)
    creds = return_credentials()

    # make some nice easy to read variables for authenticating with
    CLIENT_ID = creds['royal_mail']['CLIENT_ID']
    CLIENT_SECRET = creds['royal_mail']['CLIENT_SECRET']
    USERNAME = creds['royal_mail']['USERNAME']
    PASSWORD_HASHED = creds['royal_mail']['PASSWORD_HASHED']

    # Create a new delivery object to make our address etc with
    body = RoyalMailBody('Delivery')

    # Add our items to it
    body.add_ship_date(None)
    body.add_service('P', 1, 'TPN', 'T', True,  ['14'])
    body.customer_reference = 'D123456'
    body.department_reference = 'Q123456'
    body.sender_reference = 'A123456'
    body.add_items(1, 100, 'g')
    body.add_receipient_contact('Joe Bloggs', 'joe.bloggs@royalmail.com', None, '07970810000')
    body.add_receipient_address('Broadgate Circle', 'London', None, 'EC1A 1BB', country='GB', building_number='1',
                                address_line2='Add line 2', address_line3='Add line 3', building_name='My building')

    # Request our body to use to request a label from royal mail
    my_rm_body = body.return_domestic_body()


    # Create a shipping object, populate it with our credentials
    my_shipping = ShippingApi(CLIENT_ID, CLIENT_SECRET, USERNAME, PASSWORD_HASHED)
    # Request an authentication token
    my_shipping.get_token()
    # Post a shipping request
    post_shipping = my_shipping.post_domestic(my_rm_body)

    # Store our tracking id for use.
    tracking_ref = post_shipping['completedShipments'][0]['shipmentItems'][0]['shipmentNumber']

    # Get a label ! this is base64 encoded
    label = my_shipping.put_shipment_label(tracking_ref)

    # Now lets change some info about the receipient
    body.add_receipient_contact('Alex Hellier', 'alex@me.com', 'Alex S Hellier', '123455')
    # And get our update body - this is slightly different from the original request
    new_data = body.return_domestic_update_boy()
    # Request a change
    change_name = my_shipping.put_shipment(tracking_ref, new_data)
    # Request a new label
    new_label = my_shipping.put_shipment_label(tracking_ref)

    # Lets delete the post request
    delete_shipping = my_shipping.delete_shipment(tracking_ref)

    # If we have some labels to manifest - request it
    manifest_info = {'yourReference': '123'}
    manifest_data = my_shipping.post_manifest(manifest_info)
    # Get the manifest doumentation - note, you will need the maniefest number to get this
    manifest_label = my_shipping.put_manifest(manifest_batch_number=5)


    # Now, a period of time has passed, we can track those packages
    tracking_api = TrackingApi(CLIENT_ID, CLIENT_SECRET)
    try:
        test_tracking = tracking_api.summary(tracking_ref)
        print(test_tracking)
    except Exception as e:
        print(e)

    try:
        test_pod = tracking_api.proof_of_delivery(tracking_ref)
        print(test_pod)
    except Exception as e:
        print(e)

    try:
        history_tracking = tracking_api.history(tracking_ref)
        print(history_tracking)
    except Exception as e:
        print(e)

Example Shipping object from Royal Mail

{
"shipmentType":"Delivery",
 "service":{
            "format":"P",
            "occurrence":"1",
            "offering":"TPN",
            "type":"T",
            "signature":"true",
            "enhancements":["14"

            ]
         },
"shippingDate":"2017-09-25",
"items":[
   {
     "count":1,
     "weight":{
                   "unitOfMeasure":"g",
                   "value":100
      }
    }
  ],
  "recipientContact":{
     "name":"Joe Bloggs",
     "complementaryName":"null",

     "email":"joe.bloggs@royalmail.com"
     },

   "recipientAddress":{
     "buildingName":"Cable and Engineering Limited",
     "buildingNumber":"1",
     "addressLine1":"Broadgate Circle",
     "addressLine2":"Address line 2",
     "addressLine3":"Address Line 3",
     "postTown":"London",
     "country":"GB",
     "postCode":"EC1A 1BB"
    },
  "senderReference":"Senders Ref",
  "departmentReference":"Dept Ref",
  "customerReference":"Do not use",
  "safePlace":"null"
}

royal_mail_rest_api

royal_mail_rest_api package

Submodules

royal_mail_rest_api.api module

Main module.

class royal_mail_rest_api.api.RoyalMailBaseClass[source]

Bases: object

BASE CLASS FOR SHIPPING

url = 'https://api.royalmail.net'

royal_mail_rest_api.errors module

exception royal_mail_rest_api.errors.GeneralError[source]

Bases: Exception

exception royal_mail_rest_api.errors.NotAuthorised[source]

Bases: Exception

exception royal_mail_rest_api.errors.RoyalMailError[source]

Bases: Exception

royal_mail_rest_api.example module

royal_mail_rest_api.get_credentials module

royal_mail_rest_api.get_credentials.return_credentials()[source]

royal_mail_rest_api.shipping module

class royal_mail_rest_api.shipping.ShippingApi(client_id, client_secret, username, password)[source]

Bases: royal_mail_rest_api.api.RoyalMailBaseClass

Royal Mail Shipping Class, used to communicate with the Royal Mail Rest API to create labels

delete_shipment(shipment_number)[source]

Description

Delete a shipment. Send a shipment identifier in Url. Successful response will be 200 with no content.

Returns:
delete_shipment_url = '/shipping/v2/'
get_token()[source]

Method to get a JWT token

This method will accept a DMO/NEOPOST user name and password. On successful validation of the user credential it will issue a JWT token to the user which will be valid for 4 hours. On subsequent requests, user will pass the JWT token in the request header.

Returns:
post_domestic(data)[source]

Operation to create a shipment

This method will take a domestic shipment request in the body and on successful response, it will return the shipment numbers and item details. :return:

post_domestic_url = '/shipping/v2/domestic'
post_manifest(manifest_options=None)[source]

This method creates a shipping manifest

Returns:
post_manifest_url = '/shipping/v2/manifest'
put_manifest(sales_order_number=None, manifest_batch_number=None)[source]

This method return a manifest label for a previously manifested shipment.

Returns:
put_shipment(shipment_number, data)[source]

updateShipment

Update a shipment. Send a shipment request in body. On successful response, it will return shipment number and warnings. Service related information can not be updated, and if passed as part of request, it will be ignored.

Returns:
put_shipment_label(shipment_number)[source]

This method returns a label for the shipment identifier passed in the url.

Returns:
put_shipment_label_url = '/shipping/v2/'
put_shipment_update_url = '/shipping/v2/'
token_url = '/shipping/v2/token'

royal_mail_rest_api.tools module

class royal_mail_rest_api.tools.RoyalMailBody(shipment_type)[source]

Bases: object

add_items(number, weight, unit_of_measure)[source]

Add items- currently only a single item :param number: :param weight: :param unit_of_measure: :return:

add_receipient_address(address_line1, post_town, county, postcode, country, building_name=None, building_number=None, address_line2=None, address_line3=None)[source]

Add address of receipient :param address_line1: :param post_town: :param county: :param postcode: :param country: :param building_name: :param building_number: :param address_line2: :param address_line3: :return:

add_receipient_contact(name, email, complementary_name=None, telephone=None)[source]

Add the name and contact of who this is being sent to :param name: :param email: :param complementary_name: :param telephone: :return:

add_service_enhancements(enhancement)[source]

add a single service enhancement, can be called multiple times to append required items :param enhancement: :return:

add_service_format(format=None)[source]

add a valid service format using our friendly names :param format: :return:

add_service_occurence()[source]
add_service_offering(service_offering=None)[source]

add a valid service offering using our friendly names :param service_offering: :return:

add_service_type(service_type=None)[source]

add a valid service type using our friendly names :param service_type: :return:

add_ship_date(date_obj=None)[source]

take a datetime object and format it to royal mails Y-m-d format :param date_obj: :return:

add_signature(signature=False)[source]

Do we want a signature on delivery :param signature: :return:

static remove_none_values(iterable)[source]

take out values of None by removing the key :param iterable: :return: dictionary

return_domestic_body()[source]

build domestic body from items :return:

return_domestic_update_boy()[source]

build domestic body from items :return:

service_enhancements = {'loss_2500': '2', 'e-mail_notification': '14', 'saturday_guaranteed': '24', 'sms_notification': '13', 'safeplace': '15', 'loss_10000': '5', 'signature': '12', 'sms_and_e-mail_notification': '16', 'recorded': '6', 'local_collect': '22', 'loss_750': '11', 'loss_5000': '3', 'loss_1000': '1', 'loss_7500': '4'}
service_formats = {'inland_format_not_applicable': 'N', 'international_parcel': 'E', 'inland_parcel': 'P', 'inland_large_letter': 'F', 'international_letter': 'P', 'inland_letter': 'L', 'international_large_letter': 'G', 'international_format_not_applicable': 'N'}
service_offerings = {'international_tracked_on_account': 'OTA', 'royal_mail_48_sort8_ll_daily_rate': 'RM9', 'intl_bus_mail_signed': 'MTM', 'intl_bus_mail_l_ltr_ctry_srt_lo_vol_economy_': 'DG6', 'intl_bus_mail_mixed_zero_sort_premium': 'ZC1', 'intl_bus_parcels_zero_sort_hi_vol_priority_i': 'DE1', 'intl_bus_mail_signed_country_priced': 'MTO', 'intl_bus_mail_mixed_zone_sort_priority': 'OZ1', 'royal_mail_24_p_flat_rate': 'RM5', 'royal_mail_48_sort8_ll_flat_rate': 'FS2', 'royal_mail_tracked_48_lbt_hv': 'TRL', 'intl_bus_parcels_zone_sort_plus_priority': 'MTQ', 'royal_mail_tracked_24_lbt': 'TRN', 'intl_bus_parcels_zero_sort_priority': 'WE1', 'intl_bus_parcels_tracked_and_signed': 'MTA', 'intl_bus_mail_lrg_ltr_max_sort_economy': 'PS8', 'intl_standard_on_account': 'OLA', 'intl_bus_mail_l_ltr_ctry_srt_hi_vol_priority_': 'DG1', 'intl_bus_parcel_track&sign_xtr_cmp_ctry': 'MTF', 'intl_bus_mail_mixed_zero_sort_priority': 'WW1', 'sd_guaranteed_by_9am_2500': 'SD6', 'intl_tracked_and_signed_on_acct_extra_comp': 'OTD', 'intl_bus_parcels_signed_country_priced': 'MP9', 'intl_economy_on_account': 'OLS', 'intl_bus_mail_tracked_and_signed_xtr_comp': 'MTD', 'intl_bus_mail_tracked_extra_comp_ctry': 'MTL', 'royal_mail_48_sort8_p_flat_rate': 'PK2', 'royal_mail_tracked_24': 'TPN', 'intl_bus_mail_mixed_zero_sort_pri_mch': 'WW4', 'royal_mail_24_sort8_ll_flat_rate': 'FS1', 'intl_bus_mail_tracked_and_signed': 'MTC', 'royal_mail_tracked_48_hv': 'TPL', 'intl_bus_mail_signed_extra_comp_country': 'MTP', 'intl_bus_mail_lrg_ltr_zero_srt_priority': 'WG1', 'sd_guaranteed_by_1pm_2500': 'SD3', 'intl_bus_mail_mixed_zero_sort_economy': 'WW3', 'intl_bus_mail_lrg_ltr_zero_sort_economy': 'WG3', 'sd_guaranteed_by_9am_1000': 'SD5', 'royal_mail_tracked_returns_48': 'TSS', 'intl_bus_parcels_signed_extra_comp': 'MP6', '1st_and_2nd_class_account_mail': 'STL', 'intl_bus_parcels_tracked_country_priced': 'MP7', 'intl_bus_mail_lrg_ltr_zone_sort_economy': 'IG3', 'intl_bus_parcels_zone_sort_priority_': 'IE1', 'royal_mail_48_sort8_llp_daily_rate': 'PK4', 'royal_mail_48_ll_flat_rate': 'PK0', 'royal_mail_tracked_returns_24': 'TSN', 'royal_mail_48_sort8_p_daily_rate': 'RM0', 'royal_mail_24_sort8_ll_daily_rate': 'RM7', 'intl_bus_mail_mixd_zero_srt_economy_mch': 'WW6', 'royal_mail_24_sort8_llp_daily_rate': 'PK3', 'intl_bus_parcels_zero_sort_economy': 'WE3', 'royal_mail_tracked_24_lbt_hv': 'TRM', 'intl_bus_parcels_print_direct_priority': 'MB1', 'intl_bus_mail_track_and_sign_xtr_comp_ctry': 'MTH', 'royal_mail_24_ll_flat_rate': 'PK9', 'royal_mail_24_sort8_p_daily_rate': 'RM8', 'intl_bus_mail_lrg_ltr_max_sort_standard': 'PSB', 'royal_mail_24_48_p_flat_rate': 'PPF', 'sd_guaranteed_by_1pm_1000': 'SD2', 'intl_bus_mail_mixed_zone_sort_pri_mch': 'OZ4', 'intl_bus_parcels_max_sort_standard': 'PSC', 'royal_mail_tracked_48_lbt': 'TRS', 'intl_bus_parcels_max_sort_economy': 'PS0', 'intl_bus_mail_mixed_zone_srt_economy_mch': 'OZ6', 'intl_bus_parcels_zone_sort_economy': 'IE3', 'intl_bus_mail_tracked': 'MTI', 'royal_mail_48_p_daily_rate': 'RM4', 'royal_mail_24_48': 'CRL', 'international_tracked_and_signed_on_acct': 'OTC', 'intl_bus_mail_l_ltr_zero_srt_economy_mch': 'WG6', 'intl_tracked_on_account_extra_comp': 'OTB', 'intl_bus_parcels_signed': 'MP5', 'intl_bus_mail_l_ltr_ctry_srt_lo_vol_priority_': 'DG4', 'intl_signed_on_account_extra_comp': 'OSB', 'intl_bus_mail_tracked_extra_comp': 'MTJ', 'intl_bus_parcels_zero_sort_hi_vol_economy_': 'DE3', 'intl_bus_parcels_zone_srt_plus_economy': 'MTS', 'intl_bus_parcels_zero_srt_lo_vol_priority_': 'DE4', 'intl_bus_mail_tracked_and_signed_country': 'MTG', 'royal_mail_24_sort8_p_flat_rate': 'PK1', 'royal_mail_48_p_flat_rate': 'RM6', 'intl_bus_mail_lrg_ltr_zone_srt_pri_mch': 'IG4', 'royal_mail_tracked_48': 'TPS', 'intl_bus_parcels_print_direct_economy': 'MB3', 'intl_bus_mail_l_ltr_zone_srt_economy_mch_': 'IG6', 'intl_bus_mail_l_ltr_ctry_srt_hi_vol_economy_': 'DG3', 'intl_bus_mail_signed_extra_comp': 'MTN', 'royal_mail_24_ll_daily_rate': 'RM1', 'sd_guaranteed_by_9am': 'SD4', 'intl_bus_parcels_signed_extra_comp_ctry': 'MP0', 'intl_bus_parcels_tracked_extra_comp_ctry': 'MP8', 'royal_mail_48_ll_daily_rate': 'RM3', 'intl_bus_mail_lrg_ltr_max_sort_priority': 'PS7', 'intl_bus_parcels_tracked_and_signed__ctry': 'MTE', 'intl_bus_parcels_max_sort_priority': 'PS9', 'intl_bus_mail_lrg_ltr_zone_sort_pri': 'IG1', 'intl_bus_parcels_tracked_extra_comp': 'MP4', 'intl_bus_mail_mixed_zone_sort_economy': 'OZ3', 'sd_guaranteed_by_1pm': 'SD1', 'intl_bus_mail_tracked_country_priced': 'MTK', 'royal_mail_24_p_daily_rate': 'RM2', 'intl_bus_parcels_print_direct_standard': 'MB2', 'international_signed_on_account': 'OSA', 'intl_bus_parcels_zero_srt_lo_vol_economy_': 'DE6', 'intl_bus_parcels_tracked_signed_xtr_comp': 'MTB', 'royal_mail_tracked_24_hv': 'TPM', 'intl_bus_mail_lrg_ltr_zero_srt_pri_mch': 'WG4', 'intl_bus_parcels_tracked': 'MP1'}
service_types = {'special_delivery': 'D', 'tracked_returns': 'R', 'international': 'I', 'royal_mail_24': '1', 'BFPO': 'H', 'royal_mail_tracked': 'T', 'Royal Mail 48': '2'}

royal_mail_rest_api.tracking module

class royal_mail_rest_api.tracking.TrackingApi(client_id, client_secret)[source]

Bases: royal_mail_rest_api.api.RoyalMailBaseClass

Start class for royal mail shipping api

history(tracking_number)[source]

Return history for a tracked item :param tracking_number: :return:

history_url = 'mailPieces/{}/history'
pod_url = 'mailPieces/{}/proofOfDelivery'
proof_of_delivery(tracking_number)[source]

recover proof of delivery :param tracking_number: :return:

summary(tracking_number)[source]

takes 13 digit tracking number and requests summary data :param tracking_number: :return: tracking_summary

summary_url = 'mailPieces/{}/summary'

Module contents

Top-level package for Royal Mail Rest API.

Contributing

Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given.

You can contribute in many ways:

Types of Contributions

Report Bugs

Report bugs at https://github.com/bobspadger/royal_mail_rest_api/issues.

If you are reporting a bug, please include:

  • Your operating system name and version.
  • Any details about your local setup that might be helpful in troubleshooting.
  • Detailed steps to reproduce the bug.

Fix Bugs

Look through the GitHub issues for bugs. Anything tagged with “bug” and “help wanted” is open to whoever wants to implement it.

Implement Features

Look through the GitHub issues for features. Anything tagged with “enhancement” and “help wanted” is open to whoever wants to implement it.

Write Documentation

Royal Mail Rest API could always use more documentation, whether as part of the official Royal Mail Rest API docs, in docstrings, or even on the web in blog posts, articles, and such.

Submit Feedback

The best way to send feedback is to file an issue at https://github.com/bobspadger/royal_mail_rest_api/issues.

If you are proposing a feature:

  • Explain in detail how it would work.
  • Keep the scope as narrow as possible, to make it easier to implement.
  • Remember that this is a volunteer-driven project, and that contributions are welcome :)

Get Started!

Ready to contribute? Here’s how to set up royal_mail_rest_api for local development.

  1. Fork the royal_mail_rest_api repo on GitHub.

  2. Clone your fork locally:

    $ git clone git@github.com:your_name_here/royal_mail_rest_api.git
    
  3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development:

    $ mkvirtualenv royal_mail_rest_api
    $ cd royal_mail_rest_api/
    $ python setup.py develop
    
  4. Create a branch for local development:

    $ git checkout -b name-of-your-bugfix-or-feature
    

    Now you can make your changes locally.

  5. When you’re done making changes, check that your changes pass flake8 and the tests, including testing other Python versions with tox:

    $ flake8 royal_mail_rest_api tests
    $ python setup.py test or py.test
    $ tox
    

    To get flake8 and tox, just pip install them into your virtualenv.

  6. Commit your changes and push your branch to GitHub:

    $ git add .
    $ git commit -m "Your detailed description of your changes."
    $ git push origin name-of-your-bugfix-or-feature
    
  7. Submit a pull request through the GitHub website.

Pull Request Guidelines

Before you submit a pull request, check that it meets these guidelines:

  1. The pull request should include tests.
  2. If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in README.rst.
  3. The pull request should work for Python 2.7, 3.4, 3.5 and 3.6, and for PyPy. Check https://travis-ci.org/bobspadger/royal_mail_rest_api/pull_requests and make sure that the tests pass for all supported Python versions.

Tips

To run a subset of tests:

$ py.test tests.test_royal_mail_rest_api

Deploying

A reminder for the maintainers on how to deploy. Make sure all your changes are committed (including an entry in HISTORY.rst). Then run:

$ bumpversion patch # possible: major / minor / patch
$ git push
$ git push --tags

Travis will then deploy to PyPI if tests pass.

Credits

Development Lead

Contributors

None yet. Why not be the first?

History

0.0.6 (2018-04-06)

Raise useful errors from the api calls so they are returned to the calling app

0.0.5

Tests Service helpers

0.0.4

0.0.3 (2018-04-03)

Store token for 4 hours (issue #7) Fix some poor documentation Fix some examples

0.0.2 (2018-04-03)

Fixing PyPI install not working

0.0.1 (2018-03-26)

  • First release on PyPI.

Indices and tables