Refolk
PlaybookEngineering and open source

The Integration Partner Map: Dependents Co-Usage to a Ranked Partner Slate

Turn your package's public dependents into a dated slate of 5 to 10 integration partners, each with an affinity score, a build-or-pitch call, and a named merge owner.

22 min readLast reviewed November 10, 2026Read as Markdown

Key takeaways

  • GitHub does not expose dependents through its REST or GraphQL API, so a co-usage analysis has to start by scraping the dependents page and combining it with an open index such as ecosyste.ms.
  • Python dependents in GitHub's dependency graph are about 71% correct against about 97% for Java, so a Python-ecosystem partner slate needs a hand-check of 30 random manifests before you trust the matrix.
  • Fewer than a thousand packages account for 80% of dependent repositories in large registries, so raw co-usage counts mostly reflect how concentrated the registry is, not real affinity, and the ranking should use a measure that ignores how common each package is, such as log-odds affinity alpha.
  • A pair whose affinity confidence interval includes 0 co-occurs only at chance level and should be dropped, however large its raw co-dependent count.
  • GitHub code search allows 10 requests per minute and returns at most 1,000 results per query, so the glue-code demand check has to come after the statistical filter and big pairs need their queries split by language or path.
  • A CODEOWNERS rule that resolves to the integrations path is verified merge-rights evidence because GitHub requires explicit write access, while a profile title is not: in Refolk's index, 16% of a US partner-title sample was off-target and 52% sat at Meta.

This playbook is for DevRel leads, technical founders, engineering managers, and technical sourcers who have to decide which adjacent tools a developer product should integrate with next, and who on each of those projects can say yes. Instead of a customer survey, it uses evidence from code: the dependency manifests of your actual adopters, corrected for popularity bias and confirmed by glue code they have already written by hand. You end with a dated slate of 5 to 10 partner projects. Each has a co-usage score, a build-or-pitch call, and the named owner who controls the merge.

What does an integration partner map deliver?

An integration partner map is a dated, ranked list of 5 to 10 adjacent projects. Your adopters use each one alongside your package more often than chance would predict, and each is tied to a named person who can approve the integration. It answers "which integrations to build first" from what adopters have shipped, not from what they say in an interview.

The guides that currently rank for prioritising integrations (getknit, Prismatic, viasocket) all start from customer demand. Prismatic's rule of thumb is that any tool found in 70% or more of your top 20 customers' stacks is a must-have. That is a fair sanity check, but it has two blind spots. It only hears from customers you can reach, and it drifts toward the biggest platforms because those are the names everyone remembers. Manifests have neither problem. Every public repo that depends on your package lists, in a file, the other packages it pairs with yours.

Each row of the finished slate carries:

  • Candidate project: the project and its package name in the registry.
  • Co-usage score: affinity alpha with its confidence interval, plus the raw count and the count expected by chance.
  • Glue-code count: files, adapter repos, and issues that already connect the two tools by hand.
  • Surface audit: plugin API, integrations directory, CODEOWNERS, CONTRIBUTING, and responsiveness to outside PRs.
  • Call: build (you can ship it yourself against their public surface) or pitch (it needs their merge or endorsement).
  • Named owner: the maintainer or company person who controls the merge, and the evidence for it.
  • Date and thresholds: recorded so the next run can be compared with this one.

This is not the same job as turning adopters into sales accounts or recruiting channel resellers. Here your adopters are the evidence, and the person you are trying to reach is the maintainer of another project.

From dependents to a named partner slate

  1. Dependents
    Combine scraped GitHub dependents with an open index and drop forks
  2. Manifests
    Parse declared and locked dependencies, tag each as direct or transitive
  3. Affinity
    Score every co-dependency against chance, not raw frequency
  4. Glue code
    Count hand-written adapters for pairs that passed the filter
  5. Surface and owner
    Audit the extension point and name who controls the merge
  6. Slate
    Publish 5 to 10 dated rows, each with a build-or-pitch call
Each stage narrows the candidate list before the next one, so the scarce resources, code search quota and maintainer attention, go only to pairs that survived the statistics.
The tools your adopters already wire to yours by hand are your integration roadmap, written down in their manifests.

The procedure: from dependents pull to published slate

The procedure has eight steps. Expect roughly four to six working days of engineering time, plus about 90 minutes for each shortlisted candidate. The order matters: the statistical filter runs before the glue-code search because GitHub rate-limits code search so tightly that you have to budget it.

Dependents pull to ranked partner slate

  1. Pull and union the dependents list
    Scrape your package's GitHub dependents page with github-dependents-info and combine it with the ecosyste.ms index, labelling each repo by source. Done is a deduplicated list with no forks and a source label on every repo (about 1 day).
  2. Parse manifests into a presence matrix
    Fetch each repo's manifest and lockfile, plus a same-ecosystem background sample, and tag every dependency as direct or transitive. Done is a repo × package presence matrix with N recorded (1 to 2 days).
  3. Score co-usage with affinity alpha
    For every candidate, compute the raw co-dependent count, Jaccard, and log-odds affinity alpha with its confidence interval. Done is a ranked table showing N, mA, mB, the observed count, and the null expectation (half a day).
  4. Filter to direct, well-supported pairs
    Drop pairs below your pre-set minimum count, pairs that are transitive only, and pairs whose alpha interval includes 0. Done is a shortlist of 10 to 25 candidates with the threshold written down (about 2 hours).
  5. Count hand-written glue code
    Search code for files that import both packages, and look for adapter repos and issues that name both tools, within the rate limit. Done is a demand count per candidate with the queries recorded (1 to 2 days).
  6. Audit each candidate's integration surface
    Check for a plugin API, an integrations directory, CODEOWNERS on that path, a CONTRIBUTING file, and the median human Time to First Response on external PRs. Done is a build or pitch call per candidate (about 1 hour each).
  7. Name the owner who controls the merge
    Compare the CODEOWNERS rule for the integrations path with recent merge history there, then find the backing company's DevRel or partnerships owner. Done is a named person and an evidence tier per candidate (about 30 minutes each).
  8. Publish the dated slate
    List 5 to 10 projects, each with a score, a glue-code count, a build-or-pitch call, and a named owner, under a header that records the date, sources, and thresholds.

Who does what, and why the order holds:

  • Steps 1 to 4 are engineering work and can run unattended once scripted.
  • Step 5 is limited by GitHub's code search cap of 10 requests per minute, about 600 queries an hour. That is plenty for 20 filtered candidates with a few query variants each. It is not enough for two hundred unfiltered packages.
  • Steps 6 and 7 are done per candidate. A DevRel lead and a sourcer can work through them in parallel.
  • Step 8 is a decision meeting, and the slate is its only input.

The procedure rests on one structural point. Affinity needs a population in which your package is sometimes absent. If the matrix holds only your dependents, every row contains your package and there is nothing to compare against. So pull a background sample of repos from the same language and ecosystem out of the open index, parse it exactly as you parse your dependents, and record the combined population size as N.

Where do dependents come from, and how wrong are they?

GitHub does not expose the "Used by" list or count through its REST or GraphQL API. You have to scrape the dependents page and combine it with an open index. Expect errors of up to 10% on dependents and over 27% on dependencies, and expect Python to be much worse than Java.

There are two practical sources:

  • The GitHub dependents page, scraped with github-dependents-info. The "Used by" widget only appears once a package has at least 100 dependents, so a young package gets no visible signal there.
  • ecosyste.ms, an open index covering 15.2 million packages, 349 million repositories, and 25.4 billion dependencies, queryable through its Packages API. For a package below the 100-dependent line, an open index is the only way in.

No published figure says how many extra dependent repos an open index adds over the scrape for a typical package. Measure it for your own package instead. Label every repo by source (scrape only, index only, or both) and put all three counts in the slate header.

How accurate the dependency graph is

The best public measurement is Bifolco et al., "On the Accuracy of GitHub's Dependency Graph", together with its journal follow-up.

MeasureJavaPython
Dependents correctly identified96.62%70.94%
Repos with every dependent correct13.30%21.88%
Dependencies matched in manifest or lock files77%96%

Three things follow from this table:

  • Python needs a verification pass. Python dependents are 26 points less accurate than Java's. Name collisions and similar false positives get into the matrix. Java slates can mostly skip a manual check. Python slates cannot.
  • Treat the list as a sample, not a census. In either ecosystem, few repos had a fully correct dependents list. That is fine for affinity, which works on proportions, but don't quote the dependent count as a market size.
  • Lockfiles matter more in Java. In Java, only 77% of the dependency graph's entries matched the manifest or lock files, so parse the files yourself rather than trusting the graph.

For the verification pass, pick 30 manifests at random, open each one, and confirm that it really declares your package and not something with a similar name. Write the hit rate in the slate header. If it is low, clean the matrix before you score anything.

Direct versus transitive dependencies

A direct dependency is one the repo's author declared in the manifest. A transitive dependency was pulled in by another dependency. Only direct co-usage shows that someone chose to use both tools, so tag every dependency, using the manifest for direct and the lockfile for the rest.

No published procedure or worked example shows how much a top-20 ranking shifts when transitive pairs are removed. Measure it on your own data: rank both ways and record how many of the top 20 change. If the number is large, your raw ranking was mostly measuring what other packages pull in.

Which co-usage measure survives popularity bias?

Rank on the log-odds affinity index alpha and its confidence interval, and show the raw count and Jaccard alongside it. Alpha is designed to be "insensitive to prevalences", meaning it ignores how common each package is. That is exactly the property that stops ubiquitous packages from swamping the ranking.

Ubiquity bias is built into package registries. In Andrew Nesbitt's words, a small set of packages, "often less than a thousand even in registries with over a million packages, account for 80% of all downloads or dependent repositories." So any raw-count list of packages commonly used with your library mostly reflects how concentrated the registry is. Your test runner and linter come out on top because they come out on top for everyone.

MeasureWhat it countsHow it misleads
Raw co-dependent countRepos using both packagesPuts test runners and linters first
JaccardShared repos over repos using eitherStill shifts with each package's prevalence
Sørensen-Dice, SimpsonOverlap variantsUseful cross-checks, same prevalence issue
Affinity alphaLog odds of co-occurrence versus chanceBlows up on tiny counts, so read the interval

Computing it

The CooccurrenceAffinity R package, by Kumar Mainali and Eric Slud, computes alpha and its confidence interval. It also returns Jaccard, Sørensen-Dice, and Simpson, the expected co-occurrence count under the null (the count you would see if the two packages were unrelated), and a p-value, so you can compare the measures side by side. For each pair it needs four numbers:

  • N: repos in the population, meaning your dependents plus the background sample.
  • mA: repos in N that depend on your package.
  • mB: repos in N that depend on the candidate.
  • X: repos in N that depend on both.

The package's interface can change between releases, so check its current CRAN reference before scripting against it.

Reading a row

  • Interval entirely above 0: the two packages occur together more often than chance. Keep it as a candidate.
  • Interval includes 0: they co-occur at chance level. Drop the pair, however large its raw count.
  • Huge alpha from a handful of repos: this is a small cell. The package notes that extreme counts are compatible with unbounded log odds. Report the observed count next to the null expectation and let the interval width show how uncertain it is.

To check stability locally, resample your repos with replacement, rescore, and see whether the top 10 holds. If candidates change places on every resample, raise the threshold and record why.

Build or pitch: how do you read a candidate's integration surface?

Call it build when the candidate exposes a public extension point you can ship against and answers outside contributors promptly. Call it pitch when the integration has to live in their codebase, or only matters if they endorse it. The audit takes about an hour per candidate.

SignalWhat it provesWhen it lies
Plugin API or extension pointYou can ship without permissionDocumented but no longer maintained
Integrations directory in repoThey accept third-party code in treeHolds only first-party integrations
CODEOWNERS on that pathNamed people with write access review itOnly a * catch-all rule exists
CONTRIBUTING fileA stated process for outside workBoilerplate nobody follows
Median Time to First ResponseOutside PRs get human attentionBots answer first and inflate it

Notes on the two signals that are easiest to misread:

  • CODEOWNERS location. The file can live in .github/, the repo root, or docs/. GitHub uses the first one it finds, checking in that order, and paths are case-sensitive. If you read a file in docs/ while one also exists in .github/, you have read the wrong file.
  • Time to First Response. CHAOSS defines it as the time from when an activity is opened, such as an issue or change request, to when it "received the first response from a human." Its example guideline is that every PR should get a human response within two business days. Measure it on external PRs only, and leave out bot accounts and authors replying to their own threads.

No published benchmark exists for what makes a good plugin API or integrations directory. Treat those signals as yes-or-no checks, not scores.

Build or pitch

Strong co-usage evidenceWeak co-usage evidence
Pitch
Take the glue-code count to the named merge owner and ask for an in-tree integration
Build now
Ship against their public extension point, then submit it to their integrations directory
Park
Leave it off the slate and re-score it next run
Watch
Cheap to build, but wait for the evidence to firm up before spending on it
Closed surface, slow to respondOpen plugin API, responsive
Co-usage evidence decides whether a candidate belongs on the slate at all, and the integration surface decides whether you ship it yourself or need their owner to say yes.

Who controls the merge on the other project?

The owner you want is whoever the CODEOWNERS rule for the integrations path resolves to. GitHub only honours CODEOWNERS entries for users and teams with explicit write access, so that entry is verified evidence. Recent merge history on the same path is the second check. A company title is the weakest evidence and needs one of the other two before you rely on it.

Evidence that someone controls the merge

  1. CODEOWNERS rule for the integrations path
    Verified write access, scoped to the code your integration touches
  2. Merge history on that path
    Shows who actually approves and merges outside work there
  3. Company DevRel or partnerships owner
    Controls endorsement and co-marketing, not necessarily the merge
  4. Profile title alone
    Unverified, so confirm it with one of the layers above
Each layer down is weaker evidence, so a name supported only by a profile title is a lead to confirm, not an owner.

Projects with a company behind them have a second owner: someone at that company who runs developer relations or partnerships. No documented public route exists for confirming who owns a partner program, so treat any company name you find as a lead until merge history or a direct reply confirms it.

This cross-check is where Refolk removes the most friction. You describe the people you need in plain English, such as the maintainers with merge rights on a given path or the DevRel lead at the backing company, and get matching people back from across GitHub, LinkedIn, and the open web. You then check each name against the repo itself.

How big the owner populations are

The two title families you will search behave very differently. In Refolk's index of professional profiles, DevRel titles outnumber partner titles almost three to one, and the partner-title population is concentrated in one employer.

338
People in the United States holding DevRel or Developer Advocate titles in Refolk's index
That is 2.7 times the 123 US profiles with partner, ecosystem, or technology-partnerships titles, which makes DevRel the wider route into a backing company.
Measure (US, Refolk's index)DevRel / Developer AdvocatePartner / Ecosystem titles
Total matching profiles338123
Top employer share of 25-profile sample4% or less each, across 10 firms52% (Meta)
Off-target titles in sample8%16%
Dominant title in sampleHead of DevRel, 36%Partner Engineer

The country also changes who reads your pitch first.

Measure (Refolk's index)USUK
Total DevRel profiles33855
Head of DevRel share of sample36% (9 of 25)20% (5 of 25)
Developer Advocate share of sample32% (8 of 25)68% (17 of 25)
Off-target titles in sample8% (2)4% (1)

UK DevRel is mostly individual-contributor advocates, so a UK pitch is more likely to land with someone who has to escalate it. Write the pitch so it can be forwarded without extra context: the evidence, the ask, and the name of the merge owner you have already identified.

Integration pitch to a merge owner
Subject: [Your package] + [their package]: your users already wire them together

Hi [first name],

You're listed in CODEOWNERS for [integrations path] in [repo], so you're the right person to ask.

Of the public repos that depend on [your package], [X] also depend directly on [their package]. That is [observed vs expected] what you'd expect by chance (affinity alpha [value], interval [low to high]).

Those users are already connecting the two by hand: [glue-code count] files import both, and [adapter repo count] standalone adapter repos exist.

I'd like to [build an adapter against your plugin API / contribute an integration to [path]]. Is that something you'd review, and is there a format you prefer?

Happy to share the full list of repos behind these numbers.

[Name], [role], [your package]

Fill each bracket from the slate row; delete the glue-code line if the count is zero rather than inflating it.

How this goes wrong: failure modes and false positives

Most failures in this method are false positives that look like strong evidence. The common ones are name collisions in the dependents list, ubiquitous packages topping the raw counts, tiny cells with huge alpha, truncated code search, bot-inflated responsiveness, catch-all CODEOWNERS rules, and titles that sound like partnership owners but are not. Each has a cheap check.

In the data

  1. Name collisions in the dependents pull.
    • What it looks like: repos in the matrix that never actually imported your package. In Python, where only about 71% of dependents are identified correctly, a meaningful share of the matrix can be noise.
    • Check: hand-verify 30 random manifests against the package they really declare, and record the hit rate.
  2. Ubiquity.
    • What it looks like: the test runner, linter, and formatter top a raw-count ranking.
    • Check: if the alpha interval includes 0, the pair co-occurs only at chance level. Drop it.
  3. Small cells.
    • What it looks like: a pair seen in 2 repos gets an enormous alpha and jumps to the top. Extreme counts are compatible with unbounded log odds.
    • Check: report the interval and the observed count next to the null expectation, and apply the threshold you set in advance.
  4. Transitive pairs passing as choices.
    • What it looks like: a candidate that ranks highly only because another of your dependencies pulls it in.
    • Check: score direct-only pairs, and record how many of the top 20 changed when you switched.

In the demand evidence

  1. Truncated code search.
    • What it looks like: big, popular pairs show suspiciously round or capped glue-code counts. Code search returns at most 1,000 results (100 per page) and excludes forks by default, so demand for the largest pairs is undercounted without warning.
    • Check: split queries by language or path until no single query hits the cap.
  2. A few hobbyists posing as demand.
    • What it looks like: dozens of glue files, all from one or two accounts.
    • Check: no published count separates real demand from a handful of hobby projects. Report the number of distinct repo owners next to the file count, and say in the slate that the threshold is your own judgement.

In the surface audit

  1. Bot-inflated responsiveness.
    • What it looks like: a project with a near-instant Time to First Response that never merges outside PRs. CHAOSS itself warns that bots can distort this metric.
    • Check: exclude bot accounts and authors replying to their own threads, then recompute the median.

In the owner trace

  1. Catch-all CODEOWNERS rules.
    • What it looks like: the "owner" you found is an org admin who never touches integrations.
    • Check: resolve the rule for the exact integrations path, remembering that the last matching pattern wins.
  1. Titles that sound like owners.
    • What it looks like: a "Partner Engineer" who seems like the obvious partnership contact.
    • Check: confirm merge history on the target repo before naming anyone.
52%
Share of a 25-profile sample of US partner-title holders in Refolk's index who work at Meta
Most hold the title Partner Engineer, a platform-partner role rather than an open-source merge owner, and 16% of the same sample was off-target entirely.

When you search Refolk for partnership or DevRel owners, filter by the candidate's backing company rather than by title alone. Treat every result as a tier-three lead until the repo itself confirms it.

Keeping the slate current

A partner slate is a dated snapshot. Store the inputs, thresholds, and query strings with it, and re-run the whole pipeline on a cadence you choose and write down. Then compare runs: candidates that rise between runs are your new leads, and candidates whose interval slides toward 0 are coming off the slate.

Several inputs here are platform behaviour, not fixed facts, and they can change without notice. Re-check each one against its source before every run:

  • The "Used by" threshold. Look at a package you know sits just above or below 100 dependents and see whether the widget appears.
  • Code search limits. Re-read GitHub's search REST documentation for the current request rate and result cap.
  • The affinity package. Check the CooccurrenceAffinity reference on CRAN for interface changes.
  • CODEOWNERS behaviour. Re-read GitHub's code owners documentation for lookup order and matching rules.
  • Owners. Re-resolve CODEOWNERS for every candidate path, because maintainers rotate and a stale name wastes a pitch.

Keep each run in a single record so that the next person can repeat it without asking you anything.

Partner slate record
SLATE: [your package] integration partners
Run date: [YYYY-MM-DD]
Sources: scrape [n repos] / index [n repos] / both [n repos]
Background sample N: [n]  Ecosystem: [language]
Manifest verification: [hits] of 30 correct
Minimum co-dependent count: [n], set before ranking
Direct-only top-20 change vs all deps: [n] of 20
Code search queries: [file or link to query list]

CANDIDATE: [project] ([package name])
  Co-usage: X=[n] expected=[n] alpha=[value] interval=[low, high]
  Raw count rank: [n]  Jaccard: [value]
  Glue code: [files] files / [owners] distinct owners / [adapters] adapter repos / [issues] issues
  Surface: plugin API [y/n] | integrations dir [path or n] | CODEOWNERS on path [y/n] | CONTRIBUTING [y/n]
  Median human TTFR on external PRs: [days], bots excluded
  Call: BUILD / PITCH
  Owner: [name, handle]  Evidence tier: CODEOWNERS path / merge history / company title
  Company contact: [name, title] (unconfirmed / confirmed by [how])

One header per run and one row block per candidate; leave a field as "not checked" rather than blank.

Before you publish the slate

  • Every repo in the matrix carries a source label, and forks have been removed.
  • For a Python slate, 30 random manifests have been hand-verified and the hit rate is in the header.
  • The minimum co-dependent count was fixed before ranking and is written in the header.
  • No slate entry has an alpha confidence interval that includes 0.
  • Every glue-code count comes from queries that stayed under the 1,000-result cap.
  • Time to First Response was measured on external PRs with bots and self-replies excluded.
  • Each named owner comes from a CODEOWNERS rule for the integrations path or from merge history, not from a title alone.
  • Each row has a build or pitch call and the evidence behind it.
  • The slate has 5 to 10 rows and a run date.

Questions practitioners ask

How do I find which packages are commonly used together with my library?

Use the public repos that depend on your package. Parse their manifests and score each co-dependency against chance, not by raw frequency. Pull dependents from the GitHub dependents page and an open index, tag each dependency as direct or transitive, and compute log-odds affinity alpha against a background sample of repos from the same ecosystem. Raw counts will put generic tools such as test runners and linters at the top every time.

Can I get the GitHub 'Used by' dependents list through the API?

No. GitHub does not expose the dependents count or list through its official REST or GraphQL API. The only ways in are scraping the dependents page, for example with github-dependents-info, or querying open indexes such as ecosyste.ms. The 'Used by' widget also appears only once a package has at least 100 dependents. Below that, an open index is your only source.

Why not just rank integrations by how many of my users use each tool?

Because raw counts measure how popular a tool is, not how strongly it pairs with yours. In large registries, fewer than a thousand packages account for 80% of dependent repositories, so ubiquitous packages lead every raw ranking. A survey rule such as Prismatic's 70%-of-top-20-customers threshold works as a sanity check. For ranking, use a measure built to ignore how common each package is, and read it with its confidence interval.

What minimum co-dependent count should I use as a filter?

No published threshold keeps co-usage rankings stable under resampling, so you have to set one yourself. Pick a minimum count before you look at the ranking and write it in the slate header. Then resample your repos and check whether the top 10 holds. If it does not, raise the threshold and record why. Always report the observed count next to the null expectation.

How do I know who can merge an integration into another open source project?

Find the CODEOWNERS rule that applies to that project's integrations or plugins path. GitHub only honours CODEOWNERS entries for users and teams with explicit write access, so a match there is verified evidence. Check the repo's .github/, root, and docs/ directories in that order. Ignore catch-all rules, then confirm the name against recent merge history on the same path.

Should I build the integration myself or pitch it to the other project?

Build it yourself when the candidate has a public plugin API or integrations directory and answers outside PRs quickly. Pitch it when the integration has to live in their codebase, or only matters if they endorse it. When co-usage evidence is strong but the surface is closed, pitch the named merge owner and lead with the glue code your shared users have already written.

Try it on the search you came here for

Stop building boolean strings. Just describe the person.

Type one sentence. I plan the search, read GitHub, public LinkedIn and Crunchbase records, and the open web as it is right now, and hand back a ranked list with the reason next to every name.

  1. 01Describe them

    One plain sentence. Role, city, stack, stage, whatever matters to you.

  2. 02I read the web live

    GitHub, public LinkedIn and Crunchbase records, the open web. Not a database that went stale last quarter.

  3. 03You read the shortlist

    Ranked, with the reasoning under every name. Open a profile, ask a follow-up, narrow it down.

  • No boolean, no filters, no seat to buy. One box.
  • Read at search time, so a profile updated yesterday counts today.
  • Every step visible as it runs, every name with its reason.

500 free credits on sign-up. No card, no demo call. See real searches.

Read next