How I built an AI agent to extract premium LinkedIn members using Agno and ConnectSafely.ai - with real code, real numbers, and lessons learned the hard way
Built an AI agent that extracts LinkedIn Premium members from groups in ~90 seconds. No scraping, no browser automation, no account bans. Using Agno (agent framework) + ConnectSafely.ai (compliant LinkedIn API).
Full code on GitHub π
The Problem: I Wasted 6 Hours Clicking LinkedIn Profiles
You know that feeling when you need to find decision-makers in a LinkedIn group, and you end up clicking through profiles one by one like itβs 2005?
Yeah. That was me last month.
I nβ¦
How I built an AI agent to extract premium LinkedIn members using Agno and ConnectSafely.ai - with real code, real numbers, and lessons learned the hard way
Built an AI agent that extracts LinkedIn Premium members from groups in ~90 seconds. No scraping, no browser automation, no account bans. Using Agno (agent framework) + ConnectSafely.ai (compliant LinkedIn API).
Full code on GitHub π
The Problem: I Wasted 6 Hours Clicking LinkedIn Profiles
You know that feeling when you need to find decision-makers in a LinkedIn group, and you end up clicking through profiles one by one like itβs 2005?
Yeah. That was me last month.
I needed to extract all Premium/Verified members from a 5,000-person LinkedIn group. For context, these are usually:
- Founders with actual budgets
- VPs who can say "yes" to deals
- Verified professionals investing in their careers
Manual approach: 6 hours of clicking β 47 contacts β severe mouse fatigue β existential crisis about my career choices
What I needed:
- Auto-fetch thousands of members β
- Identify Premium/Verified badges β
- Filter out noise β
- Export to Google Sheets β
- Not get my LinkedIn account banned β β β
Spoiler: I built it. Let me show you how.
Why Not Just Scrape It?
Short answer: Because I like my LinkedIn account.
Long answer: I tried the "scraping route" first. Hereβs what happened:
Scraping is a technical dead end for LinkedIn automation:
- Selectors break with every UI update
- Rate limiting is aggressive and unpredictable
- Account bans are permanent
- Legal gray area (or just illegal)
There had to be a better way.
The Stack: Agno + ConnectSafely.ai
After burning a week on dead ends, I landed on this architecture:
π€ Agno: AI agent framework π ConnectSafely.ai: LinkedIn automation API (compliant, stable, actually works)
π Google Sheets API: For export and CRM integration
Why Agno?
Most agent frameworks want you to build elaborate multi-agent systems with "Product Manager Agent," "Engineer Agent," "QA Agent" all chatting via prompts.
Cool for demos. Terrible for production.
Agno is different:
- One agent that orchestrates tools
- Explicit workflows (no emergent behavior)
- Tool-first design (agent is the glue, not the worker)
Think of it as hiring one senior dev instead of managing a committee of junior agents.
Why ConnectSafely.ai?
Because I donβt want to:
- Reverse-engineer LinkedInβs private APIs
- Manage session cookies and CSRF tokens
- Handle rate limiting logic
- Debug why profiles stopped loading
- Get banned
ConnectSafely gives me:
# This just worksβ’
members = connectsafely.fetch_group_members(group_id)
No scraping. No browser automation. Just data.
Architecture: Keep It Stupid Simple
Hereβs the folder structure:
agentic/agno/
βββ app.py # Entry point
βββ agents/
β βββ agent.py # Agno agent config
β
βββ config/
β βββ agent_setup.py
β βββ workflows.py
β βββ constants.py
β
βββ tools/
β βββ linkedin/
β β βββ fetch_group_members_tool.py
β β βββ filter_premium_tool.py
β β βββ workflow_tool.py
β β
β βββ googlesheet/
β βββ sheets_tool.py
β βββ export_tool.py
β
βββ README.md
Design principle: Each tool does ONE thing. No god classes. No hidden coupling.
The Agent: Configured Once, Used Forever
Hereβs the agent setup:
from agno import Agent
from agno.llms import Gemini
agent = Agent(
name="LinkedInPremiumExtractor",
description=(
"You extract LinkedIn group members via ConnectSafely.ai, "
"filter for Premium/Verified users, and export to Google Sheets. "
"You are precise, efficient, and never scrape."
),
llm=Gemini(model="gemini-3-pro-preview"),
tools=[
fetch_group_members_tool,
filter_premium_verified_tool,
export_to_sheets_tool,
],
show_tool_calls=True, # Debug mode
markdown=True,
)
What makes this work:
- Agent knows exactly what success looks like
- Tools are explicitly listed (no discovery overhead)
- LLM is used for orchestration only (not data processing)
- One agent, zero delegation chaos
Tool #1: Fetch Members (The Right Way)
from connectsafely import ConnectSafelyClient
def fetch_group_members_tool(group_id: str) -> list:
"""
Fetch all members from a LinkedIn group.
Handles pagination automatically.
"""
client = ConnectSafelyClient(api_key=os.getenv("CONNECTSAFELY_API_KEY"))
response = client.groups.fetch_members(
group_id=group_id,
pagination=True,
include_badges=True, # We need Premium/Verified status
timeout=30
)
return response.members
What this handles automatically:
- β Pagination for group members
- β Rate limiting (respects LinkedInβs limits)
- β Retries on network failures
- β Session management
What I donβt have to worry about:
- β CSS selectors
- β Browser fingerprinting
- β Cookie management
- β Getting banned
Tool #2: Filter Premium Members
This tool isolates the filtering logic:
def filter_premium_verified_tool(members: list) -> list:
"""
Filter members to only Premium/Verified accounts.
"""
filtered = []
for member in members:
is_premium = member.get("isPremium", False)
is_verified = member.get("isVerified", False)
has_badge = "premium" in member.get("badges", [])
if is_premium or is_verified or has_badge:
filtered.append(member)
return filtered
Why isolate this?
- Easy to test in isolation
- Reusable across workflows
- Simple to extend (add location, industry filters)
- Clear single responsibility
Want to filter by geography?
if member.get("location") == "San Francisco, CA":
filtered.append(member)
Done.
Tool #3: Export to Google Sheets
Final step: getting data into a usable format.
from googleapiclient.discovery import build
def export_to_sheets_tool(members: list, sheet_id: str):
"""
Export filtered members to Google Sheets.
"""
service = build('sheets', 'v4', credentials=creds)
# Prepare data
rows = [["Name", "Headline", "Profile URL", "Status", "Company"]]
for m in members:
rows.append([
m.get("name", "N/A"),
m.get("headline", "N/A"),
m.get("profileUrl", "N/A"),
"Premium" if m.get("isPremium") else "Verified",
m.get("company", "N/A"),
])
# Write to sheet
service.spreadsheets().values().update(
spreadsheetId=sheet_id,
range="Sheet1!A1",
valueInputOption="RAW",
body={"values": rows}
).execute()
return {"status": "success", "rows": len(rows)}
Output is CRM-ready:
- Clean column headers
- No manual formatting needed
- Direct import to HubSpot/Salesforce
The Workflow: No Black Box Magic
Hereβs the beautiful partβthe workflow is explicit:
def run_extraction_workflow(group_id: str, sheet_id: str):
# Step 1: Fetch all members
print("Fetching members...")
members = fetch_group_members_tool(group_id)
print(f"β
Fetched {len(members)} members")
# Step 2: Filter Premium/Verified
print("Filtering for Premium/Verified...")
premium = filter_premium_verified_tool(members)
print(f"β
Found {len(premium)} Premium/Verified members")
# Step 3: Export to Sheets
print("Exporting to Google Sheets...")
result = export_to_sheets_tool(premium, sheet_id)
print(f"β
Exported {result['rows']} rows")
return result
No hidden agent chatter.
No task confusion.
Just execution.
You can read the code and know exactly what happens. Try doing that with 5 LLM agents delegating tasks via prompts.
Real Numbers: Production Performance
From actual production runs:
βββββββββββββββββββββββββββββββββββββββ
β Performance Metrics β
βββββββββββββββββββββββββββββββββββββββ€
β Extraction Speed: ~50 members/sec β
β Accuracy: 98% detection β
β Max Group Size: 10,000+ members β
β API Uptime: 99.9% β
βββββββββββββββββββββββββββββββββββββββ
Example run:
$ uv run streamlit run App.py
Fetching members...
β
Fetched 3,847 members (76s)
Filtering for Premium/Verified...
β
Found 412 Premium/Verified members (10.7% conversion)
Exporting to Google Sheets...
β
Exported 413 rows (4s)
Total time: 80 seconds
Thatβs 412 high-quality leads in under 90 seconds.
Manual approach? 6-8 hours of soul-crushing clicking.
Lessons Learned (The Hard Way)
β What Worked
1. Single Agent > Agent Swarm
One agent with good tools beats 5 agents arguing via prompts.
2. APIs > Scraping (Always)
Scraping is technical debt from day one. APIs scale.
3. Tool Isolation = Easy Debugging
When tools do one thing, debugging is trivial. When theyβre coupled, itβs hell.
π§ What Was Painful
1. Google OAuth Dance
Refresh tokens, scopes, service accounts... took longer than the actual agent code.
2. Agent Prompts Matter More Than You Think
Vague description = vague results. Be surgical with your agentβs purpose.
3. Large Groups Need Chunking
5k members? Youβll hit memory limits. Process in batches.
π Bugs I Hit (So You Donβt Have To)
# Bug 1: Forgot to handle missing badges
# Fix: Use .get() with defaults
badges = member.get("badges", [])
# Bug 2: Rate limits on Sheets API
# Fix: Batch updates, don't write row-by-row
service.spreadsheets().values().batchUpdate(...)
# Bug 3: Agent hallucinating tool parameters
# Fix: Use Pydantic models for strict typing
class FetchMembersInput(BaseModel):
group_id: str = Field(..., description="LinkedIn group ID")
Whatβs Next: Future Roadmap
Planning these upgrades:
# π CRM Sync
def sync_to_hubspot(members: list):
# Direct HubSpot/Salesforce integration
pass
# π Real-time Monitoring
def watch_new_premium_members(group_id: str):
# Webhook alerts for new Premium joins
pass
# π¦ Batch Processing
def process_multiple_groups(group_ids: list):
# Analyze 10+ groups in parallel
pass
# π§ AI Lead Scoring
def score_leads(members: list):
# Rank prospects by profile content
pass
Real-World Use Cases
This is already being used by:
- Sales teams: Targeting enterprise decision-makers
- Recruiters: Sourcing verified engineers/designers
- Marketers: Building ABM target lists
- Founders: Partnership research and networking
- Event organizers: Finding keynote speakers
Try It Yourself
Want to build your own version?
Quick Start
# Clone the repo
git clone https://github.com/ConnectSafelyAI/agentic-framework-examples
cd extract-linkedin-premium-users-from-linkedin-groups/agentic/agno
# Install dependencies
uv sync
# Set up environment
cp .env.example .env
# Add your ConnectSafely API key
# Run it
uv run streamlit run App.py
Resources & Support
Follow us for more automation tips:
LinkedIn β’ YouTube β’ Instagram β’ Facebook β’ X β’ Bluesky β’ Mastodon
Key Takeaways
- Agent β Complexity: One smart agent beats agent sprawl
- Tools > Prompts: Give your agent good tools, not good vibes
- APIs > Scraping: Always. No exceptions.
- Compliance Matters: Build systems you can actually run in production
Building something cool with AI agents? Drop it in the commentsβIβd love to see what youβre working on!
Hit me up if you have questions or want to chat about agent architectures. Always down to nerd out about this stuff. π€