Python wrapper around Apple App Store Api

Overview

App Store Connect Api

This is a Python wrapper around the Apple App Store Api : https://developer.apple.com/documentation/appstoreconnectapi

So far, it handles token generation / expiration, methods for listing resources and downloading reports.

Installation

Version

The project is published on PyPI, install with:

pip install appstoreconnect

Usage

Please follow instructions on Apple documentation on how to generate an API key.

With your key ID, key file (you can either pass the path to the file or the content of it as a string) and issuer ID create a new API instance:

from appstoreconnect import Api, UserRole
api = Api(key_id, path_to_key_file, issuer_id)

Here are a few examples of API usage. For a complete list of available methods please see api.py.

# list all apps
apps = api.list_apps()
for app in apps:
    print(app.name, app.sku)

# sort resources
apps = api.list_apps(sort='name')

# filter apps
apps = api.list_apps(filters={'sku': 'DINORUSH', 'name': 'Dino Rush'})
print("%d apps found" % len(apps))

# read app information
app = api.read_app_information('1308363336')
print(app.name, app.sku, app.bundleId)

# get a related resource
for group in app.betaGroups():
    print(group.name)

# list bundle ids
for bundle_id in api.list_bundle_ids():
    print(bundle_id.identifier)

# list certificates
for certificate in api.list_certificates():
    print(certificate.name)

# modify a user
user = api.list_users(filters={'username': '[email protected]'})[0]
api.modify_user_account(user, roles=[UserRole.FINANCE, UserRole.ACCESS_TO_REPORTS])
    
# download sales report
api.download_sales_and_trends_reports(
    filters={'vendorNumber': '123456789', 'frequency': 'WEEKLY', 'reportDate': '2019-06-09'}, save_to='report.csv')

# download finance report
api.download_finance_reports(filters={'vendorNumber': '123456789', 'reportDate': '2019-06'}, save_to='finance.csv')

Define a timeout (in seconds) after which an exception is raised if no response is received.

api = Api(key_id, path_to_key_file, issuer_id, timeout=30)
api.list_apps()

APIError: Read timeout after 30 seconds

Please note this is a work in progress, API is subject to change between versions.

Anonymous data collection

Starting with version 0.8.0 this library anonymously collects its usage to help better improve its development. What we collect is:

  • a SHA1 hash of the issuer_id
  • the OS and Python version used
  • which enpoints had been used

You can review the source code

If you feel uncomfortable with it you can completely opt-out by initliazing the API with:

api = Api(key_id, path_to_key_file, issuer_id, submit_stats=False)

The is also an open issue about this topic where we would love to here your feedback and best practices.

Development

Project development happens on Github

TODO

  • Support App Store Connect API 1.2
  • Support the include parameter
  • handle POST, DELETE and PATCH requests
  • sales report
  • handle related resources
  • allow to sort resources
  • proper API documentation
  • add tests

Credits

This project is developed by Ponytech

Comments
  • How to modify user roles

    How to modify user roles

    Hi Pony, I want to modify an existing user's roles (role and visibleApps), not invite new user. I look from appstore api document, it contain "Modify a User Account" api, but I don't find the related code from your api.py file. would please have a check, thanks very much.

    opened by yangcaixing 11
  • Avoid pinned dependencies

    Avoid pinned dependencies

    On 8de43ea the dependencies were pinned in setup.py. The problem with that is that now is now longer possible to install this library and, for instance, the latest release of requests alongside.

    Since I believe this project has the vocation of being a library and not a standalone project I suggest to instead of pinning the dependencies just force minimum versions*.

    I'd be very happy to do a PR if you agree with it.

    *I know that jwt 2.* is problematic. For that particular dependency we could just set a minimum and a maximum version.

    opened by josepanguera 8
  • AttributeError: 'str' object has no attribute 'decode'

    AttributeError: 'str' object has no attribute 'decode'

    Hello,

    I have been using this library without issue for a few months. I started encountering an issue since this january 1st. I'm using the same exact environment and credentials but here is the stacktrace of the exception I am now running into :

    AttributeError: 'str' object has no attribute 'decode' File "batch_reporting.py", line 48, in lambda_handler issuer_id) File "appstoreconnect/api.py", line 51, in init token = self.token # generate first token File "appstoreconnect/api.py", line 269, in token self._token = self._generate_token() File "appstoreconnect/api.py", line 65, in _generate_token headers={'kid': self.key_id, 'typ': 'JWT'}, algorithm=ALGORITHM).decode('ascii')

    The python version is 3.6 and I am using the last version of the library 0.8.4

    Do you have any lead on what might cause this ?

    opened by Datavoore 7
  • api.download_sales_and_trends_reports() sends error when specifying reportType

    api.download_sales_and_trends_reports() sends error when specifying reportType

    When making a 'api.download_sales_and_trends_reports' call with reportType set to 'SUBSCRIPTION'. apple store connect replies with 'APIError: Please include the version parameter. The latest version for this report is 1_2.'

    opened by foundmason 7
  • Bad formatting in api.download_finance_reports() calls

    Bad formatting in api.download_finance_reports() calls

    When I run the following code

    from appstoreconnect import Api 
    
    key_id = 'foo_bar'
    path_to_key_file = './foo_bar.p8'
    issuer_id = 'foo_bar'
    
    api = Api(
        key_id,
        path_to_key_filee,
        issuer_id,
        submit_stats=False)
    
    res1 = api.download_finance_reports(
        filters={
            'vendorNumber': '123456789',
            'reportDate': '2020-07',
            'reportType': 'FINANCIAL'
        },
        save_to='test.csv'
    )
    

    I get a poorly formatted .csv file. It looks like there is some strange text wrapping going on in the response from api.download_finance_reports(). The response always "wraps" at the Total Rows line. Both the reposnse and .csv show the same problem.

    Start Date	End Date	UPC	ISRC/ISBN	Vendor Identifier	Quantity	Partner Share	Extended Partner Share	Partner Share Currency	Sales or Return	Apple Identifier	Artist/Show/Developer/Author	Title	Label/Studio/Network/Developer/Publisher	Grid	Product Type Identifier	ISAN/Other Identifier	Country Of Sale	Pre-order Flag	Promo Code	Customer Price	Customer Currency
    03/29/2020	05/02/2020			foobar.iosapp.1month.subscription	1	44.52	44.52	AED	S	123456789		foobar Premium Monthly			IAY		AE			54.99	AED
    03/29/2020	05/02/2020			foobar.iosapp.1month.subscription	2	44.52	89.04	AED	S	123456789		foobar Premium Monthly			IAY		AE			54.99	AED
    03/29/2020	05/02/2020			foobar.iosapp.1month.subscription	2	36.66	73.32	AED	S	123456789		foobar Premium Monthly			IAY		AE			54.99	AED
    ...
    ...
    data continues in this pattern
    ...
    ...
    Total_Rows	262
    Country Of Sale	Partner Share Currency	Quantity	Extended Partner Share
    AE	AED	7	490.44
    AU	AUD	471	14647.84
    BR	BRL	113	7988.34
    

    I am trying to work on a fix for this in my use case, but figured I would make you aware how it affects this packages functionality as well.

    opened by GClunies 6
  • Add Register Devices endpoints

    Add Register Devices endpoints

    Hey! Me again with a small PR to add support for a new endpoint. I also updated the changelog and bumped the version so that it's easier for you to release a new version. Let me know if that's fine 😄

    opened by BalestraPatrick 5
  • APIError should expose the HTTP status code returned by App Store Connect API response

    APIError should expose the HTTP status code returned by App Store Connect API response

    Happy to take a stab at this @ppawlak.

    Issue Description

    Ideally, when an APIError exception is raised, the APIerror should also expose the HTTP status code as defined in the Apple docs. I think the best way to do this is to add the HTTP status code as an attribute to the APIError object so that the end user can check against this value.

    By exposing the HTTP status code to the end user as an attribute, errors can be maintained as contracts. Currently the only way to check for a APIError is using the error description. Something like...

    REPORT_NOT_AVAILABLE_ERROR = (
        "Report is not available yet. Daily reports for the Americas are available"
        " by 5 am Pacific Time; Japan, Australia, and New Zealand by 5 am Japan"
        " Standard Time; and 5 am Central European Time for all other territories."
    )
    NO_DATA_DATE_SPECIFIED_ERROR = "There were no sales for the date specified."
    
    except APIError as e:
        # Only except certain errors, full list of possible errors is found here: 
        # https://developer.apple.com/documentation/appstoreconnectapi/interpreting_and_handling_errors/about_the_http_status_code  # noqa
        if str(e) == REPORT_NOT_AVAILABLE_ERROR or str(e) == NO_DATA_DATE_SPECIFIED_ERROR:
            LOGGER.error("{0}".format(e))
        else:
            raise e
    

    If Apple changes the error description slightly this approach will break. I would expect the HTTP status codes to be much more stable.

    opened by GClunies 4
  • When adding filter 'reportType' or others to download_sales_and_trends_reports, invalid vendor number error.

    When adding filter 'reportType' or others to download_sales_and_trends_reports, invalid vendor number error.

    Following your lead on another issue, I have been trying to get download_sales_and_trends_reports to work, but when I add any other filters to the example, I am told the vendor number is invalid. It doesn't mind the same vendor number when using the default filters.

    opened by rolflocher 4
  • APIError: There were no sales for the date specified. Any way to get Units sold for a free app?

    APIError: There were no sales for the date specified. Any way to get Units sold for a free app?

    Our app is free so there is no sales - $ sales data, but there is sales - # units data which I am interested in. I am having trouble getting the download_sales_and_trends_reports to return anything because of this. Also, perhaps the script shouldn't crash when there is a day without sales?

    I ended up ditching the manual jwt encoding to try your wrapper because I couldn't set the authorization header with String("Bearer ") + Bytes(encoded_jwt). Perhaps you could help me? Thanks

    opened by rolflocher 4
  • APIError

    APIError

    Hi, I have an error when I run the script

    this is my script example.py

    import sys
    from appstoreconnect import Api
    
    if __name__ == "__main__":
            key_id = "XXXXXXXXXX"
            key_file = "AuthKey_XXXXXXXXXX.p8"	
            issuer_id ="XXXXXXXXXX-XXXXX-XXXXXXXXXXXXX"
            
            api = Api(key_id, key_file, issuer_id)
            apps = api.list_apps()
    
            print (apps)
            for app in apps:
                    print(app.name)
    

    The key_file is in the same directory than example.py. And this is the error

    Traceback (most recent call last):
      File "C:\API_apple\example.py", line 26, in <module>
        for app in apps:
      File "C:\API_apple\appstoreconnect\api.py", line 80, in __next__
        self.fetch_page()
      File "C:\API_apple\appstoreconnect\api.py", line 97, in fetch_page
        self.payload = self.api._api_call(self.url)
      File "C:\API_apple\appstoreconnect\api.py", line 135, in _api_call
        raise APIError(payload.get('errors', [])[0].get('detail', 'Unknown error'))
    appstoreconnect.api.APIError: Provide a properly configured and signed bearer token, and make sure that it has not expired. Learn more about Generating Tokens for API Requests https://developer.apple.com/go/?id=api-generating-tokens
    

    What is happening ?

    Thanks

    opened by pruebasft 4
  • Add support for user invitation

    Add support for user invitation

    Hey!

    I added support for two new endpoints:

    • https://developer.apple.com/documentation/appstoreconnectapi/invite_a_user
    • https://developer.apple.com/documentation/appstoreconnectapi/read_user_invitation_information

    Also fixed a bug that caused create_beta_tester to fail sending the request since the URL was not created correctly.

    opened by BalestraPatrick 4
  • Is there a way to get info about downloads and impressions of some app?

    Is there a way to get info about downloads and impressions of some app?

    Hey,

    Just wondering if there is a way to get downloads and impressions in a "report" way, grouped by day, country, etc. Like in the appstoreconnect UI

    opened by albertocarrf 0
  • Relaxing cryptography dependency

    Relaxing cryptography dependency

    The current restriction is no higher than version 2, although for more than two and a half years the third version. I am currently using 38.0.4 and everything works without any problems.

    opened by conformist-mw 0
  • Is there a way to read the error when the API fails?

    Is there a way to read the error when the API fails?

    Is there a way to read the error when the API fails?

    I am using the APIs to get user info. But it randomly fails and throws the following error: " appstoreconnect.api.APIError: An unexpected error occurred on the server side. If this issue continues, contact us at https://developer.apple.com/contact/."

    So the API calls fail without any warning or returning any reason.

    Adding how to print the error object when the API fails in the example would be helpful.

    opened by ammu2288 9
  • Provide a properly configured and signed bearer token, and make sure that it has not expired.

    Provide a properly configured and signed bearer token, and make sure that it has not expired.

    I just installed this yesterday and it worked fine. But today it starts throw error "Provide a properly configured and signed bearer token, and make sure that it has not expired. Learn more about Generating Tokens for API Requests https://developer.apple.com/go/?id=api-generating-tokens".

    At the end I found out it maybe caused by the expiration time. When I changed it from 20 to 15, it works again: exp = int(time.mktime((self.token_gen_date + timedelta(minutes=15)).timetuple())) On documentation here: https://developer.apple.com/documentation/appstoreconnectapi/generating_tokens_for_api_requests it says Tokens that expire more than 20 minutes into the future are not valid so I guess when do the calculation with 20 mins it may round up to exceed 20 mins?

    Updated: after I sync the time on my PC, it works again with 20mins.

    opened by mybluedog24 1
  • Support PyJWT>=2.0

    Support PyJWT>=2.0

    Main issue is starting with version 2.0.0 jwt.encode() retruns an str whereas older version returns bytes If not too complex we should support both.

    See this issue: https://github.com/Ponytech/appstoreconnectapi/issues/33#issuecomment-755434912

    opened by ppawlak 0
Process RunGap output file of a workout and load data into Apple Numbers Spreadsheet and my website with API calls

BSD 3-Clause License Copyright (c) 2020, Mike Bromberek All rights reserved. ProcessWorkout Exercise data is exported in JSON format to iCloud using

Mike Bromberek 1 Jan 3, 2022
Headless - Wrapper around Ghidra's analyzeHeadless script

Wrapper around Ghidra's analyzeHeadless script, could be helpful to some? Don't tell me anything is wrong with it, it works on my machine.

null 8 Oct 29, 2022
A domonic-like wrapper around selectolax

A domonic-like wrapper around selectolax

byteface 3 Jun 23, 2022
A basic notes app to store your notes.

Notes Webapp A basic notes webapp to keep your notes.You can add, edit and delete notes after signing up. To add a note type your note in the text box

null 2 Oct 23, 2021
About A python based Apple Quicktime protocol,you can record audio and video from real iOS devices

介绍 本应用程序使用 python 实现,可以通过 USB 连接 iOS 设备进行屏幕共享 高帧率(30〜60fps) 高画质 低延迟(<200ms) 非侵入性 支持多设备并行 Mac OSX 安装 python >=3.7 brew install libusb pkg-config 如需使用 g

YueC 124 Nov 30, 2022
Bad Apple printed out on the console with Python!

bad-apple Bad Apple printed out on the console with Python! Preface A word of disclaimer, while the final code is somewhat original, this project is a

CalvinLoke 186 Dec 1, 2022
Wrappers around the most common maya.cmds and maya.api use cases

Maya FunctionSet (maya_fn) A package that decompose core maya.cmds and maya.api features to a set of simple functions. Tests The recommended approach

Ryan Porter 9 Mar 12, 2022
🍏 Make Thinc faster on macOS by calling into Apple's native Accelerate library

?? Make Thinc faster on macOS by calling into Apple's native Accelerate library

Explosion 81 Nov 26, 2022
Automatically skip sponsor segments in YouTube videos playing on Apple TV.

iSponsorBlockTV Skip sponsor segments in YouTube videos playing on an Apple TV. This project is written in asycronous python and should be pretty quic

David 64 Dec 17, 2022
Singularity Containers on Apple M1 (ARM64)

Singularity Containers on Apple M1 (ARM64) This is a repository containing a ready-to-use environment for singularity in arm64 (M1). It has been prepa

Manuel Parra 4 Nov 14, 2022
ArinjoyTheDev 1 Jul 17, 2022
TurtleBot Control App - TurtleBot Control App With Python

TURTLEBOT CONTROL APP INDEX: 1. Introduction 2. Environments 2.1. Simulated Envi

Rafanton 4 Aug 3, 2022
SWS Filters App - SWS Filters App With Python

SWS Filters App Fun ?? ... Fun ?? Click On photo and see ?? ?? ?? Your Video rec

Sagar Jangid 3 Jul 7, 2022
A python script providing an idea of how a MindSphere application, e.g., a dashboard, can be displayed around the clock without the need of manual re-authentication on enforced session expiration

A python script providing an idea of how a MindSphere application, e.g., a dashboard, can be displayed around the clock without the need of manual re-authentication on enforced session expiration

MindSphere 3 Jun 3, 2022
PREFS is a Python library to store and manage preferences and settings.

PREFS PREFS is a Python library to store and manage preferences and settings. PREFS stores a Python dictionary in a total human-readable file, the PRE

Pat 13 May 26, 2022
K2HASH Python library - NoSQL Key Value Store(KVS) library

k2hash_python Overview k2hash_python is an official python driver for k2hash. Install Firstly you must install the k2hash shared library: curl -o- htt

Yahoo! JAPAN 3 Oct 19, 2022
CaskDB is a disk-based, embedded, persistent, key-value store based on the Riak's bitcask paper, written in Python.

CaskDB - Disk based Log Structured Hash Table Store CaskDB is a disk-based, embedded, persistent, key-value store based on the Riak's bitcask paper, w

null 886 Dec 27, 2022
News-app - This is a news web app for reading news from different sources and topics

News-app - This is a news web app for reading news from different sources and topics

null 1 Feb 2, 2022
A collection of repositories used to realise various end-to-end high-level synthesis (HLS) flows centering around the CIRCT project.

circt-hls What is this?: A collection of repositories used to realise various end-to-end high-level synthesis (HLS) flows centering around the CIRCT p

null 29 Dec 14, 2022