How-to › Operate integrations in production

How to judge whether an unofficial SDK is safe to depend on#

Run six checks over a candidate package before you add it, and separate the facts that should stop adoption from the costs you are choosing to take on.

Audience
Platform team
Level
intermediate
Topic
Evaluate, adopt and upgrade SDKs
Verified

The vendor ships no SDK for your language, and the registry offers three packages that claim to wrap the API. One has 40 stars and a friendly README. Nobody on the team can say who publishes it, whether it still tracks the API, or what runs when it installs, and the pull request adding it already has an approval.

What you get

You will end up with six checks that produce facts rather than impressions, and a verdict that separates what should block adoption from what you are choosing to live with. This is for you if you add third-party clients and want the decision written down.

Short answer

Check the license, whether the package runs a script on install, and whether anyone has published it in the last year. Those three are blockers. Then count the maintainers, the transitive dependencies, and the share of the API it covers, and record who publishes it. Those are costs to accept deliberately rather than reasons to walk away.

You will need

Node 22 or later, and the registry metadata and repository for each candidate. Everything the checks read is public: the registry metadata, the license field, and the repository’s release history.

Voxgig maintains the SDK Catalog. This page compares a catalog package with the provider’s own SDK, a community package, and a client you write.

Approaches compared

ApproachWhen it fitsWhat it costs youWhen to pick something else
A community package after an auditNothing official exists and the package covers what you callAn audit you repeat at every upgrade, and a maintainer who owes you nothingThe package fails any of the three blockers
A generated SDK Catalog packageYou want full coverage of an API whose provider ships no clientAn unofficial package, generated from a description, that tracks upstream on its own scheduleThe provider publishes a client for your language
The provider’s official SDKIt exists for your language and covers the operations you callWhatever the vendor decided about retries, errors and types, which you inheritThe official client is unmaintained or missing your language
Writing the client yourselfA handful of operations, or a vendor whose packages all fail the checksEvery operation and every upgrade is yours, foreverThe surface is large enough that hand maintenance loses

Run the checks before the discussion rather than after it. The argument in a pull request is usually about taste, and the facts settle most of it. A package with no license cannot be adopted, whatever anyone thinks of its API. A package published last week by six maintainers needs no defending.

The unofficial ones deserve one more question than the checks ask, which is what happens when the vendor changes the API. An official client ships with the change. A generated one ships when somebody regenerates. A community one ships when its maintainer has a free evening. That difference does not appear in any registry field, and it is the thing that bites.

Make the blockers absolute

The decision rests on three facts a script can check, so it needs no judgment call.

{
  id: 'install-scripts',
  level: 'blocker',
  test: (p) => p.install_scripts === false,
  say: () => 'runs a script on install, which executes code before you import anything',
},

An install script runs on every developer machine and every build agent, with the rights of whoever ran the install, before a single line of your code executes.1 A package that needs one may have a good reason, stated plainly in its README. What matters is that you decide, rather than finding out from an incident report.

License and recency are the same kind of fact. No license means no permission, whatever the README implies,2 and a package unpublished for two years against an API that shipped four versions is already behind.

Write that check as an allow, not a deny. A registry record usually omits the field rather than setting it to none, so a test that rejects one sentinel string waves the absent case straight through and reports a clean adopt. Require a license you recognize, and treat missing, empty and UNLICENSED alike.

Turn the rest into costs you priced

Warnings are not weaker blockers. They are the bill.

{
  id: 'provenance',
  level: 'warning',
  test: (p) => p.kind === 'official',
  say: (p) => p.kind === 'generated'
    ? 'generated by a third party from the description, so an upstream change lands when they regenerate'
    : 'published by somebody other than the API provider, with no commitment to track it',
},

Record the warnings in the pull request that adds the dependency. A year later the package has not been updated, the API has moved, and the question is what anyone knew at the time. That answer belongs in the history rather than in somebody’s memory.

Check it worked

Judge three candidates for the same API.

node demo.mjs
@meterco/sdk           adopt
meterco-client         reject
  blocker  license              license is none
  blocker  install-scripts      runs a script on install, which executes code before you import anything
  blocker  maintained           last published 780 days ago, 0 releases in a year
  warning  bus-factor           1 maintainer, so an unavailable person is an unpatched package
  warning  dependency-weight    214 transitive dependencies come with it
  warning  provenance           published by somebody other than the API provider, with no commitment to track it
  warning  coverage             covers 12 of 61 operations
@voxgig-sdk/meterco    adopt with a plan
  warning  provenance           generated by a third party from the description, so an upstream change lands when they regenerate

The third verdict is the one to read carefully. Every mechanical check passes, but it is still not a plain adopt, because nobody at the API provider has promised anything about it. That is the same warning the community package carries, and the difference between them is the other six lines.

A plain adopt is also a real outcome, and the first row shows it. Six checks passing is not a formality: it means somebody publishes the package, keeps publishing it, and stands behind what it covers. Most of the packages you will run this against will not reach that line.

node --test judge.test.mjs
1..7
# tests 7
# suites 0
# pass 7
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 124.376254

When it goes wrong

A package passes and breaks a week later. The checks measure the package, not the API behind it. Add a contract test against the vendor’s sandbox, and run it on a schedule.

The coverage number is meaningless. Counting operations treats a rarely used endpoint the same as the one you call a million times. Count the operations you actually call instead, and keep that list beside the dependency so the next upgrade can be measured the same way.

Every candidate fails. The API is young or niche. Writing a thin client for the operations you need is a legitimate outcome of this exercise, and often the cheapest one.

The audit passes, but the supply chain is still open. Transitive dependencies change between installs. Commit a lockfile, and review what an upgrade pulls in rather than only what the top package did.

When not to do this

Do not run these checks and then ignore the blockers because a deadline is close. A package with no license is a legal problem that outlives the sprint, and adopting it now means removing it later, under worse conditions.

Do not treat a catalog package as an official one. The SDK Catalog generates clients for APIs whose providers were never asked, so nothing in it carries a support commitment from the vendor.

Do not re-run the checks only at adoption. Every one of them can change, and a package that was maintained when you adopted it is exactly the sort of thing nobody looks at again.

Do not let a high star count substitute for any of this. Stars measure attention at some point in the past, which is why a star count does not answer any of the six checks.3 A package can be popular, abandoned, and unlicensed at the same time.

Last verified

Verified 2026-09-14 against Node 22.22.2. Both output blocks are what the preceding command printed.

Footnotes

  1. Package managers disagree about this. npm’s scripts documentation lists preinstall, install, and postinstall among what npm install runs. pnpm decided the other way. A dependency’s build script does not run unless the package is named in allowBuilds, and with strictDepBuilds at its default of true, one nobody has reviewed fails the install outright. Although the two package managers share one registry, they give opposite answers to whether a stranger’s script should run on your machine by default. ↩︎ Back to text

  2. The default is older than any registry. choosealicense.com, which GitHub curates, states it in one paragraph. A creative work, code included, is under exclusive copyright by default. Without a license, nobody else may copy, distribute, or modify it without risking “take-downs, shake-downs, or litigation” from whoever wrote it. Once the work has other contributors, the page adds, “nobody” starts including you. A README that says feel free is a sentiment, and copyright does not read sentiments. ↩︎ Back to text

  3. The star count has been audited by people other than GitHub. A measurement study posted in December 2024 put the suspected fake stars at 4.5 million in its title. It also found that a fake star promotes a repository for under two months, and counts against it after that. The revision of September 2025 retitled the paper “Six Million (Suspected) Fake Stars in GitHub” and changed “Scams” to “Spams” in the subtitle. The count grew by a third between drafts, which is the kind of growth a star count is meant to signal. ↩︎ Back to text

Read this page as markdown · All how-to guides

Generate the client instead of writing it#

Retries, timeouts, pagination and auth are the same problems in every client. Voxgig generates them from your OpenAPI description, in 23 languages, from one model.

Get the Voxgig dispatch

Short notes on building SDKs, CLIs, REPLs, and MCPs for API-first teams, plus the occasional Fireside episode pick.

By signing up you agree to our Terms and Conditions.