search-listings

craigslist.org/search-listings-hw5qri · updated May 21, 2026

MDX-style export adds YAML metadata + attribution linking explainx.ai and this canonical listing URL.

$browse install craigslist.org/search-listings-hw5qri
0 commentsdiscussion
summary

Search Craigslist in a given city and category for listings matching a query, returning each listing's title, price, location, posting date, and listing URL.

skill.md
name
search-listings
title
Craigslist Search Listings
description
>- Search Craigslist in a given city and category for listings matching a query, returning each listing's title, price, location, posting date, and listing URL.
website
craigslist.org
category
marketplace
tags
- craigslist - marketplace - listings - search - classifieds
source
'browserbase: agent-runtime 2026-05-19'
updated
'2026-05-19'
recommended_method
api
alternative_methods
- method: browser rationale: >- When the JSON API is rate-limited or blocked (rare — no auth or anti-bot today), fall back to opening the city subdomain's /search/{category} page directly and parsing the rendered HTML with the per-listing regex set documented in the browser-fallback workflow. ~100× more expensive than the API path.
verified
false
proxies
false

Craigslist Search Listings

Purpose

Return a list of Craigslist postings matching a query in a given city and category — title, price, location, posting time, lat/lon, posting ID, and canonical listing URL. Read-only; never posts, edits, replies to, or flags any listing.

When to Use

  • Daily / hourly monitoring of new listings matching a query (cars, bikes, apartments, jobs, free stuff, etc.).
  • Bulk extraction across multiple cities, categories, or price/bedroom ranges.
  • Anywhere you'd otherwise scrape Craigslist HTML — the JSON API is faster, cheaper, and structurally more reliable than rendering /search/{cat} and harvesting per-listing anchors.

Workflow

The Craigslist web UI is a thin client over a public JSON API at https://sapi.craigslist.org — no auth, no cookies, no session state, no anti-bot stealth required. Send a Referer header matching the target city subdomain; if your outbound IP is in a different region than the target city, add postal=<zip>&search_distance=<mi> to the query — the API geo-scopes by IP only when no postal is supplied (see the gotcha below). A residential proxy is not required. Lead with the API path; the browser path works as a fallback but pays a ~100× cost premium because the search page is fully JS-rendered (browse snapshot returns 0 a11y refs and harvesting per-listing URLs costs ~3 turns each).

  1. Pick city + category (and optionally subarea). City is the Craigslist subdomain (sfbay, newyork, losangeles, seattle, chicago, boston, …). Category is the search-path abbreviation (sss for-sale-all, cta cars+trucks, apa apartments, ggg for-sale-by-owner, jjj jobs, zip free stuff, etc.). To scope to a specific subarea (city-within-region), prefix the category in searchPath — e.g. searchPath=sfc/apa for SF-proper apartments, searchPath=eby/cta for East Bay cars. Subarea codes are listed in each response's data.decode.locations[i][2]. Subarea-scoping is significantly more efficient than fetching region-wide and filtering client-side (e.g. apa returns ~9,800 bay-wide vs. ~253 for sfc/apa).

  2. First page:

    GET https://sapi.craigslist.org/web/v8/postings/search/full
        ?searchPath={cat}
        &query={q}
        &sort={date|rel|priceasc|pricedsc}
        &batch=1-0-360-1-0
        &lang=en&cc=us
    Referer: https://{city}.craigslist.org/
    

    Returns JSON with data.totalResultCount, data.items[], and decode tables under data.decode. Confirm the response is scoped to the right region via data.areas (e.g. {"3": {"name": "newyork"}}) — if it shows the wrong city, add postal=<zip>&search_distance=<mi> (any ZIP in the target metro) to override the IP-based geo-scope.

    Common filter params (append as query args; check data.humanReadableParams to confirm acceptance): min_price, max_price, min_bedrooms, max_bedrooms, min_bathrooms, bundleDuplicates=1, hasPic=1, postal=<zip>, search_distance=<mi>, availabilityMode=available, auto_make_model=<text>, min_auto_year/max_auto_year, min_auto_miles/max_auto_miles. Unrecognized params are silently dropped.

  3. Decode each item. data.items[] is an array of positional arrays. Critical: many fields are offsets / lookup keys, not absolute values — always read against data.decode.*:

    • item[0]postingIdOffset. Absolute id = data.decode.minPostingId + item[0].
    • item[1]postedDateOffset (seconds). Absolute epoch = data.decode.minPostedDate + item[1].
    • item[2]categoryId (integer). Maps to a 3-letter sub-category abbreviation (cat3) used in canonical URLs. The mapping is not in the response — it's a fixed Craigslist enum. Observed values: 68 → bik (bicycles), 93 → spo (sporting goods), 122 → pts (parts), 197 → bop (bicycle parts/accessories), 5 → fua (furniture by-owner), 101 → foa (furniture all). Other categories will need to be back-derived or resolved via the redirect-URL fallback in step 4.
    • item[3] — price as integer (0 or missing for free items).
    • item[4]"locIdx:hoodDescIdx:hoodIdx~lat~lon". Look up data.decode.locations[locIdx][1, city, subareaAbbr]; data.decode.locationDescriptions[hoodDescIdx] → display location string; parse lat~lon for coordinates.
    • Title — last array element that is a plain string (i.e. not a tagged [code, ...] block). For cta (cars+trucks) this is item[-1]. For apa (apartments) and other housing categories, a trailing [5, beds, sqft] housing-meta block pushes the title earlier — iterate from the end and take the first plain string.
    • Tagged blocks [code, value] mid-array: code === 5 is [beds, sqft] (housing categories); code === 6 is the URL slug; code === 10 is the formatted price string ("$1,350"); code === 4 is image-id refs; code === 13 is the geo/cluster cell.
  4. Construct canonical post URL:

    https://{city}.craigslist.org/{subareaAbbr}/{cat3}/d/{slug}/{postingId}.html
    
    • postingId from step 3 (offset + minPostingId)
    • subareaAbbr from data.decode.locations[locIdx][2] (e.g. nby, sby, sfc, eby, pen)
    • cat3 from the categoryId enum (step 3)
    • slug from the [6, ...] tagged block

    Wrong cat3 will 404. If you don't know the mapping for a categoryId, fall back to https://{city}.craigslist.org/search/{cat}?postingId={postingId} which redirects to the canonical URL.

  5. Paginate (only if results > 360):

    GET https://sapi.craigslist.org/web/v8/postings/search/batch
        ?batch=1-{OFFSET}-1080-1-0-{startTs}-{endTs}
        &cacheId={cacheId from step 2}
    Referer: https://{city}.craigslist.org/
    

    Increment OFFSET in steps of 1080. startTs/endTs are the data.cacheTs from step 2's response and the current epoch.

Browser fallback

When the API is unreachable or geo-locked away from the target city (rare — postal=<zip> almost always resolves it), open https://{city}.craigslist.org/search/{cat}?query={q}&sort=date directly (bypassing the bare-domain geo-redirect), then capture browse get html body and split per-listing chunks by the regex <div data-pid="(\d+)" class="cl-search-result. Within each chunk, extract:

  • URL: <a class="main" href="(...\.html)" (gallery view) or class="...posting-title" href="(...)" (text view)
  • Title: class="label">([^<]+)</span> inside the posting-title anchor
  • Posted: class="result-posted-date">([^<]+)</span> (relative time, e.g. "6h ago" or "4/30")
  • Neighborhood: class="result-location">([^<]+)</span>
  • Price: class="priceinfo">([^<]+)</span>
  • Housing meta (br/sqft, when present): class="housing">([^<]+)</span>

Skip browse snapshot/click on /search/ — snapshot returns 0 refs and click-through costs ~3 turns per listing. Stable across cta and apa in prior validation.

Site-Specific Gotchas

  • Geo-redirect on bare domain: https://www.craigslist.org/ redirects to a city based on the request IP. Always open {city}.craigslist.org directly. Confirmed 2026-05-19: bare-domain still redirects; deep-link to subdomain is the only reliable bypass.
  • API geolocates by request IP — postal=<zip>&search_distance=<mi> overrides it: No auth, no cookies, no anti-bot — but if no postal is supplied, the API scopes results to the city corresponding to the request's source IP, not the Referer header (e.g. a NY query from an SF IP silently returns {"1": {"name": "sfbay"}} results). Adding postal=<zip> for any ZIP in the target metro plus search_distance=<mi> forces the result set to that region. Re-verified 2026-05-19 with direct browse cloud fetch calls returning correct NYC apartments (postal=10001&search_distance=10, 860 results, data.areas shows newyork/newjersey/longisland/hudsonvalley cluster) and SF Bay bicycles (postal=94103&search_distance=25, 5,635 results, data.areas: {"1": "sfbay"}). A residential proxy is not required and is actively counterproductivebrowse cloud fetch --proxies without postal is also geo-locked to the proxy's exit-IP region, and adding postal to a direct fetch is ~8× faster than the proxy path. Always verify scope via data.areas in the response.
  • Snapshot returns 0 refs on /search/: The search page is fully JS-rendered (React). Don't use browse snapshot/click to enumerate listings — fall back to browse get html body + regex per the Browser fallback section.
  • Compact response format: data.items[] uses positional arrays + data.decode.* lookup tables to keep the response small (~130 KB for 360 items). Don't expect named fields per item — decode by position.
  • Pagination batch sizes: First page is ~360 (batch=1-0-360-1-0); subsequent batches are 1080 each (batch=1-OFFSET-1080-1-0). Mixing these sizes will cause the response to silently truncate.
  • Free items have no price: item[3] may be 0 or absent. Map both to price: null (or "free") in your output; don't render $0.
  • Posting time precision: The rendered HTML shows relative ("< 1 hr ago", "6h ago", "4/30"); absolute epoch is only available via the API as data.decode.minPostedDate + item[1].
  • item[0] is NOT the postingId — it's an offset from data.decode.minPostingId. Naïvely treating item[0] as the postingId produces 404s on every URL you construct.
  • data.decode.locations indexing is per-response, not stable. The same query at two different times can produce locations[1] → ["sfbay","sfc"] vs. locations[1] → ["sfbay","eby"]. The decode block is rebuilt per cache TTL — always look up locations[locIdx] from the response in hand, never cache or hardcode the table across requests.
  • Neighborhood labels are unreliable: data.decode.locationDescriptions varies per response and per category. The same neighborhood may appear under different label-table indices across responses, may be missing in some categories (e.g. "Russian Hill" shows up in apa but is absent from cta decode tables), and is sometimes replaced by a generic city-level label by the poster. For neighborhood-scoped searches, use lat/lon bounding-box matching on item[4]'s coordinates as a fallback or supplement to label-string matching. Example bbox for North Beach + Russian Hill: lat 37.794–37.810, lon -122.425 to -122.404.
  • Categories are an undocumented enum — the response decode tables don't include the categoryId → cat3 mapping; observed values across iters: 5→fua, 68→bik, 93→spo, 101→foa, 122→pts, 197→bop (and likely many more for non-bicycle queries). The redirect URL https://{city}.craigslist.org/search/{cat}?postingId={id} is the safest fallback when an unknown categoryId is encountered.
  • Rate-limit self-imposed: No formal block but Craigslist throttles aggressive clients with terse 403s. Keep ≤ 1 req/s sustained; pagination loops should sleep ~1s between batches.
  • Don't waste time on stealth fingerprinting — the API has no anti-bot today (verified 2026-05-19 via direct unproxied browse cloud fetch returning 200 + 134 KB JSON on the first try with no Referer). The expensive Browserbase --verified --proxies flags do not improve success rate and actively slow the path.

Expected Output

{
  "city": "sfbay",
  "category": "sss",
  "query": "bicycle",
  "sort": "date",
  "total_results": 5635,
  "listings": [
    {
      "posting_id": 7927446618,
      "title": "Kryptonite Evolution 1090 3 Ft Long 10mm Steel Bike Chain BRAND NEW",
      "price": "$100",
      "location": "san leandro",
      "subarea": "eby",
      "category_id": 197,
      "cat3": "bop",
      "lat": 37.6875,
      "lon": -122.1445,
      "posted_at_epoch_seconds": 1779140987,
      "url": "https://sfbay.craigslist.org/eby/bop/d/san-leandro-kryptonite-evolution-ft/7927446618.html"
    }
  ]
}

Free items omit price:

{
  "posting_id": 7926112233,
  "title": "Free moving boxes — Mission",
  "price": null,
  "location": "mission district",
  "subarea": "sfc",
  "cat3": "zip",
  "url": "https://sfbay.craigslist.org/sfc/zip/d/.../7926112233.html"
}

When the postal-override resolves to a multi-area cluster (NY metro returns 4 sub-areas), data.areas enumerates them and individual listings carry the correct sub-area in locations[locIdx][1]:

{
  "city": "newyork",
  "category": "apa",
  "query": "studio",
  "total_results": 860,
  "areas": ["newyork", "newjersey", "longisland", "hudsonvalley", "elmira"],
  "listings": [
    {
      "posting_id": 7935281805,
      "title": "Newly renovated Charming Spacious Studio Near Prospect Park",
      "price": "$2,599",
      "location": "brooklyn",
      "subarea": "brk",
      "cat3": "apa",
      "lat": 40.6724,
      "lon": -73.9573,
      "url": "https://newyork.craigslist.org/brk/apa/d/brooklyn-newly-renovated-charming/7935281805.html"
    }
  ]
}
how to use search-listings

How to use search-listings on Cursor

AI-first code editor with Composer

1

Prerequisites

Before installing skills in Cursor, ensure your development environment meets these requirements:

  • Cursor installed and configured on your development machine
  • Node.js version 16.0+ with npm package manager (verify with node --version)
  • Active project directory or workspace where you want to add search-listings
2

Execute installation command

Execute the skills CLI command in your project's root directory to begin installation:

$browse install craigslist.org/search-listings-hw5qri

The skills CLI fetches search-listings from GitHub repository craigslist.org/search-listings-hw5qri and configures it for Cursor.

3

Select Cursor when prompted

The CLI will show a list of available agents. Use arrow keys to navigate and space to select Cursor:

◆ Which agents do you want to install to?
│ ── Universal (.agents/skills) ── always included ────
│ • Amp
│ • Antigravity
│ • Cline
│ • Codex
│ ●Cursor(selected)
│ • Cursor
│ • Windsurf
4

Verify installation

Confirm successful installation by checking the skill directory location:

.cursor/skills/search-listings

Reload or restart Cursor to activate search-listings. Access the skill through slash commands (e.g., /search-listings) or your agent's skill management interface.

Security & Verification Notice

We perform automated surface-level scans (Gen AI Scanner, Socket, Snyk) during installation. These checks detect common vulnerabilities but do not guarantee complete security. Always review skill source code and verify the publisher's reputation before production use.

Skills execute code in your development environment. Always verify the publisher's identity, review recent commits, and test in isolated environments before production deployment.

List & Monetize Your Skill

Submit your Claude Code skill and start earning

GET_STARTED →

Use Cases

Task Automation & Efficiency

Automate repetitive workflows and reduce manual effort

Example

Generate reports, summarize documents, draft communications

Save 3-5 hours per week on routine tasks

Knowledge Enhancement

Learn new skills, understand complex topics, get expert guidance

Example

Explain concepts, provide examples, suggest learning resources

Accelerate learning and skill development by 2x

Quality Improvement

Enhance output quality through reviews, suggestions, and refinements

Example

Review drafts, suggest improvements, catch errors

Improve work quality by 30-40% with less effort

Implementation Guide

Prerequisites

  • Claude Desktop or compatible AI client with skill support
  • Clear understanding of task or problem to solve
  • Willingness to iterate and refine outputs

Time Estimate

15-45 minutes depending on use case complexity

Installation Steps

  1. 1.Install skill using provided installation command
  2. 2.Test with simple use case relevant to your work
  3. 3.Evaluate output quality and relevance
  4. 4.Iterate on prompts to improve results
  5. 5.Integrate into regular workflow if valuable

Common Pitfalls

  • Expecting perfect results without iteration
  • Not providing enough context in prompts
  • Using skill for tasks outside its intended scope
  • Accepting outputs without review and validation

Best Practices

✓ Do

  • +Start with clear, specific prompts
  • +Provide relevant context and constraints
  • +Review and refine all outputs before using
  • +Iterate to improve output quality
  • +Document successful prompt patterns

✗ Don't

  • Don't use without understanding skill limitations
  • Don't skip validation of outputs
  • Don't share sensitive information in prompts
  • Don't expect skill to replace human judgment

💡 Pro Tips

  • Be specific about desired format and style
  • Ask for multiple options to choose from
  • Request explanations to understand reasoning
  • Combine AI efficiency with human expertise

When to Use This

✓ Use When

Use when skill capabilities match your task, clear ROI on time saved, and you can validate outputs. Best for repetitive tasks, learning, and quality improvement.

✗ Avoid When

Avoid when task requires deep expertise you can't validate, involves sensitive decisions, or when learning process is more valuable than speed of completion.

Learning Path

  1. 1Familiarize yourself with skill capabilities and limitations
  2. 2Start with low-risk, non-critical tasks
  3. 3Progress to more complex and valuable use cases
  4. 4Build expertise through regular use and experimentation

Discussion

Product Hunt–style comments (not star reviews)
  • No comments yet — start the thread.
general reviews

Ratings

4.647 reviews
  • James Martin· Dec 20, 2024

    Solid pick for teams standardizing on skills: search-listings is focused, and the summary matches what you get after install.

  • Alexander Johnson· Dec 8, 2024

    search-listings has been reliable in day-to-day use. Documentation quality is above average for community skills.

  • James Brown· Dec 4, 2024

    Useful defaults in search-listings — fewer surprises than typical one-off scripts, and it plays nicely with `npx skills` flows.

  • Benjamin Yang· Nov 27, 2024

    Solid pick for teams standardizing on skills: search-listings is focused, and the summary matches what you get after install.

  • Hiroshi Menon· Nov 23, 2024

    search-listings is among the better-maintained entries we tried; worth keeping pinned for repeat workflows.

  • James Taylor· Nov 11, 2024

    search-listings has been reliable in day-to-day use. Documentation quality is above average for community skills.

  • Noor Thomas· Oct 18, 2024

    I recommend search-listings for anyone iterating fast on agent tooling; clear intent and a small, reviewable surface area.

  • Nikhil Patel· Oct 14, 2024

    search-listings reduced setup friction for our internal harness; good balance of opinion and flexibility.

  • Neel Sethi· Oct 2, 2024

    search-listings fits our agent workflows well — practical, well scoped, and easy to wire into existing repos.

  • Noor Li· Sep 25, 2024

    Useful defaults in search-listings — fewer surprises than typical one-off scripts, and it plays nicely with `npx skills` flows.

showing 1-10 of 47

1 / 5