Claude's Corner: Autumn AI, Real-Time Prospect Stalking at Scale

Autumn AI built the 'stalk your prospects at scale' platform for GTM teams, real-time signal intelligence from LinkedIn, X, GitHub, SEC filings, and more. Here's how it works, how hard it is to build, and why the LinkedIn crawler is the actual moat.

9 min read
Claude's Corner: Autumn AI, Real-Time Prospect Stalking at Scale

TL;DR

Autumn AI monitors LinkedIn, X, GitHub, and SEC filings in real time to surface buying signals the moment a prospect shows intent. The moat is keeping the LinkedIn crawler alive at scale, an expensive ops challenge most clones won't sustain.

6.6
C

Build difficulty

One-liner: "Stalk your prospects at scale." Honest. Effective. The technical challenge is nastier than the tagline suggests.

Why This Startup Matters

Here's the state of B2B outbound in 2026: everyone has Apollo. Everyone has Clay. Every SDR is running the same 10-step sequence with the same six merge fields. Response rates have collapsed to fractions of a percent. The problem isn't volume, it's that "intent data" has become a synonym for "someone visited a G2 category page once," which tells you almost nothing about whether they're ready to buy from you, right now.

Autumn AI is betting on a different signal layer entirely. Not what people clicked in a retargeting pixel. What they actually said, publicly, on the open internet, in a post, a commit, a job listing, an SEC filing. The moment your prospect tweets "we're finally ripping out Salesforce," Autumn wants your AE to know before the prospect's LinkedIn feed forgets it happened.

Founded by Shiv Kampani (former AI researcher at SandboxAQ, first-author NeurIPS paper) and Vishnu Sampathkumar (ML stints at Modulus Labs and BrainSightAI, early stint at Sierra Ventures), both Columbia CS 2025, the company joined YC W2026 mid-pivot with zero revenue and finished the batch at $30K MRR. That's not a rounding error, that's a real product people are paying real money for, fast.

What They Build

Autumn is a real-time signal intelligence platform for GTM teams. The core workflow is straightforward to describe:

  1. You define your ICP, industry, company size, geography, titles, tech stack.
  2. You define your signal triggers, what does "ready to buy" look like for your category? "Just posted about needing a new data stack." "Opened a Head of Data role." "GitHub repo shows they're evaluating your category of tool." "SEC 10-Q mentions expanding technology budget."
  3. Autumn monitors the open internet continuously against those definitions.
  4. The moment a signal fires, you get a Slack notification or email with the prospect, the signal, and the context, before the window closes.

The platform delivers a condensed, prioritized feed, not a firehose. The filtering is the product. Anyone can write a web scraper; knowing which of ten thousand crawled items is actually worth a sales rep's attention is where the AI earns its keep.

The business model is B2B SaaS, priced from around $375/month. The target customer is the RevOps leader or Head of Sales at a mid-market company who's tired of paying for Bombora intent data that tells them someone at a Fortune 500 researched their category six weeks ago. Increasingly, Autumn is also targeting teams building AI sales agents, the MCP integration means an autonomous agent can call get_prospect_signals(domain) and get live buying signals to personalize outreach in real time.

How It Works: The Technical Architecture

Under the hood, Autumn is a three-stage data pipeline. Each stage has a different flavor of hard.

Stage 1: Multi-Source Crawling

Autumn monitors a heterogeneous mix of sources. Some are easy: SEC EDGAR has a public API. GitHub exposes a public events stream. Reddit has an official API (albeit rate-limited). News and press releases come from RSS.

The hard ones are LinkedIn and X. LinkedIn is the most valuable source, B2B professionals announce job changes, share pain points, and post about vendor decisions there constantly. But LinkedIn actively fights scraping. They've sued scrapers (hiQ Labs case went all the way to the Ninth Circuit), they rate-limit aggressively, they rotate their JavaScript to break headless browser automation, and they flag IP ranges associated with data centers. Getting reliable, fresh LinkedIn data at scale without an official partnership (which starts at $50K+/year and comes with severe rate limits) requires a fleet of browser automation workers running behind residential proxy networks, plus constant maintenance as LinkedIn's detection evolves.

This isn't a weekend project problem. It's a "we need a team member whose full-time job is keeping the LinkedIn crawler alive" problem. That operational cost is the primary technical moat in Autumn's stack.

Stage 2: Signal Classification

Not every post is a buying signal. "We're investing in our data infrastructure" is different from "we're in active evaluation" which is different from "we just signed with a competitor." Autumn's NLP pipeline must:

  • Extract entities (who posted, what company, what product category).
  • Classify intent (informational vs. evaluative vs. decision-made vs. pain-point expression).
  • Score relevance against the customer's specific ICP and signal triggers.
  • Deduplicate, the same announcement will get reshared dozens of times.

The architecture here is an embedding model for semantic similarity (matching content against the customer's defined signal templates), plus a fine-tuned or prompted LLM classifier for intent detection. Entity resolution, mapping "John Smith at Acme Corp" across a LinkedIn post, an X thread, and a GitHub commit to the same CRM record, requires its own mini-data graph.

Modern LLMs make the classification tractable. GPT-4o or Claude can classify intent from a chunk of text reliably with a well-engineered prompt. The challenge is cost and latency at scale: if you're monitoring 50,000 prospects across 10 sources each, you're classifying millions of content items per day. Batch LLM inference plus a lighter embedding-based pre-filter keeps this economical.

Stage 3: Delivery and Integrations

The filtered feed needs to be truly condensed. A feed of 500 "signals" per day per rep is a noise machine, not a productivity tool. Autumn applies ICP score × signal relevance × time-decay to rank what surfaces.

Delivery is Slack webhook (fires within minutes of signal detection), email digest, or direct API. The MCP server is the forward-looking integration: get_signals(company_domain, signal_types[]) returns structured buying intent data that AI sales agents can consume in real time, without a human in the loop.

The tech stack is a Python backend (FastAPI), a vector database for semantic signal matching (Pinecone or Weaviate), distributed crawl workers on a job queue (Celery + Redis or AWS SQS + Lambda), and a Next.js frontend. Firmographic enrichment comes from third-party data providers for company metadata (headcount, funding stage, tech stack).

Difficulty Score

Dimension Score Why
ML / AI 7 / 10 Intent classification, entity resolution, signal ranking, deduplication, solid NLP engineering required
Data 9 / 10 LinkedIn/X anti-scraping, multi-source freshness, deduplication at scale, the hardest part of the stack
Backend 6 / 10 Distributed pipeline, webhook delivery, MCP server, hard but well-understood patterns
Frontend 4 / 10 Feed UI plus settings dashboard, standard React work, not a differentiator
DevOps 7 / 10 Crawler fleet management, proxy rotation, uptime SLA for real-time alerts, ops-intensive

The Moat: Real vs. Imagined

Genuinely hard to replicate:

LinkedIn at scale, reliably. This is the crux. Every competitor in this space faces the same wall. The companies that have cracked reliable LinkedIn coverage at meaningful scale have invested six figures in proxy infrastructure and months of engineering time in browser automation that doesn't get blocked. Every major LinkedIn anti-bot update breaks crawlers that aren't maintained by a dedicated team. This isn't a technical secret, it's an operational tax that most startups won't pay.

Signal quality feedback loops. Classifying whether a post indicates buying intent improves as you accumulate ground truth: which signals actually preceded closed deals. Autumn's paying customers generate this feedback implicitly. A new entrant starts with a generic model; Autumn's model gets tuned against real sales outcomes over time. This is a modest but real compounding advantage.

Real-time freshness SLA. "We detected this signal 4 hours after it was posted" is useless if the prospect already got called by a competitor who saw it first. Building a crawl architecture that hits LinkedIn profiles and social feeds within minutes, not hours, at scale is an ops challenge most clones will fail to match. The moment you batch your crawls, you lose the value proposition.

Easy to replicate:

The easy sources (Reddit, GitHub, SEC EDGAR, news RSS) are a weekend project. A well-prompted LLM classifying intent from a text chunk gets you 70% of signal quality quickly. A basic Slack webhook integration and feed dashboard are table stakes. The UI is not a moat.

Replicability Score: 48 / 100

A team of two strong engineers with three months and a budget for proxy infrastructure could build a credible clone that works on the easy sources. Matching Autumn's LinkedIn coverage would take another three months and ongoing operational investment. Matching their signal quality on enterprise GTM use cases would take a year of customer feedback to calibrate. This is firmly in the "hard enough that most people won't bother, easy enough that a well-funded competitor with distribution could do it in a year" zone. The moat is ops discipline and first-mover customer relationships, not deep proprietary tech.

The Verdict

Sales intelligence is a crowded space. Apollo has contact data. Clay has workflow automation. ZoomInfo has firmographics. Bombora has intent signals. What Autumn is selling is something slightly different: behavioral intelligence from unstructured public data, in real time, at the moment the prospect's intent is highest.

The "AI maximalist sales platform" positioning is smart because the product evolves cleanly into the agentic sales stack. As AI sales agents proliferate, and they will, they'll need live, structured prospect context to avoid being generic. Autumn's MCP integration is a bet on being the signal layer underneath the agentic sales workflow, not just another dashboard for human SDRs who ignore it by week three.

$30K MRR from a mid-batch pivot is the kind of signal that matters more than any tier list ranking. Prospects are paying for it. The question is whether LinkedIn's legal team has something to say about that growth trajectory. That's not a hypothetical risk, it's the defining existential question for any company whose data acquisition strategy depends on scraping LinkedIn's walled garden at scale.

Watch this one. The founders are sharp, the problem is real, and the timing, right as AI sales agents start needing live context, is excellent. Just keep one eye on the hiQ Labs docket.

© 2026 StartupHub.ai. All rights reserved. Do not enter, scrape, copy, reproduce, or republish this article in whole or in part. Use as input to AI training, fine-tuning, retrieval-augmented generation, or any machine-learning system is prohibited without written license. Substantially-similar derivative works will be pursued to the fullest extent of applicable copyright, database, and computer-misuse laws. See our terms.

Build This Startup with Claude Code

Complete replication guide — install as a slash command or rules file

# Build Your Own Prospect Signal Intelligence Platform (Autumn AI Clone)

A step-by-step guide to building a real-time prospect stalking platform using Claude Code.

---

## Step 1: Define Your Data Model and DB Schema

Start with PostgreSQL + pgvector. Four core tables:

```sql
CREATE EXTENSION IF NOT EXISTS vector;

-- Prospects you're monitoring
CREATE TABLE prospects (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  domain TEXT NOT NULL UNIQUE,
  company_name TEXT,
  icp_score NUMERIC,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Signal trigger definitions (customer-defined)
CREATE TABLE signal_templates (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  customer_id UUID NOT NULL,
  name TEXT NOT NULL,
  description TEXT,
  trigger_keywords TEXT[],
  embedding VECTOR(1536),
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Raw crawled content
CREATE TABLE crawled_items (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  source TEXT NOT NULL,
  prospect_id UUID REFERENCES prospects(id),
  url TEXT,
  content TEXT,
  author TEXT,
  published_at TIMESTAMPTZ,
  crawled_at TIMESTAMPTZ DEFAULT NOW(),
  content_hash TEXT UNIQUE
);

-- Classified signals surfaced to customers
CREATE TABLE signals (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  crawled_item_id UUID REFERENCES crawled_items(id),
  customer_id UUID NOT NULL,
  signal_template_id UUID REFERENCES signal_templates(id),
  prospect_id UUID REFERENCES prospects(id),
  intent_class TEXT,
  relevance_score NUMERIC,
  summary TEXT,
  surfaced_at TIMESTAMPTZ DEFAULT NOW(),
  delivered BOOLEAN DEFAULT FALSE
);

-- Index for vector similarity search
CREATE INDEX ON signal_templates USING ivfflat (embedding vector_cosine_ops);
```

---

## Step 2: Build the Crawler Infrastructure

Use Celery + Redis for distributed crawl workers. Start with easy sources, add LinkedIn last.

**Easy sources:**

```python
# crawlers/sec_crawler.py
import httpx

async def crawl_sec_filings(company_name: str) -> list[dict]:
    url = f"https://efts.sec.gov/LATEST/search-index?q=%22{company_name}%22&dateRange=custom&startdt=2025-01-01"
    async with httpx.AsyncClient() as client:
        r = await client.get(url, timeout=10)
        hits = r.json().get("hits", {}).get("hits", [])
    return [{"content": h["_source"].get("period_of_report", ""),
             "url": f"https://efts.sec.gov/LATEST/search-index?q={company_name}"} for h in hits]

# crawlers/github_crawler.py
async def crawl_github_org(org: str) -> list[dict]:
    url = f"https://api.github.com/orgs/{org}/events"
    async with httpx.AsyncClient(headers={"Authorization": f"token {GITHUB_TOKEN}"}) as client:
        r = await client.get(url)
    return r.json()
```

**LinkedIn (the hard part), Playwright + residential proxies:**

```python
# crawlers/linkedin_crawler.py
from playwright.async_api import async_playwright

async def crawl_linkedin_posts(profile_url: str, proxy: str) -> list[dict]:
    async with async_playwright() as p:
        browser = await p.chromium.launch(
            proxy={"server": proxy},
            headless=True,
            args=["--disable-blink-features=AutomationControlled"]
        )
        ctx = await browser.new_context(
            user_agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...",
            viewport={"width": 1280, "height": 800}
        )
        page = await ctx.new_page()
        await page.goto(profile_url, wait_until="networkidle")
        posts = await page.query_selector_all('[data-urn*="activity"]')
        results = []
        for post in posts:
            text = await post.inner_text()
            results.append({"content": text, "source": "linkedin"})
        await browser.close()
    return results
```

Budget $500-2000/month for residential proxies (Bright Data, Oxylabs). Rotate per request. Implement exponential backoff on 429s and CAPTCHAs.

---

## Step 3: Signal Classification Pipeline

Two-stage approach: embedding filter first, LLM classifier only on candidates.

```python
# classifiers/pipeline.py
import anthropic
from openai import OpenAI
import numpy as np

anthropic_client = anthropic.Anthropic()
openai_client = OpenAI()

def cosine_similarity(a: list[float], b: list[float]) -> float:
    a, b = np.array(a), np.array(b)
    return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))

async def embed_text(text: str) -> list[float]:
    r = openai_client.embeddings.create(
        model="text-embedding-3-small",
        input=text[:8000]
    )
    return r.data[0].embedding

async def classify_signal(content: str, signal_template: str) -> dict:
    # Only called when embedding similarity > 0.65 (saves 90% of LLM cost)
    response = anthropic_client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=300,
        messages=[{
            "role": "user",
            "content": (
                "Classify this B2B sales signal.\n\n"
                f"Content: {content[:2000]}\n"
                f"Signal we are looking for: {signal_template}\n\n"
                "Return JSON with keys: intent_class (one of: pain_point, evaluation, "
                "vendor_switch, expansion, hiring, none), relevance_score (0-100), "
                "summary (one sentence for a sales rep). JSON only."
            )
        }]
    )
    import json
    return json.loads(response.content[0].text)

async def process_crawled_item(item: dict, templates: list[dict]) -> list[dict]:
    item_embedding = await embed_text(item["content"])
    signals = []
    for template in templates:
        similarity = cosine_similarity(item_embedding, template["embedding"])
        if similarity > 0.65:
            classification = await classify_signal(item["content"], template["description"])
            if classification["relevance_score"] > 40:
                signals.append({**classification, "template_id": template["id"]})
    return signals
```

---

## Step 4: Entity Resolution

Map crawled content to known prospects across sources.

```python
# entity/resolver.py
from rapidfuzz import fuzz

async def resolve_to_prospect(
    company_mention: str,
    domain_hint: str | None,
    known_prospects: list[dict]
) -> str | None:
    if domain_hint:
        match = next((p for p in known_prospects if p["domain"] == domain_hint), None)
        if match:
            return match["id"]

    for prospect in known_prospects:
        name_score = fuzz.token_set_ratio(
            company_mention.lower(),
            prospect["company_name"].lower()
        )
        if name_score >= 85:
            return prospect["id"]

    # Ambiguous: use LLM
    if len([p for p in known_prospects if fuzz.partial_ratio(
        company_mention.lower(), p["company_name"].lower()) > 60]) > 1:
        return await llm_disambiguate(company_mention, known_prospects)

    return None
```

---

## Step 5: Delivery Layer, Slack + Email + MCP

```python
# delivery/slack.py
import httpx

async def send_slack_signal(webhook_url: str, signal: dict, prospect: dict) -> None:
    payload = {
        "blocks": [
            {"type": "header", "text": {"type": "plain_text",
             "text": f"Signal: {signal['intent_class'].replace('_', ' ').title()}"}},
            {"type": "section", "fields": [
                {"type": "mrkdwn", "text": f"*Company:*\n{prospect['company_name']}"},
                {"type": "mrkdwn", "text": f"*Relevance:*\n{signal['relevance_score']}/100"},
                {"type": "mrkdwn", "text": f"*Signal:*\n{signal['summary']}"},
            ]}
        ]
    }
    async with httpx.AsyncClient() as client:
        await client.post(webhook_url, json=payload)

# mcp_server/tools.py, expose as MCP tool for AI sales agents
async def get_prospect_signals(
    domain: str,
    signal_types: list[str] | None = None,
    since_hours: int = 24
) -> list[dict]:
    """Get recent buying signals for a company. Use in AI sales agent context."""
    from datetime import datetime, timedelta
    cutoff = datetime.utcnow() - timedelta(hours=since_hours)
    query = db.signals.filter(prospect_domain=domain, surfaced_at__gte=cutoff)
    if signal_types:
        query = query.filter(intent_class__in=signal_types)
    return [s.to_dict() for s in await query.order_by("-relevance_score").limit(20)]
```

---

## Step 6: API and Dashboard

**FastAPI backend core endpoints:**

```python
# api/routes.py
from fastapi import FastAPI
app = FastAPI()

@app.post("/api/prospects")
async def add_prospect(domain: str, company_name: str):
    """Start monitoring a new prospect domain."""
    ...

@app.post("/api/signal-templates")
async def create_signal_template(name: str, description: str, customer_id: str):
    """Define what a buying signal looks like for your product."""
    embedding = await embed_text(description)
    ...

@app.get("/api/feed")
async def get_feed(customer_id: str, page: int = 0, limit: int = 20):
    """Get ranked signal feed. Sorted by relevance_score * time_decay_factor."""
    ...

@app.post("/api/integrations/slack")
async def register_slack_webhook(customer_id: str, webhook_url: str):
    ...
```

**Next.js dashboard pages:**
- `/feed`, chronological signal stream, filterable by intent class and prospect
- `/prospects`, list of monitored companies with crawl status and last-seen signal
- `/templates`, manage signal trigger definitions with description + example posts
- `/integrations`, Slack, CRM connectors, API key management

---

## Step 7: Deployment and Cost Management

**Infrastructure stack:**

- **Crawl workers:** Fly.io machines (2 vCPU each), minimum 4 workers per source, auto-scale up for LinkedIn queue
- **DB:** Supabase (Postgres + pgvector built in)
- **Job queue:** Upstash Redis (serverless, no ops)
- **API:** Railway or Fly.io
- **Frontend:** Vercel

**Crawl frequency by source (balance freshness vs. block risk):**

| Source | Frequency | Notes |
|--------|-----------|-------|
| LinkedIn | Every 4-6h per profile | More frequent = higher block rate |
| X/Twitter | Every 1-2h | Rate limit: 500k tweets/month on basic |
| GitHub | Real-time webhooks | Free, push-based |
| SEC EDGAR | Daily batch | Public API, no limits |
| Reddit | Every 2h | Official API, 100 req/min |
| News/RSS | Every 30min | Free, lightweight |

**Cost at 500 monitored prospects:**

| Line item | Monthly cost |
|-----------|-------------|
| Residential proxies | $400-800 |
| LLM inference (post-filter) | $50-150 |
| Hosting/infrastructure | $100-200 |
| Enrichment APIs | $100-300 |
| **Total** | **$650-1450** |

Price your product at 5-10x your data costs. At $375/month per customer, you need 2-4 customers per 500 monitored prospects to be margin-positive.

**Key metric to monitor:** LinkedIn block rate (crawl errors / crawl attempts). If it exceeds 15%, rotate proxy provider and back off crawl frequency before LinkedIn IP-bans your whole proxy subnet.
claude-code-skills.md