Disclaimer: This article is for education and for lawful, authorised professional research. Use these methods only where you have a legitimate purpose and a lawful basis, and follow the laws and platform terms that apply to you, including data protection law such as the UK GDPR and EU GDPR. See our Terms of Service.
TL;DR
- GitHub OSINT draws on some of the most useful attribution data on the open web: organisation membership, commit author metadata, timestamps with timezone offsets, forks and stars.
- Almost all of it is set by the account holder and none of it is verified by default. Treat every field as a claim, and look for agreement between independent fields.
- The REST API gives you 60 requests an hour without a token and 5,000 with one. Clone repositories locally and let
git logdo the heavy lifting. - Commit timezones are a weak signal. Working-hour patterns and holiday gaps are more telling than the offset itself.
- UserSearch lets you take a GitHub handle, commit email or linked domain into Username, Email and Domain Intelligence in one place, with the work saved to a Case.
Why Is a GitHub Account Rarely Just a Code Host?
GitHub OSINT is the collection and analysis of public GitHub data, such as organisation membership, commit author metadata, timestamps, forks and stars, to answer an attribution or verification question. A security team rarely sets out to research GitHub. It ends up there. A copycat login page carries a comment pointing at a repository. A vendor's sales deck lists "our open-source SDK". A suspicious browser extension was built from a public template. A new maintainer turns up on a library your product depends on. Each time, the question is the same: which organisation or account is behind this code, and do its claims hold up?
GitHub answers more of that question than most platforms, because git records metadata by design. Every commit carries an author name, an author email, a committer, two timestamps and a timezone offset. Organisations publish member lists. Stars and forks leave a dated social graph. The catch is that almost every one of those fields is typed in by the person you are researching. Git will happily record whatever name and email a machine is configured with.
This guide is written for analysts who research the infrastructure behind a campaign and for teams checking a supplier's open-source footprint. We cover the manual method properly, including the commands we actually use, the limits you will hit, and the places where people over-read the data.
What Does GitHub OSINT Cover, From Organisations to Commit Metadata?
GitHub OSINT is the collection and analysis of publicly available GitHub data to answer an attribution or verification question. It works at three levels.
- Accounts and organisations. A personal account has a login, a numeric ID that never changes, a creation date and optional bio fields (name, company, blog URL, location text, social handles). An organisation is a shared account that owns repositories and lists members. GitHub's own documentation on how organisations work is worth reading once, mainly to understand what is visible to outsiders: only members who have chosen to make their membership public appear on the organisation page.
- Repositories. Code, README files, issues, pull requests, releases, workflow files, forks and stars. Each has timestamps.
- Commits. The richest layer. Author and committer identities, dates with offsets, optional signatures, and the text of the change.
The useful mental model: accounts tell you what someone says about themselves, repositories tell you what they built, and commits tell you how and when they worked. Attribution gets stronger when those three agree.
Why Do Security Teams Keep Ending Up on GitHub?
Two public cases from recent years show why this matters for security teams.
In June 2025, Microsoft Threat Intelligence published its analysis of Jasper Sleet, the North Korean remote IT worker activity. It describes the operators using developer platforms such as GitHub to show a supposed portfolio of previous work, and includes an example GitHub account claiming 13 years of full-stack experience that GitHub later removed. Its recommendations include checking whether an applicant's professional accounts are highly similar to other people's accounts. That is GitHub OSINT, written up by a vendor as a control.
The second case is the xz Utils supply-chain incident discovered in March 2024 (Russ Cox keeps a detailed timeline of the xz incident). A maintainer account called "Jia Tan" spent over two years earning trust in a compression library used across Linux distributions. When the malicious code surfaced, researchers turned to the only evidence available: the commit history. A close reading of the commit timestamps by Rhea Karty and Simon Henniger found that the account mostly committed with a +0800 offset, yet a handful of commits carried +0200 and +0300, including two commits on the same day that were minutes apart but in different offsets. The working-hour and holiday patterns fitted Eastern Europe better than China. That work never named a person, and it did not try to. It showed how far commit metadata can take you, and where it stops.
For a security operations or threat research team, this is day-to-day material: vendor assurance, dependency risk, campaign attribution and incident scoping. If that is your work, our cyber security use cases page shows how teams use UserSearch alongside their existing tooling.
Reading GitHub Like an Analyst: The Manual Method
You need three things: a terminal with git, curl and jq, the GitHub CLI if you prefer it, and a personal access token. You can do a lot without the token, but not for long.
Start with the Organisation and Its Public Members
Begin with the organisation page, because it frames everything else. Note the creation date, the verified domain badge if there is one, the listed website and the public members.
curl -s https://api.github.com/orgs/example-org | jq '{login, id, name, blog, created_at, is_verified, public_repos}'
curl -s "https://api.github.com/orgs/example-org/public_members?per_page=100" | jq -r '.[].login'What each part does:
/orgs/example-orgreturns the organisation record.is_verifiedis true only when the organisation has verified a domain with GitHub, which is one of the few fields GitHub itself checks./public_memberslists only members who have chosen to show their membership. An empty list does not mean an empty team.per_page=100is the maximum page size. Anything larger needs thepageparameter or the CLI's--paginate.
For a vendor check, compare the member list with the engineers the vendor names in its marketing, and compare the organisation's creation date with the "founded" story. A company claiming a decade of open-source work with an organisation created last spring is not proof of anything, but it is a question worth asking.
Pull Account Metadata from the REST API
For each account of interest, the user endpoint gives you the fields that matter most.
curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \
https://api.github.com/users/example_handle \
| jq '{login, id, name, company, blog, twitter_username, created_at, updated_at, public_repos, followers}'idis the stable numeric identifier (the users API reference lists every field returned). Logins can be renamed; the ID cannot. Record it every time.created_atis the account creation date, which the account holder cannot edit.blogandtwitter_usernameare free text, and often the best pivots off the platform.updated_atchanges when the account record changes, not when the person commits, so do not read it as "last active".
Rate limits shape the whole workflow. GitHub's REST API rate limit documentation sets unauthenticated requests at 60 an hour per IP address and authenticated requests at 5,000 an hour, with secondary limits on concurrency and bursts. Sixty requests disappear in minutes once you start paginating member lists. Create a fine-grained token with no repository permissions (public data needs none), and check your remaining budget with:
curl -s -H "Authorization: Bearer $GITHUB_TOKEN" https://api.github.com/rate_limit | jq '.resources.core'If you prefer the CLI, gh api handles authentication and pagination for you:
gh api --paginate /orgs/example-org/repos --jq '.[] | [.name, .created_at, .fork, .stargazers_count] | @tsv'The --paginate flag walks every page, and --jq filters the output without a separate pipe.
Clone the Repository and Read Commit Author Metadata
Do not walk commit history through the API. Clone it. A partial clone skips file contents you do not need and is quick even on large projects:
git clone --filter=blob:none https://github.com/example-org/example-repo.git
cd example-repo
git log --all --format='%H|%an|%ae|%cn|%ce|%aI|%cI|%G?' > commits.txtThe format string, from the git pretty-formats reference:
%Hfull commit hash, so every finding can be cited.%anand%aeauthor name and email: who wrote the change, as configured on their machine.%cnand%cecommitter name and email: who applied it. These differ after rebases, cherry-picks and merges through the web interface.%aIand%cIauthor and committer dates in strict ISO 8601, including the offset.%G?signature status:Gfor a good signature,Nfor none,Ewhen git cannot check it locally.--allincludes every branch, not just the default one. Stale branches often keep older author details.
Then summarise the identities:
git log --all --format='%an <%ae>' | sort | uniq -c | sort -rn
git log --all --format='%cn <%ce>' | sort | uniq -c | sort -rnThree patterns show up again and again. First, GitHub's private email addresses. GitHub's documentation explains the format: accounts created after 18 July 2017 get [email protected], where the number is the stable account ID. That is useful. Even after a rename, the ID ties the commit back to one account. Second, real work or personal email addresses, usually in older commits made before the developer changed their settings. Third, machine defaults such as user@localhost or a hostname-based address, which can hint at a build environment.
A single commit can also be read in patch form, which prints the author line in email format:
curl -s https://github.com/example-org/example-repo/commit/COMMIT_SHA.patch | head -5Here is the friction nobody warns you about. Author details are not verified. Any user can set git config user.email to any address and push, and GitHub will link the commit to whichever account has that address verified. A commit showing a well-known developer's avatar proves only that someone typed their email. Signature status helps. GitHub marks commits as Verified or Unverified, and account holders can switch on vigilant mode so unsigned commits under their name are flagged, as described in GitHub's documentation on verification statuses. Most developers never sign, so an unsigned commit is normal, but a signed commit from a key tied to the account is strong evidence.
Commit Timezones: A Weak Signal, Handled Carefully
Each timestamp carries the offset of the machine that made the commit. Count them:
git log --all --author='example_handle' --format='%ad' --date=format:'%z' | sort | uniq -c | sort -rn--date=format: prints the date in the commit's own recorded offset, and %z gives just the offset. Use format-local: instead if you want everything converted to your own clock.
Now the caveats. An offset is a laptop setting, not a place. It changes with travel, with daylight saving (so +0100 and +0000 from the same UK developer is normal), with CI systems that run in UTC, and with people who deliberately set TZ before committing. The xz case showed both sides: the headline offset was probably chosen, and the slips were what mattered.
The sturdier approach is to look at hours of activity in UTC across hundreds of commits:
git log --all --author='example_handle' --format='%at' \
| while read t; do date -u -d "@$t" +%H; done | sort | uniq -c%at is the author time as a Unix timestamp, and date -u converts it to a UTC hour (on macOS, use date -u -r "$t" +%H). A block of eight to ten active hours, a gap that looks like sleep, and quiet days that match a particular country's public holidays are far harder to fake than an offset. Even then, write it up as "consistent with" a region, never as a finding on its own. Shared accounts, shift work and automated commits all distort the picture.
Forks, Stars and the Social Graph
Stars and forks tell you who noticed a repository and when. The stargazers endpoint returns dates if you ask for them:
gh api --paginate -H "Accept: application/vnd.github.star+json" \
/repos/example-org/example-repo/stargazers --jq '.[] | [.starred_at, .user.login] | @tsv'The custom Accept header adds starred_at to each record. Plot those dates. Organic interest arrives in bursts after a release or a mention on a news site. Hundreds of stars in a single hour, from accounts created the same week with no repositories of their own, suggest the count was bought. That matters when a vendor quotes star counts as proof of adoption.
Forks work the same way. A campaign that reuses a public template often leaves a fork, or a copy with the original commit history intact, and the earliest commits in that copy may carry the original author rather than the operator. Do not attribute a fork to the upstream author. Check where the history diverges and whose commits come after that point.
The Public Events Feed and Its 30-Day Window
The events API shows recent public activity: pushes, forks, stars, issue comments. It has hard limits, set out in GitHub's events documentation: only the last 30 days, up to 300 events, and a latency of anywhere from 30 seconds to six hours.
gh api /users/example_handle/events/public --jq '.[] | [.created_at, .type, .repo.name] | @tsv'For anything older, the GH Archive project has recorded the public GitHub event stream hourly since 2011 and publishes it as downloadable files and a public BigQuery dataset. It is how you recover activity on repositories that were later deleted or made private. Expect to write SQL, and watch your BigQuery costs on wide date ranges.
If you would rather use a packaged tool for account and repository lookups, Bellingcat has published Octosuite, a Python terminal toolkit for GitHub data analysis (version 5.1.0 on PyPI as of September 2026). Install it with pip install octosuite; it uses the same public API, so the same rate limits apply and a token is still worth having.
Taking the Handle Beyond GitHub with UserSearch
GitHub research produces identifiers faster than you can follow them: a handle, two or three commit emails, a blog domain, a social handle in the bio, sometimes an IP address or hostname in a configuration file. Following each by hand means a new browser tab, a new tool and a new set of notes. That is where manual work usually breaks down. Leads get dropped, and nobody can reconstruct later why a link was made.
UserSearch 2.0 puts those pivots in one place:
- Username Intelligence checks the GitHub handle across other platforms, which is how you find accounts using the same handle on package registries, forums and social networks.
- Email Intelligence takes any non-noreply commit email further.
- Domain Intelligence covers the blog or company domain from the account bio, including ownership history and favicon checks.
- IP Intelligence and Cyber Intelligence (Shodan Modules) cover hosts that turn up in code or workflow files.
- OneScan runs one identifier across several selected sources and merges the results with source attribution. The Credit cost is shown before you run it.
Work in Forensic Mode and every search and bookmark is kept in a Case, so the chain from repository to conclusion stays reviewable. When the evidence is a README or an account page that may change, capture it with Forensic Capture, our free browser extension, which records full-page captures with SHA-256 fingerprints and independent timestamps. SargeBot, the in-platform research assistant, can help plan the entity searches and draft a PDF report, and you verify what it produces before relying on it. Everything returned is a lead to corroborate, and coverage depends on each third-party source. New to the platform? Start with our beginner's guide to UserSearch OSINT.
Four Research Patterns for Campaign and Vendor Work
Scenario One: Checking a Supplier's Open-Source Claims
Context. A procurement team is assessing Example Analytics Ltd, which says its data connector is "open source, maintained by our core team since 2018" and points to github.com/example-org.
Actions. The analyst pulls the organisation record and finds it was created in 2018, with is_verified true for example.com. So far, consistent. The public members list shows four accounts. Cloning the connector repository and summarising authors shows 1,900 commits, of which 1,650 come from two accounts whose older commits use @example.com addresses. The other 250 come from a contractor account using a noreply address, active only in the last eight months. The UTC activity histogram for the two core authors shows a steady European working day. The analyst checks both core handles in Username Intelligence and finds matching conference talk pages and package registry accounts under the same names.
Outcome. The claim holds up. The one follow-up question for the vendor is about the contractor, who now writes most new code, and what access that person has to release signing. The Case holds the command output, bookmarks and captures for the procurement file.
Scenario Two: Attributing the Code Behind a Campaign
Context. A threat research team is reviewing a set of copycat login pages hosted on example.net. The page source includes a comment with a repository path, example-kit/landing-templates.
Actions. The repository was deleted, but GH Archive shows push events to it over five months, all from one account, @example_handle, created two weeks before the first push. The account still exists and has two public repositories. Cloning both yields 140 commits. Author emails are noreply addresses with the same numeric ID, so all activity belongs to one account despite a rename midway. Offsets are mostly +0000, which is uninformative, but the UTC hour histogram shows activity clustered between 06:00 and 14:00, and nothing during a particular national holiday week. The templates post their form data to a second domain, example.org. The team takes the handle and both domains into UserSearch, where Domain Intelligence shows the two domains share a distinctive favicon and were registered in the same week, and Username Intelligence finds the same handle on a package registry publishing an earlier version of the templates.
Outcome. The team records a medium-confidence link between the kit, the package registry account and the second domain, a low-confidence working-hours region assessment, and no named individual. The report states which fields were self-asserted. Their findings go to the hosting provider's abuse contact and into the team's internal threat library.
Vetting a Dependency Before You Adopt It
The xz lesson for engineering teams: before adopting a small library or a third-party GitHub Action, look at maintainer continuity. Who has release rights now? When did they join? Did commit volume and signing behaviour change when a new maintainer arrived? A quick git shortlog -sne --since="18 months ago" shows who has been writing the code recently, with commit counts and emails.
Linking a Cluster of Batch-Created Accounts
Accounts created in batches share tells: creation dates within hours of each other, identical README templates, the same small set of starred repositories, and forks of the same upstream at the same commit. Pull created_at for every stargazer or contributor on a suspect repository and sort. Clusters jump out. This is the fastest way we know to show that a "community" around a repository is one operator.
Staying Inside GitHub's Rules and Data Protection Law
GitHub is public, but that does not make everything on it fair game. Three points we hold to.
Follow the platform's terms. GitHub's Acceptable Use Policies separate API access from automated collection of the website and allow the latter for research only where resulting publications are open access, and for archiving. They prohibit using collected information for spam or selling personal information, for example to recruiters. Use the API with a token, respect rate-limit headers, and do not run crawlers against the web interface.
Commit emails are personal data. A name and email pulled from a commit falls under the UK GDPR and EU GDPR like any other personal data. Record your purpose and lawful basis before you start, collect only what the question needs, and do not contact developers using addresses taken from commit history.
Write findings at the right confidence. Self-asserted fields, offsets and working hours are indicators. A report that says "the account's activity is consistent with a UTC+2 working day" is defensible. One that names a person based on a timezone is not. Keep the raw output and hashes so another analyst can check your work.
Frequently Asked Questions About GitHub OSINT
What is GitHub OSINT?
GitHub OSINT is the collection and analysis of publicly available GitHub data to answer an attribution or verification question. It works at three levels: accounts and organisations, which show what someone says about themselves; repositories, which show what they built; and commits, which show how and when they worked. Attribution is strongest when all three agree.
What metadata does a git commit contain?
Every git commit carries an author name and email, a committer name and email, two timestamps with a timezone offset, an optional signature and the text of the change. These fields are set on the committer's machine and are not verified by default, so GitHub OSINT treats each one as a claim and looks for agreement between independent fields.
What are the GitHub API rate limits?
GitHub's REST API allows 60 unauthenticated requests an hour per IP address and 5,000 an hour with a token, with secondary limits on concurrency and bursts. A fine-grained personal access token with no repository permissions is enough for public data. Cloning repositories locally and reading history with git log avoids spending API requests on commits.
How reliable are commit timezones in GitHub OSINT?
Commit timezone offsets are a weak signal, because an offset is a machine setting that changes with travel, daylight saving, CI systems running in UTC and deliberate choices. Working-hour patterns in UTC across hundreds of commits, and quiet days matching public holidays, are harder to fake, but they should still be written up as consistent with a region, never as a finding.
How can you tell if GitHub stars were bought?
Request stargazer records with the star+json Accept header to get the date of each star, then plot them. Organic interest arrives in bursts after a release or a news mention. Hundreds of stars within one hour from accounts created the same week with no repositories of their own suggest the count was bought, which matters when a vendor cites stars as adoption.
How do you see older or deleted GitHub activity?
GitHub's public events API only covers the last 30 days and up to 300 events. For older activity, the GH Archive project has recorded the public GitHub event stream hourly since 2011 and publishes it as downloadable files and a public BigQuery dataset, which can recover activity on repositories later deleted or made private. Expect to write SQL.
Turning Commit Metadata into Evidence You Can Defend
GitHub rewards patience. The organisation record frames the question, the API gives you stable IDs and dates, a local clone gives you every author line and timestamp, and the social graph shows whether interest is real. The skill is in weighing it: verified domains and signatures count for a lot, self-typed fields for little, and timezones only when hundreds of commits agree. For the wider method of following one handle across platforms, our username OSINT guide picks up where this article ends.
Stop guessing. Start researching with UserSearch. Take the handle, the commit emails and the linked domains from a GitHub review into one UserSearch account, reach 100+ third-party data sources without separate subscriptions, run them together with OneScan, and keep every step in a Case you can turn into a report. When the vendor or the incident lead asks how you reached a conclusion, you will have the answer ready.