{"id":13602703,"url":"https://github.com/facundoolano/aso","last_synced_at":"2025-05-16T04:03:40.832Z","repository":{"id":37550196,"uuid":"59616826","full_name":"facundoolano/aso","owner":"facundoolano","description":"Tools for app store optimization on iTunes and Google Play","archived":false,"fork":false,"pushed_at":"2023-11-15T15:25:32.000Z","size":329,"stargazers_count":772,"open_issues_count":19,"forks_count":148,"subscribers_count":52,"default_branch":"master","last_synced_at":"2025-05-09T12:51:23.480Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/facundoolano.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["facundoolano"]}},"created_at":"2016-05-24T23:58:21.000Z","updated_at":"2025-05-07T19:49:48.000Z","dependencies_parsed_at":"2023-01-24T16:15:53.744Z","dependency_job_id":"2efc2d68-fc94-40d1-8def-72091a325398","html_url":"https://github.com/facundoolano/aso","commit_stats":{"total_commits":70,"total_committers":5,"mean_commits":14.0,"dds":"0.11428571428571432","last_synced_commit":"9a1a89bdf774f1ea9ba3025e6f0d3a4e41f33c9b"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/facundoolano%2Faso","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/facundoolano%2Faso/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/facundoolano%2Faso/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/facundoolano%2Faso/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/facundoolano","download_url":"https://codeload.github.com/facundoolano/aso/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254464891,"owners_count":22075570,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":[],"created_at":"2024-08-01T18:01:34.518Z","updated_at":"2025-05-16T04:03:40.812Z","avatar_url":"https://github.com/facundoolano.png","language":"JavaScript","funding_links":["https://github.com/sponsors/facundoolano"],"categories":["JavaScript"],"sub_categories":[],"readme":"# App Store Optimization (aso)\n\nThis Node.js library provides a set of functions to aid [App Store Optimization](https://en.wikipedia.org/wiki/App_store_optimization) of applications in iTunes and Google Play.\n\nThe functions use either [google-play-scraper](https://github.com/facundoolano/google-play-scraper)\nor [app-store-scraper](https://github.com/facundoolano/app-store-scraper) to\ngather data, so bear in mind a lot of requests are performed under the hood\nand you may hit throttling limits when making too many calls in a short period of time.\n\n* [Installation](#installation)\n* [API reference](#api-reference)\n   * [Keyword Scores](#keyword-scores)\n      * [Difficulty](#difficulty)\n      * [Traffic](#traffic)\n   * [Keyword suggestions](#keyword-suggestions)\n      * [Suggestions by category](#suggestions-by-category)\n      * [Suggestions by similarity](#suggestions-by-similarity)\n      * [Suggestions by competition](#suggestions-by-competition)\n      * [Suggestions by an arbitrary list of apps](#suggestions-by-an-arbitrary-list-of-apps)\n      * [Suggestions based on seed keywords](#suggestions-based-on-seed-keywords)\n      * [Suggestions based on search hints](#suggestions-based-on-search-hints)\n   * [App visibility score](#app-visibility-score)\n   * [App Keywords](#app-keywords)\n      * [A note on keyword relevancy for iTunes](#a-note-on-keyword-relevancy-for-itunes)\n   * [Store backend configuration](#store-backend-configuration)\n\n\n## Installation\n\n```\nnpm install aso\n```\n\n## API Reference\n\nThe module exports a function to build a client that will query either iTunes (`'itunes'`)\nor Google Play (`'gplay'`):\n\n```js\nconst gplay = require('aso')('gplay');\nconst itunes = require('aso')('itunes');\n\n// do stuff with google play\ngplay.scores('panda').then(console.log);\n\n// do stuff with itunes\nitunes.scores('panda').then(console.log);\n```\n\nThe behaviour of the algorithms is the same for both stores, except where noted.\n\n### Keyword scores\n\nThe `scores` function gathers several statistics about a keyword and builds\n`difficulty` and `traffic` scores that can be used to evaluate the\nconvenience of targeting that keyword.\n\nThe only argument is the keyword itself:\n\n```js\nconst aso = require('aso')('gplay');\n\naso.scores('panda').then(console.log)\n```\n\nReturns:\n\n```js\n{ difficulty:\n   { titleMatches: { exact: 10, broad: 0, partial: 0, none: 0, score: 10 },\n     competitors: { count: 33, score: 5.95 },\n     installs: { avg: 2470000, score: 10 },\n     rating: { avg: 4.04, score: 8.08 },\n     age: { avgDaysSinceUpdated: 81.4, score: 8.53 },\n     score: 8.84 },\n  traffic:\n   { suggest: { length: 3, index: 3, score: 8.7 },\n     ranked: { count: 5, avgRank: 52.2, score: 5.48 },\n     installs: { avg: 2470000, score: 10 },\n     length: { length: 5, score: 8.5 },\n     score: 8.18 } }\n```\n\nScores are calculated as linear functions and aggregated with somewhat arbitrary\nweights. All statistics are included in the response to allow custom scoring\nfunctions to be used.\n\nAny suggestions on how to tune or improve the score calculations are welcome :)\n\n#### Difficulty\n\nThe difficulty of a keyword measures how hard it is to rank high on searches for\nthat kewyord. This is usually the most important aspect to consider when picking\na keyword (after relevance of the keyword for the given app). The lower this score,\nthe better the candidate keyword.\n\nThe properties considered for this score are:\n\n* `titleMatches`: classifies the titles of the top 10 apps for the keyword according\nto how well they match the words that make it: exact (contains all the words, in the same order),\nbroad (contains all the words in a different order), partial (contains some of the\nwords), none (does not contain any of the words).\n* `competitors`: counts how many of the top 100 apps for the keyword actually\ntarget that keyword in their title and description.\n* `installs`: measures the average amount of installs of the top 10 apps. Since iTunes\ndoes not expose the amount of installs, the reviews count is used instead.\n* `rating`: measures the average rating of the top 10 apps.\n* `age`: measures the average time since the apps in the top 10 have been updated.\n\n#### Traffic\n\nThe traffic score estimates how much traffic that keyword gets. Note this factor\nis better considered after picking keywords with high relevance and low difficulty.\nA high score means high traffic and therefore a better keyword candidate.\n\nThe properties considered for this score are:\n\n* `suggest`: For Google Play the amount of characters needed for the keyword to come up as a\nsuggestion in the search box, and the position in the suggestions list. iTunes already\nscores their suggest results, so that number is used instead.\n* `ranked`: the amount of apps in the top 10 of the keyword that appear in their\ncategory rankings, and the average ranking of those that do.\n* `installs`: same metric as in difficulty, but with a lower weight in the overall score.\n* `length`: length of the keyword (less traffic is assumed for longer keywords).\n\n### Keyword suggestions\n\nThe `suggest` function returns a list of suggestions consisting\nof the most commonly used keywords among a given set of apps. There are several\nstrategies to select that set of apps.\n\nThis function takes an options object with the following properties:\n* `strategy`: the strategy used to get suggestions. Defaults to `CATEGORY`.\n* `num`: the amount of suggestions to get in the results. Defaults to 30.\n* `appId`: store app ID (for iTunes both numerical and bundle IDs are supported).\nRequired for the `CATEGORY`, `SIMILAR` and `COMPETITION` strategies.\n* `apps`: array of store app IDs. Required for the `ARBITRARY` strategy.\n* `keywords`: array of seed keywords. Required for the `KEYWORDS` and `SEARCH` strategies.\n\nA common flow of work would be to try all the strategies for a given app, hand pick the most interesting\nkeywords and then run the `scores` function on them to analize their quality.\n\n#### Suggestions by category\nLooks at apps in the same category as the one given.\n\n```js\nconst aso = require('aso')('gplay');\n\naso.suggest({\n  strategy: aso.CATEGORY,\n  appId: 'com.dxco.pandavszombies',\n  num: 5})\n.then(console.log);\n```\n\nReturns:\n```js\n[ 'game', 'world', 'features', 'weapons', 'action' ]\n```\n\n#### Suggestions by similarity\nLooks at apps marked by Google Play as \"similar\". For iTunes the \"customers also bought\" apps are used instead (which may not necessarily be similar to the given app).\n\n```js\nconst aso = require('aso')('gplay');\n\naso.suggest({\n  strategy: aso.SIMILAR,\n  appId: 'com.dxco.pandavszombies',\n  num: 5})\n.then(console.log);\n```\n\nReturns:\n```js\n[ 'game', 'zombies', 'zombie', 'weapons', 'action' ]\n```\n\n#### Suggestions by competition\nLooks at apps that target the same keywords as the one given.\n\n```js\nconst aso = require('aso')('gplay');\n\naso.suggest({\n  strategy: aso.COMPETITION,\n  appId: 'com.dxco.pandavszombies',\n  num: 5})\n.then(console.log);\n```\n\nReturns:\n```js\n[ 'game', 'zombies', 'features', 'app', 'zombie' ]\n```\n\n#### Suggestions by an arbitrary list of apps\n\n```js\nconst aso = require('aso')('gplay');\n\naso.suggest({\n  strategy: aso.ARBITRARY,\n  apps: ['com.dxco.pandavszombies'],\n  num: 5})\n.then(console.log);\n```\n\nReturns:\n```js\n[ 'game', 'zombies', 'features', 'app', 'zombie' ]\n```\n\n#### Suggestions based on seed keywords\nLook at apps that target one of the given seed keywords.\n\n```js\nconst aso = require('aso')('gplay');\n\naso.suggest({\n  strategy: aso.KEYWORDS,\n  keywords: ['panda', 'zombies', 'hordes'],\n  num: 5})\n.then(console.log);\n```\n\nReturns:\n```js\n[ 'features', 'game', 'zombies', 'panda', 'zombie' ]\n```\n\n#### Suggestions based on search hints\nGiven a set of seed keywords, infer a new set from the search completion suggestions of each one. Then look at apps that target the resulting keywords. This is expected to work better for iTunes, where the search completion yields more\n  results.\n\n```js\nconst aso = require('aso')('gplay');\n\naso.suggest({\n  strategy: aso.SEARCH,\n  keywords: ['panda', 'zombies', 'hordes'],\n  num: 5})\n.then(console.log);\n```\n\nReturns:\n```js\n[ 'game', 'features', 'zombie', 'zombies', 'way' ]\n```\n\n### App visibility score\n\nThe `visibility` function gives an estimation of the app's discoverability within\nthe store. The scores are built aggregating how well the app ranks for its target\nkeywords, the traffic score for those keywords and how the app ranks in the\ntop global and category rankings.\n\nThe only argument to the function is the App ID (package id for Google Play and\neither numerical or bundle ID for iTunes).\n\nGoogle Play example:\n```js\nconst aso = require('aso')('gplay');\n\naso.visibility('com.dxco.pandavszombies').then(console.log);\n```\n\nReturns:\n\n```js\n{ keywords:\n   { 'panda vs zombies': { traffic: 2.94, rank: 1, score: 29.4 },\n     rocky: { traffic: 7.81, rank: 74, score: 57.48 },\n     'panda vs zombie': { traffic: 3.49, rank: 8, score: 34.03 },\n     'panda warrior': { traffic: 1.47, rank: 5, score: 14.49 },\n     'zombie elvis': { traffic: 3.3, rank: 1, score: 33 },\n     meatloaf: { traffic: 5.79, rank: 16, score: 54.77 },\n     ftw: { traffic: 2.88, rank: 58, score: 22.87 } },\n  collections:\n   { global: { rank: undefined, score: 0 },\n     category: { rank: undefined, score: 0 } },\n  score: 246.04 }\n```\n\niTunes example:\n\n```js\nconst aso = require('aso')('gplay');\n\naso.visibility(284882215) // ID for the facebook app\n  .then(console.log);\n```\n\nReturns:\n```js\n{ keywords:\n   { facebook: { traffic: 9.55, rank: 1, score: 95.5 },\n     friends: { traffic: 7.21, rank: 2, score: 71.74 } },\n  collections:\n   { global: { rank: 3, score: 991 },\n     category: { rank: 2, score: 99.5 } },\n  score: 1257.74 }\n```\n\n### App keywords\n\nThe `app` function returns an array of keywords extracted from title and description\nof the app. The only argument is the Google Play ID of the application (the `?id=` parameter on the url).\n\n```js\nconst aso = require('aso')('gplay');\n\naso.app('com.dxco.pandavszombies').then(console.log)\n```\n\nReturns:\n\n```js\n[\n  'panda',\n  'rocky',\n  'zombie',\n  'panda vs zombie',\n  'elvis',\n  'undead',\n  'time',\n  'game',\n  'vs',\n  (...)\n]\n```\n\n[retext-keywords](https://github.com/wooorm/retext-keywords) is used to extract the keywords\nfrom the app title and description.\n\n#### A note on keyword relevancy for iTunes\n\nAs said, the algorithm used by the `app` function extracts the keywords from title and\ndescription. This algorithm is also used internally by the `scores` and\n`suggest` functions.\n\nWhile in all cases the most important place to look at for keywords is the title,\nthe app description is usually less relevant in the iTunes app store, since there's\na specific keywords list field when submitting the app. Unfortunately the contents\nof that field are not (that I know of) reachable from any public page or API. So\nkeywords based on description may not have a big weight on iTunes searches.\n\nGoogle Play, on the other hand, doesn't have a keywords field and so the description is\nexpected to contain most of the app's targeted keywords.\n\n### Store backend configuration\n\nAn object can be passed as a second argument to the client builder function, with\noptions to override the behavior of [google-play-scraper](https://github.com/facundoolano/google-play-scraper)\nand [app-store-scraper](https://github.com/facundoolano/app-store-scraper).\nThe given options will be included in every method call to the stores.\nThis can be used, for example, to target a differnt country than the default `'us'`:\n\n```js\nconst itunesRussia = require('aso')('itunes', { country: 'ru' });\n\n// do stuff with itunes\nitunesRussia.scores('panda').then(console.log);\n```\n\nOther options that may be useful are `cache` and `throttle`. See the reference\nof each scraper for all the available options.\n\n### Note about Google Play performance\n\nWhile iTunes provides an API to search apps with all their details, getting data from Google Play usually requires making a request for the search and then additional requests to get the details for each resulting app, then parsing the HTML. This means that most of the functions of this module (specially scores) will be muchs slower for Google Play than for iTunes (taking even minutes). This is expected given that data is scraped from Google Play in real time on every call. This can be partially mitigated using memoization, at the expense of memory usage, but a better approach (outside the scope of this project) to get faster results would be to periodically scan Google Play, save the data to a database and query that for score calculations.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffacundoolano%2Faso","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffacundoolano%2Faso","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffacundoolano%2Faso/lists"}