Turn your GitHub interaction history into a networking list: who you've worked with, where they work, when you last spoke, and a LinkedIn link where one exists.
It runs in two stages so you only pay the GitHub API cost once and can iterate on the report for free.
gh_interactions.py ──► people.json ──► linkedin_match.py ──► network.md
(talks to GitHub) (handoff) (no network calls) network.csv
- Python 3.9+ (no third-party packages)
ghCLI, authenticated — rungh auth statusto check
The scripts shell out to gh api, so they use your existing login. No tokens
to manage.
# Stage 1 — pull your interaction graph from GitHub
python3 gh_interactions.py --all
# Stage 2 — resolve LinkedIn and build the report
python3 linkedin_match.pyStart smaller on a first run to see the shape of the data:
python3 gh_interactions.py --no-comments --max-threads 300Everything that touches the GitHub API:
- Finds every issue/PR you're involved in via the Search API (
involves:you) — authored, commented on, assigned, reviewed, or mentioned in. The Search API caps at 1000 results per query, so it windows by date to walk further back. - Collects the other participants and the most recent time you shared a thread with each.
- Fetches each person's profile, plus any LinkedIn URL they put on their
GitHub profile (via the
social_accountsAPI, falling back to a scan of their blog/bio). - Infers a company for people who left the field blank — from public org
memberships,
@handlementions in their bio, or a non-free email domain.
| Flag | Effect |
|---|---|
--all |
Pull the entire history (same as --max-threads 0) |
--max-threads N |
Cap the scan (default 1000) |
--no-comments |
Only thread authors, skip fetching every commenter — much faster |
--no-org-lookup |
Skip the org-membership lookup used to infer companies |
--user NAME |
Someone else's public activity |
--refresh |
Ignore caches and re-pull |
No network calls. Reads people.json and:
- Keeps LinkedIn URLs found on GitHub profiles.
- For everyone else, builds a LinkedIn people-search URL from their name and company — see the caveat below.
- Drops anyone with neither a LinkedIn nor a real name.
- Groups by company, sorts by most recent contact.
| Flag | Effect |
|---|---|
--min-interactions N |
Drop people you've shared fewer than N threads with |
--company-sources ... |
Which inferred company sources to trust (see below) |
--in PATH |
Read a different stage-1 file |
An optional example of post-processing people.json into a hand-tiered
CONTACTS.md — ranked tiers, a junk-company blocklist, and an explicit record
of what was excluded. Treat it as a starting point to edit, not a finished tool.
Written to gh_network_out/:
| File | What |
|---|---|
interactions.json |
Cache: threads and participants |
profiles.json |
Cache: fetched user profiles |
orgs.json |
Cache: public org memberships |
people.json |
Handoff file between the two stages |
network.md |
The report, grouped by company |
network.csv |
Same data, flat |
All of this is gitignored. See Privacy below.
LinkedIn is not scraped, and can't be. LinkedIn blocks automated access behind a login wall and scraping it violates their terms. So there are exactly two kinds of LinkedIn link in the output:
- profile — the person put their LinkedIn on their GitHub profile. Reliable.
- search — no LinkedIn on file, so you get a pre-filled name + company search to confirm by eye. This is a lead, not a match.
If you have legitimate access to a sanctioned lookup API, resolve() in
linkedin_match.py is the single seam where it would plug in.
Inferred companies are noisy. GitHub's company field is optional and most
people leave it blank. The org-membership fallback roughly halves the unknown
bucket, but GitHub orgs are frequently open-source projects rather than
employers — llvm, tldr-pages and sonata-project are not anyone's job.
Every company is therefore tagged with its source:
| Source | Trust |
|---|---|
profile |
They typed it themselves — reliable |
bio |
An @handle in their bio — decent |
org |
A public org membership — noisy, verify |
email |
A non-free email domain — weak |
Filter with --company-sources profile,bio for a smaller, cleaner list.
Rate limits. A full run with comment-fetching is roughly one API call per thread, per comment page, and per person. Large histories can exceed the 5000/hour authenticated limit; the script sleeps and resumes automatically. Caches are written at the end of the gather step, so killing it mid-pull loses that run's progress.
This tool aggregates personal data about real people — names, locations, employers, and social links. It only ever reads public GitHub profile data via the official API, and only for people you have actually interacted with.
.gitignore excludes every output file. Keep it that way. Publishing a
generated list would republish third parties' personal data without their
consent, and aggregating scattered public details into a single profile is a
meaningfully different act from each detail being public on its own. Use it for
remembering who you know; not for building a dossier, and not for bulk outreach.
If you're in the EU/UK, note that a list like this falls under GDPR once it leaves personal use.
MIT — see LICENSE.