{"id":37078805,"url":"https://github.com/codinn/applaud","last_synced_at":"2026-01-14T09:32:50.129Z","repository":{"id":57411053,"uuid":"444057866","full_name":"codinn/applaud","owner":"codinn","description":"Python client library for App Store Connect API.","archived":false,"fork":false,"pushed_at":"2022-01-22T08:50:25.000Z","size":229,"stargazers_count":25,"open_issues_count":4,"forks_count":6,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-12-22T07:58:34.663Z","etag":null,"topics":["appstoreconnectapi","library","python","sdk"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/codinn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-01-03T12:44:31.000Z","updated_at":"2025-02-27T23:20:42.000Z","dependencies_parsed_at":"2022-09-05T14:32:11.588Z","dependency_job_id":null,"html_url":"https://github.com/codinn/applaud","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/codinn/applaud","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codinn%2Fapplaud","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codinn%2Fapplaud/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codinn%2Fapplaud/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codinn%2Fapplaud/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/codinn","download_url":"https://codeload.github.com/codinn/applaud/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codinn%2Fapplaud/sbom","scorecard":{"id":298314,"data":{"date":"2025-08-11","repo":{"name":"github.com/codinn/applaud","commit":"ed168ca67465b5c0acf4ab4f4e285a2ab348c96d"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 0/22 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-17T20:05:35.156Z","repository_id":57411053,"created_at":"2025-08-17T20:05:35.156Z","updated_at":"2025-08-17T20:05:35.156Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28416096,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T08:38:59.149Z","status":"ssl_error","status_checked_at":"2026-01-14T08:38:43.588Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["appstoreconnectapi","library","python","sdk"],"created_at":"2026-01-14T09:32:49.171Z","updated_at":"2026-01-14T09:32:50.120Z","avatar_url":"https://github.com/codinn.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Applaud\n\n`Applaud` is a Python client library for accessing [App Store Connect API](https://developer.apple.com/documentation/appstoreconnectapi), generated by [Applaudgen](https://github.com/codinn/applaudgen).\n\n[![PyPI version](https://badge.fury.io/py/applaud.svg)](https://badge.fury.io/py/applaud)\n\n## Features\n\n- [x] Support App Store Connect API latest version 1.6\n- [x] Support `filter`, `fileds`, `include`, `limit`, `sort`, `exists` and other query parameters\n- [x] All endpoints / paths are implemented, include, but not limited to: App Information, TestFlight, Users and Roles, Sales and Finances\n- [x] Pythonic, all `camelCase` schema fields are represented as `snake_case` class attributes\n- [x] Embrace [Python type hints](https://www.python.org/dev/peps/pep-0483/)\n- [x] Use [Python Requests](https://docs.python-requests.org/en/latest/) to hanlde HTTP sessions\n- [x] [ErrorResponse](https://developer.apple.com/documentation/appstoreconnectapi/errorresponse) can be catched as exception\n\n## Installation\n\nInstall with `pip`:\n\n```\npip install applaud\n```\n\nInstall with [Poetry](https://python-poetry.org/):\n```\npoetry add applaud\n```\n\nInstall with [Pipenv](https://pipenv.pypa.io/en/latest/):\n```\npipenv install applaud\n```\n\n## Usage\n\nCalls to the API require authorization, so before we get started, you obtain keys to create the tokens from your organization’s App Store Connect account. See [Creating API Keys for App Store Connect API](https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api) to create your keys and tokens.\n\n\n### Connection\n\n`Connection` is the core class of `Applaud`, it holds a connection between client and remote service, [generate a new token](https://developer.apple.com/documentation/appstoreconnectapi/generating_tokens_for_api_requests#3878467) before it expires.\n\n```python\nfrom applaud.connection import Connection\n\n# Create a connection object using API keys\nconnection = Connection(APPSTORE_ISSUER_ID, APPSTORE_KEY_ID, APPSTORE_PRIVATE_KEY)\n```\n\nIn most of cases, all tasks you'd like to perform on remote service should be initiated from a `Connection` object. `Connection` has a bunch of functions help you create `…Endpoint` objects:\n\n```python\n# Return an AppListEndpoint object\nconnection.apps()\n```\n\n### Endpoint\n\nA `…Endpoint` class encapsulates all operations you can perform on a specific resource. For example, this snippet fetches first two (sort by app name) apps that \"ready for sale\" and have game center enabled versions:\n\n```python\n# Return an AppsResponse object\nconnection.apps().filter(\n    app_store_versions_app_store_state=AppStoreVersionState.READY_FOR_SALE\n).exists(\n    game_center_enabled_versions=True\n).limit(\n    2\n).sort(\n    name: SortOrder.ASC\n).get()\n```\n\n#### `…Endpoint.get()`\n\nThe `get()` operation initiates a HTTP `GET` request on the endpoint's path. For example, the URL of [List Apps](https://developer.apple.com/documentation/appstoreconnectapi/list_apps) service endpoint is:\n```\nGET https://api.appstoreconnect.apple.com/v1/apps\n```\n\nThe corresponding code in `Applaud`:\n```python\n# Return an AppsResponse object\nresponse = connection.apps().get()\n\nfor app in response.data:\n    print(f'{app.attributes.name}: {app.attributes.bundle_id}')\n```\n\nUnlike other operations (`create()`, `update()` and `delete()`), `get()` operation can be chained by query parameters functions:\n\n**`filter()`**\n\nYou use `filter()` function to extract matching resources. For example:\n```\nfilter[bundleId]  Attributes, relationships, and IDs by which to filter.\n        [string]\n```\n\nThe corresponding code in `Applaud`:\n```python\nresponse = connection.apps().filter(\n    bundle_id=\"com.exmaple.app1\"\n).get()\n# or\nconnection.apps().filter(\n    bundle_id=[\"com.exmaple.app1\", \"com.exmaple.app2\"]\n).get()\n\nfor app in response.data:\n    print(f'{app.attributes.name}: {app.attributes.bundle_id}')\n```\n\n**`include()`**\n\nYou use `include()` function to ask relationship data to include in the response. For example:\n```\n include  Relationship data to include in the response.\n[string]  Possible values: appClips, appInfos, appStoreVersions,\n          availableTerritories, betaAppLocalizations,\n          betaAppReviewDetail, betaGroups, betaLicenseAgreement,\n          builds, ciProduct, endUserLicenseAgreement,\n          gameCenterEnabledVersions, inAppPurchases, preOrder,\n          preReleaseVersions, prices\n```\n\nThe corresponding code in `Applaud`:\n```python\nresponse = connection.apps().include(\n    AppListEndpoint.Include.BETA_LICENSE_AGREEMENT\n).get()\n# or\nresponse = connection.apps().include(\n    [AppListEndpoint.Include.BETA_LICENSE_AGREEMENT, AppListEndpoint.Include.PRICES]\n).get()\n```\n\n**`fields()`**\n\nYou use `fields()` function to ask fields data to return for included related resources in a `get()` operation. Related resources specified in `fields()` function *MUST* be included explicitly in `include()` function, otherwise, the remote service may not return the fields data that you expect. For example:\n```\nfields[betaLicenseAgreements]  Fields to return for included related types.\n                     [string]  Possible values: agreementText, app\n```\n\nThe corresponding code in `Applaud`:\n```python\nconnection.apps().include(\n    AppListEndpoint.Include.BETA_LICENSE_AGREEMENT\n).fields(\n    beta_license_agreement=[BetaLicenseAgreementField.AGREEMENT_TEXT, BetaLicenseAgreementField.APP]\n).get()\n```\n\n**`limit()`**\n\nYou use `limit()` function to restrict the maximum number of resources to return in a `get()` operation. For example:\n```\n  limit  Number of resources to return.\ninteger  Maximum Value: 200\n```\n\nThe corresponding code in `Applaud`:\n```python\n# Return a response contains 10 apps at most\nconnection.apps().limit(10).get()\n\n# Raise a ValueError exception, the maxinmu allowed value is 200\nconnection.apps().limit(400).get()\n```\n\nYou can also included limit the number of related resources to return, as in `fields()` function, you *MUST* also specify the related resources explicitly in `include()` function. For example:\n```\nlimit[appStoreVersions]  integer\n                         Maximum Value: 50\n```\n\nThe corresponding code in `Applaud`:\n```python\n# All returned apps have 5 related app store version at most\nconnection.apps().include(\n    AppListEndpoint.Include.APP_STORE_VERSIONS\n).limit(app_store_versions=5).get()\n\n# Raise a ValueError exception, the maxinmu allowed value is 50\nconnection.apps().include(\n    AppListEndpoint.Include.APP_STORE_VERSIONS\n).limit(app_store_versions=100).get()\n```\n\nBy leverage `limit()` function with `sort()` function, your script can be more responsive.\n\n**`sort()`**\n\nYou use `sort()` function to sort the returned resources by attributes in ascending or descending order. For example:\n```\n    sort  Attributes by which to sort.\n[string]  Possible values: bundleId, -bundleId, name, -name, sku, -sku\n```\n\nThe corresponding code in `Applaud`:\n```python\nconnection.apps().sort(name=SortOrder.ASC, bundleId=SortOrder.DESC).get()\n```\n\n**`exists()`**\n\n`exists()` is a special type of filter – filter by existence or non-existence of related resource. For example:\n```\nexists[gameCenterEnabledVersions]  [string]\n```\n\nThe corresponding code in `Applaud`:\n```python\nconnection.apps().exists(game_center_enabled_versions=True).get()\n```\n\n#### `…Endpoint.create()`\n\nThe `create()` operation initiates a HTTP `POST` request on the endpoint's path. For example, the URL of [Create an App Store Version](https://developer.apple.com/documentation/appstoreconnectapi/create_an_app_store_version) service endpoint is:\n```\nPOST https://api.appstoreconnect.apple.com/v1/appStoreVersions\n```\n\nThe corresponding code in `Applaud`:\n```python\nrequest = AppStoreVersionCreateRequest(\n            data = AppStoreVersionCreateRequest.Data(\n                relationships = AppStoreVersionCreateRequest.Data.Relationships(\n                    app = AppStoreVersionCreateRequest.Data.Relationships.App(\n                        data = AppStoreVersionCreateRequest.Data.Relationships.App.Data(\n                            id = 'com.exmaple.app1'\n                        )\n                    )\n                ),\n                attributes = AppStoreVersionCreateRequest.Data.Attributes(\n                    version_string = '1.6',\n                    platform = Platform.IOS,\n                    copyright = f'Copyright © 2021 Codinn Technologies. All rights reserved.',\n                    release_type = AppStoreVersionReleaseType.AFTER_APPROVAL\n                )\n            )\n        )\n\n# Return an AppStoreVersionResponse object\nreponse = connection.app_store_versions().create(request)\n\nversion = response.data\nprint(f'{version.version_string}: {version.created_date}, {version.app_store_state}')\n```\n\n#### `…Endpoint.update()`\n\nThe `update()` operation initiates a HTTP `PATCH` request on the endpoint's path. For example, the URL of [Modify an App Store Version](https://developer.apple.com/documentation/appstoreconnectapi/modify_an_app_store_version) service endpoint is:\n```\nPATCH https://api.appstoreconnect.apple.com/v1/appStoreVersions/{id}\n```\n\nThe corresponding code in `Applaud`:\n```python\n# Get the version id created in previous example\nversion_id = version.data.id\n\n# Update version's information\nrequest = AppStoreVersionUpdateRequest(\n            data = AppStoreVersionUpdateRequest.Data(\n                id = version.data.id,\n\n                attributes = AppStoreVersionUpdateRequest.Data.Attributes(\n                    version_string = '1.6.1',\n                    platform = Platform.IOS,\n                    copyright = f'Copyright © 2022 Codinn Technologies. All rights reserved.',\n                    release_type = AppStoreVersionReleaseType.AFTER_APPROVAL\n                )\n            )\n        )\n\n# Return an AppStoreVersionResponse object\nreponse = connection.app_store_version(version_id).update(request)\n\nversion = response.data\nprint(f'{version.version_string}: {version.copyright}, {version.app_store_state}')\n```\n\n#### `…Endpoint.delete()`\n\nThe `delete()` operation initiates a HTTP `DELETE` request on the endpoint's path. For example, the URL of [Delete an App Store Version](https://developer.apple.com/documentation/appstoreconnectapi/delete_an_app_store_version) service endpoint is:\n```\nDELETE https://api.appstoreconnect.apple.com/v1/appStoreVersions/{id}\n```\n\nThe corresponding code in `Applaud`:\n```python\n# Get the version id created in previous example\nversion_id = version.data.id\n\nconnection.app_store_version(version_id).delete()\n```\n\n### Exceptions\n\n`…Endpoint.get()`, `…Endpoint.create()`, `…Endpoint.update()` and `…Endpoint.delete()` may raise two types of exceptions:\n\n1. HTTP request exceptions raised by [Python Requests](https://docs.python-requests.org/en/latest/api/#exceptions)\n2. Remote service returns an [ErrorResponse](https://developer.apple.com/documentation/appstoreconnectapi/errorresponse)\n\nFor the second case, `Applaud` raises an `EndpointException` exception, and attaches all [`ErrorResponse.Error`](https://developer.apple.com/documentation/appstoreconnectapi/errorresponse/errors) objects in `EndpointException.errors` attribute.\n\nSome errors are harmless, for example, App Store Connect has no API for you to tell whether a tester has accepted beta test invitation or not. When you trying to resend invitations to testers, you may encounter an `ALREADY_ACCEPTED` error, it's safe to just ignore such error:\n\n```python\ntry:\n    # Send / resend beta test invitations\n    response = connection.beta_tester_invitations().create(…)\nexcept EndpointException as err:\n    already_accepted_error = False\n    for e in err.errors:\n        if e.code == 'STATE_ERROR.TESTER_INVITE.ALREADY_ACCEPTED':\n            # silent this error\n            already_accepted_error = True\n            break\n\n    if not already_accepted_error:\n        raise err\n```\n\n## Caveats\n\n- Query parameters functions (`filter()`, `include`, `fields` …) play a role only when using with `…Endpoint.get()`. Though there is no side effects if chain it with `…Endpoint.create()`, `…Endpoint.update()` and `…Endpoint.delete()` operations, it is not adviced.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodinn%2Fapplaud","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcodinn%2Fapplaud","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodinn%2Fapplaud/lists"}