Python 3 client for the Etsy Open API v3.
The authorisation flow in v3 of Etsy's API is somewhat different to the flow used in v2. It is the OAuth 2.0 Authorization Code Grant flow, documented quite well by Etsy themselves. Make sure you've done the setup at Requesting an OAuth Token
, in terms of getting your Etsy API keystring and callback URLs set up.
In the etsyv3.utils.util.auth
package, the auth_helper.py
module contains a helper class (AuthHelper
) for the authentication flow. Provided with the keystring, one of the redirect URLs that you've specific in your Etsy app setup, a list of scopes to be provided in this authentication (a list of strings at present, but likely to become a set of enums
in future), a code verifier string (specified by you) and a state string (also specified by you), it will allow for some simplification of the process.
With your initialised AuthHelper
, the flow looks something like this:
- Call
get_auth_code()
on yourAuthHelper
- this will return an Etsy authentication URL - Go to that URL and authenticate with Etsy
- Use the
state
andcode
params from the callback that Etsy will make to callset_authorization_code(code, state)
on anAuthHelper
object initialised with the same the arguments passed to your first one (this callback is likely to be in a completely new request context to the first - your original object may well no longer exist) - You can then call
get_access_token()
on yourAuthHelper
object and you should get a dictionary returned with the keysaccess_token
,refresh_token
andexpires_at
. These a required to create theEtsyAPI
object.
Technically no. If you set the callback URL to a localhost address that doesn't have an application listening on it, you can just copy and paste the state
and code
URL parameters that will be present in the 404'd URL that Etsy will redirect you to and then continue in calling set_authorization_code()
and get_access_token()
on the AuthHelper
object.
Just set up something simple in Flask would be my recommendation if you're doing this for anything even semi-serious: it'll make your life easier.
In a Djano context, it could be as simple as something like the below, wired up to the router appropriately:
def oauth_callback(request):
state = request.GET["state"]
code = request.GET["code"]
auth = AuthHelper(
keystring, redirect_uri, code_verifier="super_secret_and_random_code_verifier_string", state="super_secret_and_random_code_state_string"
)
auth.set_authorisation_code(code, state)
token = auth.get_access_token()
save_this_for_later(token)
return HttpResponse("Logged in!")
Now that you have your keystring, access token, refresh token and token expiry time, you can create your EtsyAPI
object! The EtsyAPI
initialiser requires a keystring
, token
, refresh_token
and expiry
(as a datetime
object) to be set: these will be the values returned from the call to get_access_token()
in authentication. A function can be specified as a named argument refresh_save
, that takes three parameters access_token
, refresh_token
and expires_at
. This function will be run whenever the token needs to be refreshed to update it for future use and store it somewhere.
You can call any of the Etsy API methods from the EtsyAPI
object: they'll generally be just a snake-case form of the Etsy published names (eg GetBuyerTaxonomyNodes
becomes get_buyer_taxonomy_nodes()
).
For POST
and PUT
requests (requests that need payloads), there are subclassed Request
types for each type. They generally take all required arguments into a constructor that is then serialised into JSON when passed to an API-calling method. Where you wish to null an optional parameter in a call, you should create the request parameter with the empty-form of whatever you're trying to null, so if the API takes a string, provide ""
; if a list, []
.
If you're using an IDE or a featureful text editor, your autocomplete should do you quite well in working out what you can do: I've made a point to use type hints for all method parameters, so it should be OK. I will be adding more docstrings over time, so it should hopefully get easier to use.
It's intended to be a 'neat' way to handle refreshes - it's a function (or method) that takes the token and metadata and does stuff with it, whatever you want that stuff to be. It could look something like this:
def refresh_save(access_token, refresh_token, expires_at):
api_creds = get_some_persistence_object()
api_creds.access_token = access_token
api_creds.refresh_token = refresh_token
api_creds.expires_at = expires_at
api_creds.save()
Etsy API | Implemented |
---|---|
GetBuyerTaxonomyNodes | ✔️ |
GetPropertiesByBuyerTaxonomyId | ✔️ |
GetSellerTaxonomyNodes | ✔️ |
GetPropertiesByTaxonomyId | ✔️ |
CreateDraftListing | ✔️ |
GetListingsByShop | ✔️ |
DeleteListing | ✔️ |
GetListing | ✔️ |
FindAllListingsActive | ✔️ |
FindAllActiveListingsByShop | ✔️ |
GetListingsByListingIds | ✔️ |
GetFeaturedListingsByShop | ✔️ |
DeleteListingProperty | ✔️ |
UpdateListingProperty | ❌ |
GetListingProperty | ❌ (501 only from Etsy) |
GetListingProperties | ✔️ |
UpdateListing | ❌ |
GetListingsByShopReceipt | ✔️ |
GetListingsByShopSectionId | ✔️ |
DeleteListingFile | ✔️ |
GetListingFile | ✔️ |
GetAllListingFiles | ✔️ |
UploadListingFile | ❌ |
DeleteListingImage | ✔️ |
GetListingImage | ✔️ |
GetListingImages | ✔️ |
UploadListingImage | ❌ |
GetListingInventory | ✔️ |
UpdateListingInventory | ✔️ |
GetListingOffering | ✔️ |
GetListingProduct | ✔️ |
CreateListingTranslation | ❌ |
GetListingTranslation | ✔️ |
UpdateListingTranslation | ❌ |
GetListingVariationImages | ✔️ |
UpdateVariationImages | ✔️ |
Ping | ✔️ |
TokenScopes | ✔️ |
GetShopPaymentAccountLedgerEntry | ✔️ |
GetShopPaymentAccountLedgerEntries | ✔️ |
GetPaymentAccountLedgerEntryPayments | ✔️ |
GetShopPaymentByReceiptId | ✔️ |
GetPayments | ✔️ |
GetShopReceipt | ✔️ ️ |
UpdateShopReceipt | ❌️ ️ |
GetShopReceipts | ✔️ ️ |
CreateReceiptShipment | ❌ |
GetShopReceiptTransactionsByListing | ✔️ |
GetShopReceiptTransactionsByReceipt | ✔️ |
GetShopReceiptTransaction | ✔️ |
GetShopReceiptTransactionsByShop | ✔️ |
GetReviewsByListing | ✔️ |
GetReviewsByShop | ✔️ |
GetShippingCarriers | ✔️ |
CreateShopShippingProfile | ❌ |
GetShopShippingProfiles | ✔️ |
DeleteShopShippingProfile | ✔️ |
GetShopShippingProfile | ✔️ |
UpdateShopShippingProfile | ❌ |
CreateShopShippingProfileDestination | ❌ |
GetShopShippingProfileDestinationsByShippingProfile | ✔️ |
DeleteShopShippingProfileDestination | ✔️ |
UpdateShopShippingProfileDestination | ❌ |
CreateShopShippingProfileUpgrade | ❌ |
GetShopShippingProfileUpgrades | ✔️ |
DeleteShopShippingProfileUpgrade | ✔️ |
UpdateShopShippingProfileUpgrade | ❌ |
GetShop | ✔️ |
UpdateShop | ❌ |
GetShopByOwnerUserId | ✔️ |
FindShops | ✔️ |
GetShopProductionPartners | ✔️ |
CreateShopSection | ❌ |
GetShopSections | ✔️ |
DeleteShopSection | ✔️ |
GetShopSection | ✔️ |
UpdateShopSection | ❌ |
GetUser | ✔️ ️ |
DeleteUserAddress | ❌ |
GetUserAddress | ❌ (Etsy only returns a 501) |
GetUserAddresses | ✔️ |