HubSpot is the CRM of choice for most B2B SaaS companies selling to technical buyers. GitHub is where those technical buyers reveal their intent. Connecting the two — pushing GitHub lead signals directly into HubSpot as contacts — closes the loop between developer activity and your sales workflow. This guide covers how to do it using the HubSpot API directly, and how GitLeads automates the entire process.
What GitHub Signals Should You Push to HubSpot
Before connecting GitHub to HubSpot, define which events you want to treat as lead creation triggers. Not every GitHub event warrants a HubSpot contact record — being selective keeps your CRM clean and your sales team focused on high-intent signals.
- New stargazers on your own repo: developers actively bookmarking your product — high intent, warm lead
- New stargazers on competitor repos: developers evaluating your competitive landscape — medium-high intent, often best reached with a relevant comparison message
- GitHub Issues or PRs mentioning your category keywords: developers stating a problem you solve — very high intent, best leads in the pipeline
- GitHub Issues mentioning competitor names: developers expressing dissatisfaction or evaluating alternatives — high intent, respond with positioning against their current tool
- Code commits referencing your SDK or integration: developers already implementing something related to your product — may already be customers, cross-check CRM before outreach
Method 1: Direct HubSpot API Integration
If you are building a custom pipeline, here is how to create or update a HubSpot contact from a GitHub profile using the HubSpot Contacts API v3.
import axios from 'axios';
interface GitHubProfile {
login: string;
name: string | null;
email: string | null;
company: string | null;
bio: string | null;
location: string | null;
html_url: string;
followers: number;
public_repos: number;
}
interface GitHubSignal {
profile: GitHubProfile;
signalType: 'stargazer' | 'keyword_mention';
sourceRepo?: string;
keyword?: string;
signalContext?: string;
}
async function pushToHubSpot(signal: GitHubSignal, hsToken: string) {
const { profile, signalType, sourceRepo, keyword, signalContext } = signal;
const properties: Record<string, string> = {
github_username: profile.login,
github_profile_url: profile.html_url,
github_signal_type: signalType,
github_followers: String(profile.followers),
github_public_repos: String(profile.public_repos),
lead_source: 'GitHub Signal',
};
if (profile.name) {
const parts = profile.name.split(' ');
properties.firstname = parts[0];
if (parts.length > 1) properties.lastname = parts.slice(1).join(' ');
}
if (profile.email) properties.email = profile.email;
if (profile.company) properties.company = profile.company.replace(/^@/, '');
if (profile.location) properties.city = profile.location;
if (sourceRepo) properties.github_source_repo = sourceRepo;
if (keyword) properties.github_signal_keyword = keyword;
if (signalContext) properties.github_signal_context = signalContext.slice(0, 500);
// Use upsert to avoid duplicates — search by email if available, else by github_username
const searchField = profile.email ? 'email' : 'github_username';
const searchValue = profile.email ? profile.email : profile.login;
try {
// Try to find existing contact
const search = await axios.post(
'https://api.hubapi.com/crm/v3/objects/contacts/search',
{
filterGroups: [{
filters: [{ propertyName: searchField, operator: 'EQ', value: searchValue }]
}],
limit: 1,
},
{ headers: { Authorization: `Bearer ${hsToken}`, 'Content-Type': 'application/json' } }
);
if (search.data.total > 0) {
// Update existing contact
const contactId = search.data.results[0].id;
await axios.patch(
`https://api.hubapi.com/crm/v3/objects/contacts/${contactId}`,
{ properties },
{ headers: { Authorization: `Bearer ${hsToken}`, 'Content-Type': 'application/json' } }
);
return { action: 'updated', contactId };
} else {
// Create new contact
const create = await axios.post(
'https://api.hubapi.com/crm/v3/objects/contacts',
{ properties },
{ headers: { Authorization: `Bearer ${hsToken}`, 'Content-Type': 'application/json' } }
);
return { action: 'created', contactId: create.data.id };
}
} catch (error: any) {
if (error.response?.status === 409) {
// Contact exists with that email, update by email
console.warn('Contact conflict, retrying as update');
}
throw error;
}
}Setting Up Custom HubSpot Properties for GitHub Signals
Before pushing GitHub leads to HubSpot, create the custom contact properties your pipeline will populate. In HubSpot, go to Settings → Properties → Contact Properties and create the following:
- github_username (Single-line text): the developer's GitHub login — use this as your deduplication key when email is not available
- github_profile_url (Single-line text): link directly to their GitHub profile for sales team research
- github_signal_type (Single-line text or Dropdown): stargazer, keyword_mention, fork, issue_author — used to segment contacts by signal quality
- github_source_repo (Single-line text): which repository triggered the signal — critical for routing to the right sales rep or campaign
- github_signal_keyword (Single-line text): the keyword that matched, if applicable
- github_signal_context (Multi-line text): the issue title, PR title, or commit message that contained the keyword — essential context for personalized outreach
- github_followers (Number): follower count as a proxy for developer influence and reach
- github_public_repos (Number): public repo count as a proxy for seniority and engagement level
HubSpot Workflow Automation for GitHub Leads
Once GitHub leads are in HubSpot as contacts with signal properties populated, use HubSpot Workflows to automate your follow-up. Here are three high-value workflow patterns:
Workflow 1: Assign to Sales Rep by Signal Type
Trigger: Contact is created with Lead Source = "GitHub Signal". Condition: if github_signal_type = "stargazer" AND github_source_repo = "your-repo", assign to the Account Executive responsible for warm inbound. If github_signal_type = "keyword_mention", assign to the SDR team for active outreach. If github_signal_type = "stargazer" AND github_source_repo = "competitor-repo", assign to the competitive sales rep.
Workflow 2: Enroll in Email Sequence by Signal Context
Trigger: Contact property github_signal_type is set. Use HubSpot Sequences (Sales Hub) to enroll the contact in a personalized email sequence. The first email in the sequence should reference the specific signal — the repo they starred or the keyword context. This level of personalization consistently produces 3–5x higher reply rates than generic developer outreach.
Workflow 3: Create Deal on High-Intent Signal
Trigger: Contact is created with github_signal_type = "keyword_mention" AND github_followers > 100. Action: Create an associated Deal in the pipeline at stage "GitHub Signal — High Intent", set deal amount to your average contract value, assign to account executive, and send Slack notification to the sales channel. This ensures your hottest leads get immediate attention without manual triage.
Method 2: Push GitHub Leads to HubSpot with GitLeads
Building and maintaining a custom GitHub-to-HubSpot pipeline requires managing GitHub API rate limits, handling deduplication logic, maintaining the HubSpot OAuth token, and keeping the integration running reliably. GitLeads provides a native HubSpot integration that handles all of this.
To connect GitLeads to HubSpot: navigate to the Integrations section in your GitLeads dashboard, click Connect HubSpot, and authorize via OAuth. GitLeads will automatically create custom properties in HubSpot for all signal fields, and push new leads as contacts — with deduplication by email and GitHub username — within minutes of capturing a signal.
- No custom property setup required — GitLeads creates all HubSpot properties on first connect
- Deduplication by email and GitHub username — existing contacts are updated rather than duplicated
- Signal context is preserved — the exact issue title, PR, or keyword match is stored in the contact record
- Real-time sync — new signals appear in HubSpot within 5 minutes of being captured on GitHub
- Works on all GitLeads plans including Free (50 leads/month)
Common Pitfalls When Syncing GitHub Leads to HubSpot
- Not deduplicating: if a developer has starred multiple tracked repos, you will create multiple contact records unless you dedup by GitHub username or email
- Missing email on ~70% of profiles: GitHub email is only public when users have "Keep my email address private" turned off. Always store github_username as a fallback deduplication key
- Overwriting existing contact data: if a contact already exists in HubSpot (e.g., they signed up for a trial), patching with GitHub data can overwrite company or name fields. Use conditional updates — only write fields that are currently empty
- Not logging signal history: a contact may trigger multiple signals over time. Use HubSpot timeline events or a custom multi-value property to log each signal rather than overwriting the last one
- Forgetting GDPR lawful basis: if you are in the EU or selling to EU developers, document your lawful basis for processing GitHub data. GitLeads includes GDPR compliance guidance in the product documentation