Skip to main content

On This Page

Automating Freelance Lead Generation with Claude AI and GitHub Actions

3 min read
Share

These articles are AI-generated summaries. Please check the original sources for full details.

How I Built a Job Finder Agent with Claude AI, GitHub Actions, and Notion

Engineer Omer Farooq built a functional job-finding agent in under 150 lines of Python. The system handles 120 listings per week, filtering for high-relevance matches via Claude-3.5-Sonnet and scheduling runs via GitHub Actions.

Why This Matters

The project demonstrates that complex frameworks like LangChain or CrewAI are often unnecessary for high-signal engineering tasks. By focusing on a simple fetch-score-store pipeline, developers can achieve 90% accuracy in relevance filtering while maintaining negligible operational overhead. It highlights the shift from pattern-matching regex to qualitative reasoning using LLMs for unstructured data processing.

Key Insights

  • Structured Scraping: Upwork RSS feeds provide structured XML without authentication, while Apify’s LinkedIn Scraper handles anti-bot measures for approximately $2/month.
  • Prompt Optimization: Instructing Claude to return ‘JSON only — no preamble’ prevents parsing errors in json.loads() and ensures compatibility with Notion properties.
  • Cost Scaling: Limiting Claude-3.5-Sonnet to 300 max_tokens keeps API costs at ~$0.04 per run while forcing concise, high-signal reasoning.
  • Serverless Execution: GitHub Actions provides a free runtime and scheduler (cron: 0 2 * * *), eliminating the need for dedicated server maintenance.
  • Qualitative Filtering: Unlike keyword-based blocklists, LLM-based scoring correctly identifies niche skills and budget fits that regex would miss.

Working Examples

Function to score job listings using Claude API with structured JSON output.

def score_listing(listing: JobListing) -> dict:
    prompt = f"""You are a job relevance scorer. Given a freelancer profile and a job listing,
    return a JSON object only — no preamble, no markdown fences.
    Freelancer profile:
    {MY_PROFILE}
    Job listing:
    Title: {listing.title}
    Description: {listing.description}
    Budget: {listing.budget}
    Return this exact JSON shape:
    {{
    "score": <1-10>,
    "match_reason": "<one sentence>",
    "required_skills": ["<skill1>", "<skill2>"],
    "budget_fit": "<low|good|high>",
    "action": "<apply|skip|maybe>"
    }}"""
    response = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=300,
        messages=[{"role": "user", "content": prompt}]
    )
    return json.loads(response.content[0].text)

GitHub Actions workflow to schedule the agent daily at 6am UAE time.

name: Job Finder Agent
on:
  schedule:
    - cron: '0 2 * * *'
  workflow_dispatch:
jobs:
  run-agent:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - name: Install dependencies
        run: pip install anthropic notion-client feedparser requests
      - name: Run Job Agent
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          NOTION_TOKEN: ${{ secrets.NOTION_TOKEN }}
          NOTION_DATABASE_ID: ${{ secrets.NOTION_DATABASE_ID }}
        run: python job_agent.py

Practical Applications

  • Lead Generation: Automating recruitment or freelance lead scoring to bypass manual board trawling. Pitfall: Neglecting deduplication can lead to duplicate entries in Notion across consecutive runs.
  • Notification Systems: Extending the pipeline with Make.com to trigger WhatsApp alerts for listings with a score ≥ 8. Pitfall: Excessive token usage if long job descriptions are not truncated before API calls.

References:

Continue reading

Next article

JSONVault Pro: Replacing Compromised Extensions with High-Performance Tooling

Related Content