lurk Agent Guide

September 17, 2026 ยท View on GitHub

You are operating lurk by AnyAPI, a Reddit buyer-intent lead finder, on behalf of a human user. Everything here is read-only: this product finds and scores leads, and the person decides what to do about them.

The API lives on the same origin you fetched this file from, under /api/v1. The examples below call it $BASE, so set BASE=https://<this instance>/api/v1.

  • Machine-readable schema: /openapi.json
  • MCP endpoint: /api/mcp

What a lead actually is

A lead is a Reddit post or comment that this app bought from AnyAPI, one call at a time, and then scored against the user's product profile. It is scored output, not raw Reddit access: every lead carries a written reason, the matchedPhrase that surfaced it, an intent stage, a sellerSide flag for the posters who are selling rather than buying, and costUsd, which is what the Reddit data behind that lead cost in dollars.

score is 0-100 and it is a sort order for a human's attention. It is not a probability that the person will buy, and you should never present it as one.

Authentication

Every request carries the user's key:

Authorization: Bearer rl_sk_...

The user creates keys in the app under Settings -> API. Keys are read-only (read scope). Reading this API costs nothing, and every response says so with X-Request-Cost-Usd: 0; the only money in this product is the Reddit data the scans buy, which is already spent by the time you read a lead.

Each key has a daily request allowance of 1,000 requests a day, lifted entirely once the user has connected their own AnyAPI wallet. Over it you get 429 with a Retry-After header in seconds; the counter resets at UTC midnight. A self-hosted instance has no allowance at all. GET /me reports the cap as requestsPerDay, which is null when there is none.

The loop

Start with GET $BASE/me. It tells you the user's tier, their limits, and how many requests the key has spent today.

curl -H "Authorization: Bearer $KEY" $BASE/me

Then list projects and pull the untriaged queue in one call:

curl -H "Authorization: Bearer $KEY" $BASE/projects
curl -H "Authorization: Bearer $KEY" \
  "$BASE/projects/$PROJECT/leads?status=new&minScore=70&include=body&limit=50"

status=new is the default and is the queue the user sees in the app. The default window is the last 30 days of Reddit posts, which is also how long this product keeps Reddit data. include=body returns the full post or comment text, which is what you need to classify anything. Paginate with limit and offset and stop when pagination.hasMore is false; the page cap is 100.

For an incremental sync, pass since=<ISO 8601>. That filters on when the lead was scored, not when the post was written, so you see exactly what is new since your last pull.

Other reads:

GET $BASE/projects/$PROJECT/seo-opportunities   Reddit threads already ranking
                                                on Google for the keywords
GET $BASE/projects/$PROJECT/pain-themes         what the leads keep complaining
                                                about, clustered
GET $BASE/projects/$PROJECT/usage               today's AnyAPI spend for this
                                                project

Triage is yours, acting is the human's

For each lead, decide whether it is a real buyer worth a conversation, and tell your user what you concluded and why. Quote the poster's own words back to them; the reason field is the app's one-line judgement, and your job is to check it against the body rather than repeat it.

There is no sending in this product, and no reply writing either. No DM queue, no browser extension, no comment posting, no outbox, no drafted replies. Hide and not a fit are actions the human takes in the app, and they are the only two states a lead moves into by hand. If your user asks you to reply to a lead, the words have to come from you or from them: hand them the text and point them at the url field. Reddit punishes generic outreach and so do the subreddits; a specific, peer-to-peer message that references the actual post is the only kind worth sending.

Where the money is

Every lead carries costUsd: the dollar cost of the AnyAPI call that fetched the post behind it. It is often a fraction of a cent. GET .../usage sums today's spend for a project and splits it two ways that matter:

  • fetched is calls this project actually paid for.
  • reused is calls served from data another project had already bought, which cost this project nothing.

If a user asks "what is this costing me", answer from usage, per project, in dollars. Do not estimate, and do not turn a per-lead cost into a projection unless they ask for one.

Connecting over MCP

The same API is available as an MCP server over streamable HTTP at /api/mcp, with the same Bearer key:

{
  "mcpServers": {
    "lurk": {
      "url": "https://<this instance>/api/mcp",
      "headers": { "Authorization": "Bearer rl_sk_..." }
    }
  }
}

Tools: list_projects, list_leads, get_lead, list_seo_opportunities, list_pain_themes, get_usage, and describe, which returns this guide. list_leads takes the same filters as the REST call.

Errors

Every error is the same envelope:

{ "error": { "code": "...", "message": "..." } }

Codes: unauthorized (401, missing or wrong key), not_found (404, or a project that is not this user's), invalid_request (400, a filter value this API cannot read), rate_limited (429, back off until Retry-After), and internal_error (500).