How are you turning GitHub contributions into income?

Hi everyone,

I’ve seen many developers say that consistent GitHub contributions can eventually lead to earning opportunities like:

  • Freelance projects
  • Full-time jobs
  • Open-source sponsorships
  • Bug bounty opportunities
  • Paid consulting

I’m curious to hear from people who have actually done it.

A few questions:

  1. What type of GitHub contributions helped you the most?
  2. Did your GitHub profile directly help you land paid work or bug bounty opportunities?
  3. How long did it take before you started earning?
  4. If you were starting from scratch today, what would you focus on?
  5. Are there any repositories, organizations, or contribution strategies you’d recommend?

I’m currently trying to build a strong GitHub profile while improving my cybersecurity and development skills, so I’d really appreciate hearing your experiences.

Thanks in advance!

3 Likes

Every answer you’re about to get says “contribute consistently, be patient.” That’s a shop with no footfall — and it’s why the question keeps getting asked.

Flip which side you’re reading. The demand side is already public, machine-readable, and dated.

points at readable? has a deadline?
Your contribution graph you barely no
Their manifest + advisory + removal date them fully, by API yes, to the day

:play_button: Paste this. It returns work with a date on it.

PGPASSWORD=udd-mirror psql -h udd-mirror.debian.net -p 5432 -U udd-mirror -d udd -tA \
 -c "SELECT source, to_timestamp(removal_time)::date, bugs FROM testing_autoremovals ORDER BY removal_time LIMIT 6;"

Real output, run today:

matrix-synapse   | 2026-08-10 | 1141211
libervia-backend | 2026-08-10 | 1138268
apksigcopier     | 2026-08-11 | 1140940
diffoscope       | 2026-08-11 |

That’s a named package · the exact bug killing it · the date it gets deleted. Nobody had to be discovered for that to appear. No account, no token — Debian leaves a read-only Postgres open to the world. :warning: The credentials every copy of this gets wrong: it’s udd-mirror / udd-mirror, not udd/udd — that role is IP-locked to Debian’s own infra and fails with a pg_hba error.

No psql? Same data, browser only → autoremovals.yaml


⋈ The whole method is one join

who runs itwhat dies, and when = a named org, a scoped job, and a deadline they didn’t choose — arrived at before they wrote the ticket.


:round_pushpin: Where a real deadline exists — all six of them

Be honest about this, because it’s the part that makes it work: an ordinary abandoned library has no date. No registry assigns one. Hard, externally-authored, non-negotiable dates live in exactly six places:

Feed The date it hands you
Debian autoremovals package deleted from testing on this day
Fedora orphans six-week auto-retirement clock, already ticking
CRAN checks fix it or the package is archived
CISA KEV dueDate — the only legally mandated remediation date in existence
endoflife.date 460+ runtimes/frameworks, support ends then
Homebrew disable_date — formula stops working on that date

Everywhere else, “urgent” is a word you wrote. In these six, it’s a field you read.

🔎 HALF A — pull the list of who actually runs a thing (verified endpoints)

:gem_stone: Any public repo hands you its full resolved dependency list, with no token at all:

curl -s https://api.github.com/repos/<org>/<repo>/dependency-graph/sbom \
  | jq -r '.sbom.packages[] | "\(.name) \(.versionInfo)"'

SPDX 2.3, minted on demand — the org never had to publish anything. 60/hr anonymous, 5,000/hr with any free token. Everyone assumes this needs auth and scopes; it doesn’t.

:gem_stone: Sweep every public manifest at once — regex across 2M+ repos, no account:

curl -s -G -H 'Accept: text/event-stream' 'https://sourcegraph.com/.api/search/stream' \
  --data-urlencode 'q=context:global file:^requirements\.txt$ ^Django==3\. count:all' \
  --data-urlencode 'v=V3' --data-urlencode 't=regexp' | grep -o '"repository":"[^"]*"' | sort -u

Anchors: file:^go\.mod$ · ^pom\.xml$ · ^Gemfile\.lock$ · ^composer\.json$ · ^Cargo\.toml$ · ^\.github/workflows/.*\.ya?ml$ · \.tf$ — plus repo:^github\.com/<org>/.* to scope to one company. (Defaults exclude forks/archived — add fork:yes archived:yes.)

Registries that answer natively — the only four that do:
├─ crates.io · Rust
├─ RubyGems · the whole reverse edge set in one GET
├─ Packagist · PHP — and it carries the abandoned flag naming the replacement, so it answers both halves at once
└─ pub.dev · Dart/Flutter, via the dependency: qualifier

Ranking, not namingdeps.dev v3alpha returns dependent counts (the stable /v3/ path 404s — use v3alpha). Counts tell you blast radius; they never list a name.

github-dependents-info — GitHub’s own “Used by” page as a CLI, because that page has no API.

Named institutions, if you want slow-but-certain buyers: code.gouv.fr/data/deps.json maps French ministries to the libraries they run; Comptoir du Libre names the public bodies deploying each tool.

💣 HALF B — what is breaking, right now or on a schedule (verified endpoints)

Advisories → instant blast radius

curl -s -X POST -H 'Content-Type: application/json' \
  -d '{"package":{"name":"lodash","ecosystem":"npm"},"version":"4.17.20"}' \
  https://api.osv.dev/v1/query

(POST only — a GET returns 405 and people conclude it’s dead.) Batch 1,000 per call. Companion: GitHub Advisory REST, no auth, filterable by published=>.

Abandonment, scored
├─ OpenSSF Criticality Score bulk CSV — dependent count and months-since-update in the same row. Sort it and the top of the list is “hugely used, nobody home.” :gem_stone:
├─ Scorecard API — the Maintained check as an abandonment probe
└─ issues.ecosyste.ms — exposes active_maintainers, i.e. bus factor as a number

Broken this minute
├─ OSS-Fuzz status — ~300 major projects currently failing, with last-success dates. Zero skill, opens in a browser.
├─ Koschei — Fedora rebuilds that broke because an upstream dependency changed
├─ ci.debian.net — autopkgtest status dump
└─ check-runs, anonymously: curl -s https://api.github.com/repos/<org>/<repo>/commits/main/check-runs — proves a named repo has been red since a date

Scheduled to break
├─ Red Hat lifecycle · no auth, no subscription
├─ Pluto versions.yaml · the exact Kubernetes version each API vanishes in
└─ Hackage deprecated · formal deprecation outside the npm/PyPI monoculture

Cross-distro state: Repology:warning: 403s on a naive curl, 200 the moment you send -A 'yourname/1.0'. That one missing header is why the only free cross-distro index sits unused.

osv-scanner — point it at other people’s public repos and SBOMs, not your own. That’s the whole shift in one tool.


:warning: What this does not hand you

Worth more than another endpoint:

├─ An org name is not a person. All of it stops at a repo owner. The last mile — org → someone who can authorise a payment — is manual, every time.
├─ Nothing here measures willingness to pay. 500k dependents and zero maintainers is a fact; that anyone will fund it is your assumption. Adoption queues (Debian ITA, Fedora Take) are unpaid volunteer seats — don’t mistake them for demand.
├─ Private code is invisible, and that’s where the budgets are. Every source above reads public manifests.
├─ Stale lists lie. Some dependency dumps are months to years old. Re-verify a lead live before you contact anyone, or you’ll pitch a dependency they dropped last year.
└─ The endpoints are obscure; the output isn’t scarce. Anyone reading this can run the same query. Speed and the follow-up are the moat, not the URL.

☠️ Traps — things that look alive and aren't (all re-checked today)

├─ ecosyste.ms usage / dependent_packages402 Payment Required. Freshly walled: x-ratelimit-remaining: 4994, so it’s not a rate limit. The index routes and advisories/issues/docker still answer free; the dependent lists don’t. Every write-up recommending them predates this.
├─ libraries.io dependents → HTTP 200 with body {"message":"Disabled for performance reasons"}. A 200 carrying a refusal, so scripts “succeed” and get nothing. The single most-cited endpoint in every English article.
├─ grep.app API → 429 + a bot checkpoint page. Browser UI works, scripts don’t.
├─ GitHub “Used by” page → no API at all, in either REST or GraphQL. Everyone tries it first and burns a day.
├─ GitHub code search API → 401 unauthenticated, and the regex search you perfect in the UI cannot be scripted — the API runs the legacy engine.
├─ npm has no reverse-dependency API, and never did — the browse page is 403 Cloudflare. Biggest ecosystem, zero first-party answer.
├─ deps.dev /v3/…:dependents → 404 version not found. Only the undocumented /v3alpha/ serves it.
├─ PyPI and crates.io have no deprecation concept at allyanked means “don’t resolve to this version”, never “this project is dead.”
├─ GH Archive / ClickHouse carries no CI events — the event enum has no CheckRun/CheckSuite. Run metadata is public; log bodies are 403 forever.
├─ BigQuery github_repos → snapshot ~2023, and one careless regex eats the 1TB free allowance in a single query.
└─ npms.io returns 200 and looks healthy — payload says analyzedAt: 2022-12-03. Frozen for years.

Nobody pays you for the graph. They pay whoever noticed the deadline first.

2 Likes