Craigslist Has a Hidden Lead Feed: The Buyer-Alert Loophole in the Gigs Section.
Craigslist's Gigs section is a public feed of explicitly stated buyer demand for local services. Platform policy prohibits spam, overposting, and traffic-forcing listings, which selects most growth teams out of the surface entirely. We characterise a compliant loophole - the Buyer Alert - that inverts the classifieds mechanic: instead of publishing an ad and waiting, an autonomous reply-agent subscribes to Craigslist's native saved-search alerts and responds to matching buyer requests through the native mail relay in <60s median latency. This paper is a full technical write-up of the pipeline: the queries, the ingestion path, the classifier, the Hermes drafting model, the send layer, the dedupe/rate-limit rules, the observability stack, and the 30-day results across four US metros (n=412 alerts, 187 relevant, 94 replies, 22 booked jobs). The surface is solicited-demand capture, not virality; the yield profile is a small, predictable lead flow per city per query, sustainable only if the reply stays human-grade at machine latency.
- Statuslive · pilot open
- Clearanceω-05
- SurfaceCRAIGSLIST · demand
- Read6 min read
- Craigslist Saved Search Alertsnative email push on new matching Gigs posts; one saved search per (city x vertical x intent)
- Craigslist boolean query DSLphrase matching, exclusions, grouping; 12 queries per city in the pilot
- Craigslist mail relaynative buyer<->seller reply channel; sender is a real operator mailbox, delivered through relay.craigslist.org
- Gmail API (users.watch)push notifications on the alert mailbox; fires a webhook per new alert email
- Cloudflare Worker (ingest)receives Gmail push, fetches the raw email, extracts the CL post URL, enqueues an Inngest job
- Playwright fetcher on residential proxieshydrates the post exactly once from the URL Craigslist notified us about; no scanning, no crawling
- Gemini 2.5 Flash5-way intent classifier: real_buyer / seller_ad / recruiter_w2 / spam / low_confidence
- Hermes (fine-tuned Llama-3.1-70B)reply-drafting agent; LoRA fine-tuned on ~14k real buyer<->rep threads to sound like a small-business operator
- Claude Sonnet 4.5 (critic)style-guard second pass; scores drafts on relevance, specificity, brevity, register, no-CTA, no-link
- Redis (dedupe + rate limits)per-mailbox send caps, inter-send jitter, 5-gram bloom filter across the last 500 replies
- Postmark transactional SMTPsends the reply from the operator mailbox; DKIM-signed on the operator's business domain
- Supabase Postgresappend-only event ledger: alert -> post -> classification -> draft -> critic -> send -> buyer reply -> booking
- Inngestdurable step-function orchestration per alert; retries, backoff, human-in-the-loop pauses, DLQ
- Retool (human review console)queue for low-confidence classifications and critic-rejected drafts; operator decisions feed the next Hermes epoch
- Vercel logs + Grafanaper-city funnel dashboards; alerts on classifier drift, critic rejection %, mail-bounce %, and send-latency p95
H1: Craigslist's native saved-search alerts, when scoped narrowly to a service's exact request language and paired with a per-post, in-context reply drafted by a fine-tuned messaging agent (Hermes) and sent through the native mail relay in under 60 seconds, produce a compliant, recurring lead flow that materially outperforms a paid Services listing on cost-per-booked-job in the same city. H2: The dominant failure mode is drift toward templated output, not platform detection - meaning the loophole's half-life is bounded by prompt discipline and per-city rate limits, not by anti-abuse systems.
- Surface: Craigslist Gigs section on losangeles, newyork, chicago, and sfbay subdomains; 4 verticals (web dev, cleaning, moving, event photography)
- Query pack: 12 narrow saved searches (3 per vertical) with a positive-phrase block, an exclusion block, and a location scope; the full pack ships in the artefact linked below
- Reply channel: native mail relay only; sender identity is a real Craigslist-registered operator mailbox per city, no burner accounts, no rotation
- Cohort: 30-day pilot, July 17 - August 15, 2026; pre-registered stop rules: kill any query with <10% relevance after week 2, kill any city with mail-bounce >1%
- Model artefacts: Hermes-v0.6 LoRA weights (fine-tuned on 14,214 anonymised buyer<->rep threads), Gemini 2.5 Flash classifier prompt v3, Sonnet critic rubric v2
- Ledger: every alert, hydrate, classification, draft, critic score, send event, buyer reply, and booking is an append-only row in Supabase
- Outcome metrics: alert count, relevance %, critic-passed reply %, buyer-reply %, booking %, per-city p50/p95 send latency, critic rejection %, mail-bounce %
The end-to-end recipe. Follow it top to bottom; each step assumes the previous one ran cleanly.
Step 0 - Register operator mailboxes, one per city
One operator mailbox per city (ops-la@, ops-nyc@, ...) on the operator's real business domain, verified through Craigslist's phone-verification flow. All downstream infra sends from and delivers into these mailboxes; no burner accounts, no proxy rotation on the account layer. Craigslist's account-quality signals are per-region, so we never reuse a mailbox across cities.
Fig.The full Buyer-Alert pipeline - 01Buyer postsGigs section
- 02CL alert emailsaved search
- 03Gmail pushusers.watch
- 04Worker enqueuesInngest job
- 05Playwright hydratepost body
- 06ClassifierGemini Flash
- 07Hermes draftsLlama LoRA
- 08Critic scoresSonnet 4.5
- 09SMTP sendmail relay
- 10Buyer replieslead
Step 1 - Author narrow queries in Craigslist's boolean DSL
Each saved search is one line in Craigslist's boolean DSL. Example (web dev, LA): ("need a website" | "looking for web developer" | "build me a site") -hiring -w2 -"full time" -intern -"remote only". Positive block: buyer intent phrases mined from 12 months of prior Gigs archives. Exclusion block: recruiter-side posts, W2 listings, remote-only asks that do not match a local-services operator. Queries live in configs/craigslist/queries.yaml and hot-reload into the ingest worker.
Step 2 - Ingest alert emails via the Gmail API
Craigslist pushes a plain-text email the moment a matching post lands. A Cloudflare Worker subscribes to Gmail users.watch, receives the message ID, fetches the raw email, and extracts the underlying craigslist.org/gig/... URL with a strict regex. The URL, source query name, destination city, and mailbox identity are packaged into an Inngest job and dropped into the alert.received queue. Path latency Craigslist-send -> enqueue: p50 4.2s, p95 11s, bounded by Gmail's push tick.
Step 3 - Hydrate the post exactly once with a residential fetcher
A Playwright worker running headless Chromium in a residential proxy pool (one sticky IP per city) navigates to the URL once, waits for network-idle, extracts title, body, posted-at, and inline metadata (compensation, employment type, location string), and upserts a posts row keyed by the CL post ID. Idempotent - if the row exists, skip. Craigslist prohibits scraping, so we never fan out beyond the URL we were explicitly notified about; the fetch is one page load per email received. Failures (CAPTCHA, 404, geo-block) mark the alert hydrate_failed and dead-letter for human review; no proxy-rotation retry.
Step 4 - Classify intent with Gemini 2.5 Flash
The hydrated post is passed to a Gemini 2.5 Flash classifier with a 5-way tag: real_buyer, seller_ad, recruiter_w2, spam, low_confidence. Prompt is a 400-token instruction with 8 few-shot examples per class drawn from the human-review console's confirmed labels. Structured JSON output: tag, confidence 0-1, one-sentence reasoning. real_buyer >=0.82 proceeds to drafting; low_confidence and <0.82 route to Retool; other tags archive and drop. Held-out accuracy on 400 human-labelled posts: 91% agreement, kappa 0.83.
Step 5 - Draft the reply with Hermes (fine-tuned Llama-3.1-70B)
Hermes is enso's in-house messaging agent, a Llama-3.1-70B base with a LoRA fine-tune on 14,214 real buyer<->rep threads pulled (with consent) from live sales inboxes, SMS bridges, and support logs. Fine-tune objective: sound like a small-business operator answering a live inquiry, not an assistant. Inference: temp 0.7, top-p 0.9, max 220 output tokens, per-post seed derived from the CL post ID. System message names the operator business; user message is the buyer post verbatim plus 4 rotating in-context examples from the same vertical. Hermes returns one candidate reply; no retries at this stage.
Step 6 - Style-guard the draft with the Claude Sonnet 4.5 critic
Every draft passes through a second-pass critic (Claude Sonnet 4.5) that scores on six axes 0-2: relevance to the exact task, specificity of the observation, brevity (<=5 sentences), register (small-business rep, not assistant), absence of CTA/pitch, absence of external link. Ship if total >=10/12 and no axis is 0. Rejects go to Retool with axis scores attached; every reject is added to the next Hermes fine-tune epoch. Critic rejection rate on Hermes-v0.6: 7.3% (down from 21% on v0.1).
Step 7 - Dedupe, rate-limit, and send through the native mail relay
Before send: draft is hashed into 5-grams and compared against a Redis bloom filter of the last 500 replies from the same mailbox. Any 5-gram collision >=2 blocks the send and forces a re-draft with a hard system-message diff instruction. Rate limits: max 30 replies per mailbox per day, max 4 per hour, min 90s inter-send jitter. Send itself is a standard SMTP call from the operator mailbox to the relay address embedded in the alert email; DKIM-signed on the operator's domain. p50 end-to-end (buyer posts -> reply in buyer inbox): 54s. p95: 118s.
Step 8 - Log everything, close the loop, feed the next epoch
Every stage writes an append-only row to Supabase. When the buyer replies, an inbound Postmark webhook links the buyer message back to the CL post ID. Bookings are logged manually and are the terminal state. Weekly: operator council reviews the funnel dashboards, tightens exclusion lists on queries with <10% relevance, and exports the reject queue as the next Hermes fine-tune batch. Kill switches on classifier drift (<85% w/w), critic rejection >15%, mail-bounce >1%, or any city buyer-reply <5%.
n=412 alerts fired. July 17 - August 15, 2026. Every stage is a row in the ledger; nothing is estimated.
- n=412 alerts across 4 metros x 4 verticals in 30 days. 187 (45.4%) classified as real buyer intent by Gemini + human review; 94 (22.8%) received a critic-passed reply; 41 (9.9%) drew a buyer response; 22 (5.3%) converted to a booked job.
- Fully-loaded cost per booked job: $8.30 (operator time at $45/hr + infra at $0.11/reply). Reference: paid Craigslist Services listing in the same 4 metros during the same window returned $62.40 per booked job.
- Reply latency (buyer posts -> reply in buyer inbox): p50 54s, p95 118s. Manual ops teams doing the same query set achieved p50 37 minutes - 41x slower - and could not staff more than 1 metro concurrently.
- First-responder win rate: 47% of jobs booked went to the first of >=3 replies visible in the buyer's inbox. The 40-minute half-life of a Gigs post makes speed a physical constraint, not a nice-to-have.
- Critic rejection rate on Hermes-v0.6: 7.3% (down from 21% on v0.1). Every reject was hand-labelled and added to the next fine-tune epoch; the curve is still bending.
- Zero platform actions across the pilot: no account holds, no rate warnings, no mail-relay bounces above the 0.4% baseline. The clean version stays open because the reply reads as a human rep who was fast, not as a bot who was templated.
The Buyer Alert is a demand-capture loophole, not a virality loophole. It does not scale by broadcast; it scales linearly by adding (city x vertical x query) triples. Each triple is a stateless, self-contained pipeline instance; the marginal cost of a new triple is one saved-search line and one mailbox. The clean version is not a trick - it is an attention shift. Human ops teams cannot execute at 54-second latency across 4 metros and 12 queries around the clock; the physics of the reply window select them out. Hermes and the critic together are what let the reply read as a human rep at machine latency - the moment the register drifts toward assistant-voice or the critic is bypassed, the loophole collapses in days. Every reply is per-post, grounded in the buyer's exact words, sent through the native relay, and would pass a blind read as coming from a small-business operator. Everything upstream of that invariant is plumbing.
If you want to run this in your own stack, these are the only things that actually matter.
Use native alerts as the trigger, never a scraper as the trigger
Craigslist prohibits automated collection. The loophole is compliant only because the alert system is a first-party product and we only ever fetch the exact URL Craigslist explicitly notified us about. The hydrate step is one page load per email received - never a scan, never a crawl. Any scraping stack removes the compliance property and the mechanic collapses inside a week.
Never ship a reply the critic did not pass
The critic is the load-bearing safety layer. Bypassing it to raise send volume is the single fastest way to burn a mailbox. Route rejects to human review; do not raise the send threshold to unblock throughput.
Keep the first transaction inside the mail relay
No forced website visit, no immediate call-to-action off-platform. The buyer opts out of the relay when they are ready; forcing the exit early is the pattern the platform is designed to suppress.
Fine-tune Hermes on your own inbox, not on public data
The register that keeps the mailbox alive is your operators' actual voice. Public dialogue corpora fine-tune toward assistant-speak and get flagged inside days. ~10k of your own real buyer<->rep threads is enough for a 70B LoRA to lock the register.
Pilot 30 days, one city, three queries, one mailbox
Start narrow. Measure alerts, classifier accuracy, critic-rejection %, buyer-reply %, and booking % weekly. Widen the exclusion list on any query with <10% relevance; add a second city only after the first city's numbers are stable and mail-bounce is below 1%.
Log every stage as an append-only event
The funnel dashboards are only trustworthy if every stage - alert, hydrate, classify, draft, critic, send, buyer-reply, booking - writes an immutable row. Mutable state hides drift. The ledger is the ground truth for the weekly review and the training set for the next Hermes epoch.
- [1]Craigslist Terms of Use
- [2]Craigslist Search Alerts
- [3]Craigslist Boolean Operators
- [4]Craigslist Mail Relay
- [5]Gmail API - users.watch push notifications
- [6]Inngest - durable step functions
- [7]Postmark - inbound webhooks
- [8]LoRA: Low-Rank Adaptation of Large Language Models (Hu et al., 2021)
- [9]Internal: Hermes-v0.6 fine-tune card (enso ML, 2026)
- [10]Internal: Craigslist Buyer-Alert query pack v1.2










