• Content

API v2 Python Client Upgrade Guide

This page is provided as an upgrade guide to all users of the official python client.

Two aspects of the upgrade process are covered:

  • Given that version designations for client libraries do not support the same versioning system as the v2 API, we have provided a version mapping table directly below. You can use this table in order to determine which version of the v2 API that a specific client library version is utilizing.
  • Breaking changes to client libraries are normally applied with a bump in the minor version. As an example, suppose you wish to upgrade from 2.5.2 to 2.9.2 while preserving current functionality: apply all changes listed in (2.6.0, 2.7.0, ..., 2.9.0). A guide to breaking changes between all versions of the client library can be found below.

We recommend that you first identify the version of the client library required based on the associated API version denoted in the Client Library Mapping Table (directly below), and then refer to the Guide to Breaking Changes section in order to identify next steps to upgrade.

Version Mapping Table

API Version Client Version (First) Client Version (Last)
2.6 (and below) 2.0.0 2.5.2
2.7 None None
2.8 2.6.0 2.6.3
2.9 2.7.1 2.7.1
2.10 2.8.0 2.8.0
2.11 2.8.1 2.8.1
2.12 2.8.2 2.8.3
2.13 2.8.4 2.8.6
2.14 2.8.7 2.8.8
2.15 2.9.0 2.9.0
2.16 2.9.1 2.9.1
2.17 2.9.2 2.9.3
2.18 2.9.4 2.9.4
2.19 2.9.5 2.9.6
2.20 2.9.7 2.9.7
2.21 2.9.8 2.9.8
2.22 2.9.9 2.9.11
2.24 2.9.12 2.9.13
2.25 2.9.14 2.9.15
2.26 2.9.16 2.9.16
2.27 2.9.17 2.9.17
2.28 2.9.18 2.9.18
2.29 2.9.19  

Guide to Breaking Changes


  1. Older Recurly.js token signing is not longer supported. You should upgrade to version 4 of Recurly.js.
  2. Removes support for Python 3.3. You should upgrade to a supported version of Python.


  1. InvoiceCollection

    When creating invoices or using mark_failed(), we now return an InvoiceCollection object rather than an Invoice. If you wish to upgrade your application without changing functionality, we recommend that you use the charge_invoice on the InvoiceCollection. Example:

     # Change This:
     invoice = account.invoice()
     # To this
     collection = account.invoice()
     invoice = collection.charge_invoice

    Calls that now return InvoiceCollection instead of Invoice:

    • Purchase#invoice()
    • Purchase#preview()
    • Purchase#authorize()
    • Account#invoice()
    • Account#build_invoice()

    Furthermore, Invoice#mark_failed() no longer updates the invoice but rather returns a new InvoiceCollection object:

     # Change This:
     # To this
     collection = invoice.mark_failed()
     failed_invoice = collection.charge_invoice
  2. Invoice#original_invoice removed

    Invoice#original_invoice was removed in favor of Invoice#original_invoices. If you want to maintain functionality, change your code grab the first invoice from that endpoint:

     # Change this
     original_invoice = invoice.original_invoice()
     # To this
     original_invoice = invoice.original_invoices()[0]
  3. Invoice subtotal_* changes

    We have renamed two of the invoice subtotal fields to more clearly reflect their values:

    • Renamed subtotal_in_cents to subtotal_before_discount_in_cents
    • Renamed subtotal_after_discount_in_cents to subtotal_in_cents
  4. Invoice Refund – refund_apply_order changed to refund_method

    If you were using Invoice#refund or Invoice#refund_amount and explicitly setting the second refund_apply_order parameter, then you may need to change value to fit the new refund_method format. The values for this have changed from (credit, transaction) to (credit_first, transaction_first)

    If you don’t explicitly set the refund_apply_order like in these two calls, no change is needed:


    If you do set the second param, you’ll need to change:

    • credit to credit_first
    • transaction to transaction_first


     # Change `credit`:
     invoice.refund(line_items, 'credit');
     invoice.refund_amount(1000, 'credit');
     # To `credit_first`
     invoice.refund(line_items, 'credit_first');
     invoice.refund_amount(1000, 'credit_first');
     # Change `transaction`
     invoice.refund(line_items, 'transaction');
     invoice.refund_amount(1000, 'transaction');
     # To `transaction_first`
     invoice.refund(line_items, 'transaction_first');
     invoice.refund_amount(1000, 'transaction_first');
  5. Invoice States

    If you are checking Invoice#state anywhere, you will want to check that you have the new correct values. collected has changed to paid and open has changed to pending. Example:

     # Change this
     if invoice.state == 'collected':
     #To this
     if invoice.state == 'paid':
     # Change this
     if invoice.state == 'open':
     # To this
     if invoice.state == 'pending':

    This also affects the Invoice.all_collected and Invoice.all_open functions. Example:

     # Change this
     # To this
     # Change this
     # To this


  1. The subscription link on an instance of Adjustment is now only created if adjustment is originating from a subscription plan charge, setup fee, add on, trial or proration credit. It is no longer created for other adjustments.

  2. Instances of Transaction and Invoice no longer have a subscription link and you must now use the subscriptions link.

    # Change this
    sub = invoice.subscription()
    # To this, take the first subscription
    sub = invoice.subscriptions()[0]


Country Codes

All country fields must now contain valid 2 letter ISO 3166 country codes. If your country code fails validation, you will receive a validation error. This affects any endpoint where an address is collected.

Purchase Currency

The purchases endpoint can create and invoice multiple adjustments at once but our invoices can only contain items in one currency. To make this explicit the currency can no longer be provided on an adjustment, it must be set once for the entire purchase:

purchase = recurly.Purchase(
  # The purchase object is the only place you can set the currency:
  currency = 'USD',
  account = recurly.Account(
    account_code = 'someone',
  adjustments = [
      # Remove this currency
      # You can no longer set the currency on adjustment level
      recurly.Adjustment(currency='USD', unit_amount_in_cents=1000, description='Item 1',


  1. Since the X-Records header was removed in the pagination endpoint, you can no longer call len() on a Page and expect it to return a cached response. From now on you need to explicitly call the count() class method on a Page. See PR #202 for more information.
  2. For POST /v2/subscriptions Sending None for total_billing_cycles attribute will now override plan total_billing_cycles setting and will make subscription renew forever. Omitting the attribute will cause the setting to default to the value of plan total_billing_cycles.


Python 2.6 is no longer supported.


No breaking changes to address.


Removed adjustment.taxable in favor of adjustment.tax_exempt. Keep in mind that these two fields are both booleans but logically inverse.