← all tools
Data & sourcing

GitHub

Judge an engineer by their code, not by their CV.

Cost per callfree
Your own keyoptional
CategoryData & sourcing

GitHub is where developers leave evidence. Your agent searches by language, location and project, reads what someone has actually shipped, and scores it against the brief. Sometimes the email is right there on the profile. Often it is not, and enrichment picks that up.

GitHub runs before enrichment. It finds and qualifies the engineer; the waterfall finds the email.

things you can ask for

  • Find Rust engineers in Berlin who contribute to open source, then get emails through Prospeo
  • Read this job brief url and find contributors whose work actually matches it
  • Who are the top contributors on this project? Enrich them with FullEnrich
  • Assess this candidate's real skill level and write the summary into Vincere

works well with GitHub

  • Prospeo

    GitHub rarely carries the email, this finds it

  • FullEnrich

    the waterfall for developers with private commit addresses

  • BuiltWith

    find the companies running the stack these engineers know

  • Vincere

    file the technical assessment on the candidate

Skills that use GitHub

A skill is a whole pipeline your agent already knows. These ones call GitHub as part of the run.

signal5 steps

Funded round → 30-day talent sweep

PredictLeads signals fresh funding. Build a candidate pool via GitHub + Apollo weeks before the JDs go live.

coming soon
signal5 steps

GitHub signal → senior eng pipeline

Top GitHub contributors at top companies. Enrich via Prospeo for a never-resting senior engineering pipeline.

coming soon

What your agent reads before it touches GitHub

Every tool ships with a written playbook, and the agent loads it before the first call. Auth, rate limits, what each call costs, which actions need your approval, and the mistakes worth avoiding. It is the difference between an agent that knows the tool and one that guesses at it.

Read the GitHub docs →
Show the raw playbookwritten for the agent

HOW in lib/github.py. Sourcing-relevant subset only; index: reference/docs/github/raw/endpoints.md.

Auth & config

  • REST: https://api.github.com · GraphQL: https://api.github.com/graphql.
  • Auth: PAT (fine-grained or classic) → Authorization: Bearer <token> (env GITHUB_TOKEN; never hardcode) + X-GitHub-Api-Version: 2022-11-28.
  • Rate limits: REST 5,000/hr · Search 30 req/min, 1,000-result cap · GraphQL ~5,000 points/hr. Search pagination: page/per_page (≤100); iter_search_users respects the cap. Adapter auto-sleeps on 403/429 rate-limit + transient 5xx.

Operations

Discovery: search_users/iter_search_users, search_repositories, search_code. Profile + skill signal: get_user, list_user_repos, get_repo_contributors, list_user_followers. Ops: get_rate_limit. graphql(query, variables) — generic passthrough (one endpoint; you specify the fields) for rich single-call dossiers: profile + socialAccounts (linked LinkedIn/Twitter) + top languages + pinned repos + contributionsCollection.

Pulling contact details when they ARE listed/derivable

GitHub does expose contact info three ways — try these before falling back to the waterfall:

  1. Public profile email + site + twitter: get_user(username)email (if the dev set one public), blog, twitter_username, bio.
  2. LinkedIn / social links (the icons under the avatar): get_user_social_accounts(username)[{provider, url}] (linkedin, mastodon, …). Also in GraphQL user.socialAccounts.
  3. Commit-email harvest (when the profile email is private): find_user_emails(username) walks their recent owned repos' commits and returns real emails, dropping *@users.noreply.github.com proxies. (Primitives: list_repo_commits(author=…), list_user_events.)

⚠️ The recruiter reality

  • Even with the above, many devs have no usable email on GitHub (private email + noreply commits). So GitHub is still primarily discovery + skill signal → if 1–3 come up empty, hand name + company + GitHub/LinkedIn to the enrichment waterfall. GitHub slots in before enrichment.
  • Run narrow search queries (rate cap; find_user_emails is bounded by max_repos). Keep outreach human-paced (ToS).

Handoff (IT candidate sourcing)

search_users/get_repo_contributors by language+location → get_user+list_user_repos (skill signal) → score vs the JD (agent, free) → enrich email (waterfall) → validate → ATS (Vincere/Bullhorn) → sequencer (SourceWhale/Lemlist/HeyReach). The engineer-sourcing front-end for the "source & shortlist" recipe.

Callable surface — lib/github.py

Import: from lib.github import GitHub → instantiate GitHub() (reads key from env). Base: https://api.github.com. Generic passthrough: request(method, path, *, params, json).

  • find_user_emails(username: str, *, max_repos: int = 10) -> list[str] — Harvest candidate emails from a user's public commits (when the profile email is private).
  • get_rate_limit() -> Any — GET /rate_limit — remaining REST/Search/GraphQL budget (safe read-only pilot).
  • get_repo_contributors(owner: str, repo: str, **params) -> Any — GET /repos/{owner}/{repo}/contributors — source the people who build a given project.
  • get_user(username: str) -> Any — GET /users/{username} — profile: name, company, location, blog, email (if public), hireable, counts.
  • get_user_social_accounts(username: str) -> Any — GET /users/{username}/social_accounts — the linked social accounts shown on the profile
  • graphql(query: str, variables: dict | None = None) -> Any — POST /graphql — run any GraphQL query/mutation. Best for rich single-call candidate dossiers
  • iter_search_users(q: str, *, per_page: int = 100, max_results: int = 1000, **params) -> Iterator[dict] — Paginate /search/users up to GitHub's 1,000-result cap.
  • list_repo_commits(owner: str, repo: str, *, author: str | None = None, per_page: int = 100, **params) -> Any — GET /repos/{owner}/{repo}/commits — commits (filter by author=username). Each carries
  • list_user_events(username: str, **params) -> Any — GET /users/{username}/events/public — public activity (PushEvents carry commit author emails).
  • list_user_followers(username: str, **params) -> Any — GET /users/{username}/followers — network expansion.
  • list_user_repos(username: str, *, sort: str = 'pushed', per_page: int = 100, **params) -> Any — GET /users/{username}/repos — skill signal: languages, stars, recency. sort: pushed|updated|created|full_name.
  • search_code(q: str, *, per_page: int = 30, page: int = 1, **params) -> Any — GET /search/code — find code (derive authors using a specific tech). Stricter rate limit.
  • search_repositories(q: str, *, sort: str | None = None, order: str | None = None, per_page: int = 30, page: int = 1) -> Any — GET /search/repositories — q e.g. 'language:rust stars:>500 topic:cli'. sort: stars|forks|updated.
  • search_users(q: str, *, sort: str | None = None, order: str | None = None, per_page: int = 30, page: int = 1) -> Any — GET /search/users — find developers. q e.g. 'location:Berlin language:Go followers:>50 repos:>10'.