Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

You can keep extension install counts and version numbers consistent across the Visual Studio Marketplace, Open VSX, a portfolio page, and repository documentation by feeding all of them from one data model: a browser module that shows visitors cached values and refreshes them in the background, and a scheduled Node script that rewrites the static HTML and README fallbacks. That is the design developer freerave describes for the dotUniverse portfolio in a DEV Community article published September 28, 2026 (the original implementation article). The example covers seven extensions within a portfolio the author describes as more than 20 open-source tools.

This guide walks through both paths, the exact endpoints and fields the example reads, the 24-hour cache logic, and the points where the numbers need careful labelling before you publish them as a single figure.

The manual work this design removes

The author’s starting problem is a familiar one for maintainers of several extensions. Each release means logging into more than one web dashboard, working out cross-platform totals by hand, and then editing HTML cards, version badges, and README tables one by one. The synchronizer replaces those steps with two requirements: visitors should never wait on live API calls, and anyone reading the static HTML or README, including crawlers and tools that never run JavaScript, should still see reasonably current values.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Data sources and what each count means

The two registries return different fields under different names, so the first step is to normalize them into one structure. The table below summarizes what the example reads from each.

Aspect Visual Studio Marketplace Open VSX
Request shape One POST to https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery with criteria for multiple extension IDs and flags requesting statistics, versions, and metadata One GET per extension to https://open-vsx.org/api/{namespace}/{extension}
Fields read install and downloadCount from the statistics array (matched by name); the first returned version downloadCount and version
Label used in the example output “Installs” “Downloads”
Map key Lowercase extension name Extension identifier
Failure handling Not stated in the source article Entries without an Open VSX identifier are skipped; an individual failed request does not discard other results

Visual Studio Marketplace

The batched query keeps the number of network calls low, which matters once a portfolio grows. The sample maps the returned statistics by name, reads the first version in the response, and stores the lowercase extension name as the key.

The displayed install total is calculated as:

Math.round((install || 0) + (downloadCount || 0))

The author reports that this sum matched the Marketplace UI’s “Installs” figure for all seven extensions in a manual cross-check. That is an observation from one portfolio on the date of writing, not a documented guarantee about what the API fields mean or how they will behave over time. Microsoft’s publishing documentation describes the publisher management page’s acquisition figures, but it does not specify this formula. Before relying on the sum, compare it with the UI for each of your own extensions.

Open VSX

The Open VSX path is simpler: one request per extension, reading downloadCount and version. Entries with no Open VSX identifier are skipped, and each request’s failure is handled on its own so that one broken extension does not blank the whole portfolio.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The author reports that the endpoint works from browser cross-origin requests. Treat that as observed behavior: it can change, and no published policy guaranteeing it was located. If your site depends on it, check the response from your own origin.

Combining the two counts

The example labels the Marketplace value “installs” and the Open VSX value “downloads,” and the names are not interchangeable. A portfolio-wide headline that adds them is an aggregate of two registries’ reported counts. It is not a count of unique people or machines, and a user who installs an extension from both registries is counted twice. Label the total accordingly, for example “combined registry downloads and installs.”

The sample terminal output in the article totals 19,502 (4,401 from the Marketplace plus 15,101 from Open VSX). That figure is from one sample run, not a live count, and it should not be presented as current.

The browser path: cached values first, refresh second

The visitor-facing module is a small script named extension-stats.js. Its job is to paint something useful immediately and then correct it if needed.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Cache key, TTL, and the read-before-fetch rule

The cache lives in localStorage under the key dotuniverse_ext_stats_v1. The time-to-live is set to 24 * 60 * 60 * 1000 milliseconds, which is 86,400,000 ms or exactly 24 hours.

On initialization the module behaves as follows:

  1. Read the cached payload and apply it to the page at once, so visitors do not wait on the network and text does not flicker from placeholder values to live ones.
  2. If no cache exists, or the stored timestamp is older than the TTL, request fresh data.
  3. When a fresh response arrives, replace the displayed values and write the new payload and timestamp back to storage.

The author chose 24 hours because releases for these tools tend to arrive over days or weeks, so a daily window keeps metrics reasonably fresh while cutting repeat API requests. This is a practical default for that project. It is not an established optimal duration, and no independent benchmark supporting it was identified. Choose a TTL based on your own release rhythm and API quota.

The consequence to plan for is that a visitor can see values up to 24 hours old. The model also needs a rule for a refresh that fails after the cache has expired. The example does not spell this out, so decide it deliberately: keeping the last known values visible is the more forgiving choice for a portfolio page, while blanking them is the more honest one. Either way, avoid letting a failed refresh overwrite good cached data with empty results.

Mapping data to the page

Each extension card carries a semantic data-ext-name attribute. That attribute links a normalized API result to the elements it should update: the version string, each store’s badge, and the portfolio total. Static values written into the HTML act as the baseline, so the page is complete before any script runs, and a successful fetch replaces those values as it completes.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The repository path: a scheduled script for static fallbacks

The second path serves readers who never run the browser module. A Node script, scripts/sync-extension-stats.mjs, fetches the current metrics, updates index.html and README.md, and prints a summary to the terminal.

The sync script

The script performs the same normalization as the browser module, so both paths share one data model. Its terminal summary is useful for checking a run in CI logs, because it shows the values that were written. Because the README and HTML are regenerated from the same values, a reader who views the repository on GitHub sees the same numbers as the portfolio page as of the last run.

The scheduled workflow

The workflow example uses the following configuration. Treat the Node version and the exact steps as the author’s example, not a general requirement.

  1. Trigger on cron 0 0 * * *, which runs daily at midnight UTC, and also allow a manual workflow_dispatch run.
  2. Check out the repository.
  3. Set up Node.js 20.
  4. Run scripts/sync-extension-stats.mjs, which updates the HTML and README.
  5. Stage the generated files.
  6. Check whether the staged diff is empty. If it is, stop without committing.
  7. If there are changes, commit and push them.

The empty-diff check is what keeps the history clean: on days when no count or version has changed, the job makes no commit. The job needs permission to push to the repository, so review the workflow’s token permissions and the commit identity it uses before enabling it on a public repository.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Why run both paths

Question Browser path Scheduled repository path
Who sees the output Visitors whose browsers run the module Readers of the static HTML and README, including crawlers and tools that do not run JavaScript
Freshness Cached values for up to 24 hours; refreshed on a visit once the cache has expired Updated once a day at 00:00 UTC, and only committed when values change
Output when a fetch fails Previously cached or static values remain available as the baseline, per the design; exact handling after expiry is left to the implementer Not stated in the source article
Requires JavaScript Yes No

The combination gives each audience an appropriate level of freshness. Visitors get metrics that can be newer than the committed files, and readers who never run the module still get a complete page and README.

Official publishing context

Microsoft’s Visual Studio Code extension publishing documentation describes vsce as the command-line tool for packaging, publishing, and managing extensions. It documents SemVer-compatible version increments such as vsce publish minor. It also states that the Marketplace publisher management page provides each extension’s acquisition trend, total acquisition counts, and ratings and reviews. In Microsoft’s words, the page “gives you access to each extension’s Acquisition Trend over time, as well as Total Acquisition counts and Ratings & Reviews.”

The same documentation distinguishes unpublishing from removal. Unpublishing keeps an extension’s statistics and leaves it discoverable through an existing API, whereas removing an extension deletes its statistics. This matters for a synchronizer. If the script treats an extension that has dropped out of normal listings as deleted and removes its configuration entry, the counts that still exist are lost from your page. Check the extension’s state in the publisher management page before removing an ID from the configuration.

Adapting the pattern safely

  • Compare the sum your script produces with the Marketplace UI for every extension you track, not just a sample.
  • Label any combined total as an aggregate across registries, not as unique users.
  • Confirm the current response shape from each endpoint before deploying, because the flags, statistic names, and version ordering in the example are the author’s implementation details.
  • Choose the cache TTL from your own release frequency and API limits, and document it on the page.
  • Decide, in code, what the browser shows when a refresh fails after the cache expires.
  • Scope the CI token to the minimum permissions the job needs, and keep the empty-diff check so unchanged days produce no commits.
  • Keep the terminal summary in the script so that each scheduled run leaves a readable record of the values it wrote.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.