---
title: "Craigslist API: Get Classified Postings as JSON"
description: "Search Craigslist postings by site, category and price as JSON, then poll for new ones. A real response, the parameters and the credit cost."
url: https://perceptlayer.com/blog/craigslist-api-postings-json
---

[Blog](https://perceptlayer.com/blog) / [API guides](https://perceptlayer.com/blog/category/api-guides)

# Craigslist API: Get Classified Postings as JSON

3 Jul 2026 Updated 26 Oct 2026 Perceptlayer 8 min read

Craigslist's documented developer interface is for submitting ads, not reading them, but you can get its classified postings as JSON with four GET calls: `sites`, `categories`, `search` and `posting`. Each call costs 1 credit, and a failed call costs nothing. This guide walks through them in order, shows a real search response, and ends with a polling loop that watches a category for new postings and a worked credit budget for it.

## Key takeaways

-   The Craigslist API has 4 endpoints, `/v1/craigslist/sites`, `/categories`, `/search` and `/posting`, and each successful call costs 1 credit.
-   `search` filters by `site`, `subarea`, `category`, `query`, `min_price`, `max_price`, `sort` and `page`, and returns `total`, `page`, `has_more` and a `results` array.
-   Use the `sites` and `categories` endpoints to look up the values you pass to `search`, so you never hard-code a guess.
-   A poll every 15 minutes is 96 calls a day, or 2,880 credits over 30 days, before any `posting` lookups.
-   Failed calls return `X-Credits-Used: 0`, so a retry loop around a 429 or 503 doesn't bill you for the failures.

## Is there an official Craigslist API?

None that we know of for reading postings. The interface Craigslist documents is a [bulk posting interface](https://www.craigslist.org/about/bulk_posting_interface) for submitting ads, which is a different job from searching them, and its [terms of use](https://www.craigslist.org/about/terms.of.use) are worth reading before you build anything on top of the site. If you need to read postings as structured data, the options are a scraper you maintain or an API that returns them already parsed.

The Perceptlayer [Craigslist API](https://perceptlayer.com/apis/craigslist) is the second option. It returns JSON with named fields instead of HTML, and data is collected from publicly available sources and returned as found at the time of the call. If you're weighing the two approaches, [what maintaining a scraper involves](https://perceptlayer.com/blog/scraper-vs-api-maintenance-cost) lays out the ongoing work.

## What does each Craigslist endpoint return?

The four endpoints map onto how you'd browse the site by hand: pick a city, pick a section, search, open a posting. All four are `GET` requests under `https://api.perceptlayer.com`, all cost 1 credit per successful call, and none has required parameters except `posting`, which needs an `id`.

| Endpoint | Returns | Credits | Required | Optional |
| --- | --- | --- | --- | --- |
| `/v1/craigslist/sites` | Craigslist sites (cities and regions) | 1 | none | `country`, `query` |
| `/v1/craigslist/categories` | Posting categories | 1 | none | `query` |
| `/v1/craigslist/search` | Search results | 1 | none | `site`, `subarea`, `category`, `query`, `min_price`, `max_price`, `sort`, `page` |
| `/v1/craigslist/posting` | One posting | 1 | `id` | none |

Each endpoint also has its own page with a playground: [Sites](https://perceptlayer.com/apis/craigslist/sites), [Categories](https://perceptlayer.com/apis/craigslist/categories), [Search](https://perceptlayer.com/apis/craigslist/search) and [Posting](https://perceptlayer.com/apis/craigslist/posting). The API belongs to the [Marketplaces](https://perceptlayer.com/verticals/marketplaces) area of the catalogue.

## How do you find the right site and category?

Call `sites` and `categories` once, filter them with `query`, and store the values you need. The `site` and `category` parameters on `search` take short codes, and looking them up beats guessing. The recorded search below uses `site=portland` and `category=bia`, and its results report `portland` as the site and `mlt` as the sub-area.

```bash
# Which sites match "portland"?
curl "https://api.perceptlayer.com/v1/craigslist/sites?query=portland" \
  -H "X-API-Key: $API_KEY"

# Which categories match "bike"?
curl "https://api.perceptlayer.com/v1/craigslist/categories?query=bike" \
  -H "X-API-Key: $API_KEY"
```

`sites` also takes a `country` filter, and `query` is capped at 100 characters on both endpoints. Store the values you get back rather than looking them up on every poll. Run the two calls once and the 2 credits are a setup cost, not a per-poll cost. If you're new to the key and headers, [authentication](https://docs.perceptlayer.com/authentication) covers them, and the [quick start](https://docs.perceptlayer.com/quickstart) gets a first call running.

## Which parameters does the search endpoint take?

`GET /v1/craigslist/search` takes eight optional parameters, so a bare call is valid, but you'll nearly always set `site` and `category`. Filters combine: a site, a category, a text query and a price range narrow the result set before it reaches you, which keeps pages short and credits low.

| Parameter | Type | Notes |
| --- | --- | --- |
| `site` | string | A site from the `sites` endpoint, such as `portland` |
| `subarea` | string | A sub-area within a site, such as `mlt` in the recorded response |
| `category` | string | A category from the `categories` endpoint, such as `bia` |
| `query` | string | Search text, up to 200 characters |
| `min_price` | integer | Lower price bound |
| `max_price` | integer | Upper price bound |
| `sort` | string | One of `date`, `dateoldest`, `rel`, `priceasc`, `pricedsc` |
| `page` | integer | Page number, starting from the `page` value in the response |

The `sort` values are the lever for monitoring. For a poller, `date` is the one to start with. The reference doesn't spell out how each value orders results, so check the order on your first response before you rely on it.

## What does a search response look like?

A search returns a `data` object with the match count and a `results` array of postings. Here's a real call for bikes between 50 and 300 dollars in Portland, followed by an excerpt of the recorded response, trimmed with `…`.

```bash
curl "https://api.perceptlayer.com/v1/craigslist/search?site=portland&category=bia&query=bike&min_price=50&max_price=300" \
  -H "X-API-Key: $API_KEY"
```

```json
{
  "success": true,
  "data": {
    "total": 821,
    "page": 1,
    "per_page": 360,
    "has_more": true,
    "results": [
      {
        "position": 1,
        "id": "wnD16b4hEY7DaHprfGYGAg",
        "posting_id": 7988730852,
        "title": "21\" Specialized Sirrus A1 commuter",
        "url": "https://www.craigslist.org/view/wnD16b4hEY7DaHprfGYGAg",
        "price": 250,
        "currency": "USD",
        "images": [
          "https://images.craigslist.org/00s0s_f9B6cl8tXrr_0Cz0t2_600x450.jpg"
        ],
        "slug": "portland-21-specialized-sirrus-a1",
        "location": {
          "name": "multnomah county",
          "site": "portland",
          "subarea": "mlt",
          "latitude": 45.56,
          "longitude": -122.6001
        },
        "bumped_at": "2026-10-23T20:30:33Z"
      },
      …
    ]
  },
  "meta": {
    "endpoint_family": "search",
    "billable": true
  }
}
```

The fields that matter for most jobs are `title`, `price` with `currency`, `images`, `location` (with `latitude` and `longitude`) and `bumped_at`. There are two identifiers: `id`, a string, and `posting_id`, an integer. In the recorded response, `total` was 821 and `has_more` was `true`, so one call is a window onto a larger set, and `page` moves through it. For a safe page loop, see [paginating a web data API in Python](https://perceptlayer.com/blog/paginate-web-data-api-python).

## How do you fetch a single posting?

Call `GET /v1/craigslist/posting` with the posting's `id`, the one required parameter on any Craigslist endpoint. It costs 1 credit and returns that one posting, which is the call to make when you already have an `id` from a search and want that posting on its own.

```bash
curl "https://api.perceptlayer.com/v1/craigslist/posting?id=wnD16b4hEY7DaHprfGYGAg" \
  -H "X-API-Key: $API_KEY"
```

We don't have a recorded `posting` response to quote here, so run it in the playground on the [Posting page](https://perceptlayer.com/apis/craigslist/posting) to see its exact fields before you write a parser. Treat `posting` as the second step, not the first: search results already carry title, price, photos and location, so fetch a posting on its own only when you've decided you care about it. That alone cuts a monitoring job's credit use sharply.

## How do you poll for new postings?

Run a `search` with `sort=date` on a timer, remember the `id` values you've already seen, and treat anything new as a fresh posting. The loop below uses the Python `requests` library, honours `Retry-After` on a 429 or 503, and prints new postings; swap the print for a database write or a notification.

```python
import os
import time

import requests

BASE = "https://api.perceptlayer.com"
HEADERS = {"X-API-Key": os.environ["API_KEY"]}
PARAMS = {
    "site": "portland",
    "category": "bia",
    "query": "bike",
    "min_price": 50,
    "max_price": 300,
    "sort": "date",
}
INTERVAL = 15 * 60  # seconds between polls

seen = set()
first_run = True

while True:
    resp = requests.get(
        f"{BASE}/v1/craigslist/search", headers=HEADERS, params=PARAMS, timeout=30
    )
    if resp.status_code in (429, 503):
        time.sleep(int(resp.headers.get("Retry-After", 30)))
        continue
    resp.raise_for_status()

    for item in resp.json()["data"]["results"]:
        if item["id"] in seen:
            continue
        seen.add(item["id"])
        if not first_run:
            print(item["price"], item["title"], item["url"])

    first_run = False
    print("credits left:", resp.headers.get("X-Credits-Remaining"))
    time.sleep(INTERVAL)
```

The first pass only fills `seen`, so you don't get the whole current page as "new". If `sort=date` returns the newest first, as you should confirm on your first response, page 1 is enough as long as fewer new postings arrive between polls than one page holds. If your category is busier than that, shorten the interval or narrow it with `subarea` or a tighter price range. A timeout on the client side may mean the call succeeded and was billed, since requests have no idempotency key; the next poll simply picks up whatever you missed. [Retrying without paying twice](https://perceptlayer.com/blog/retry-web-api-calls-without-paying-twice) covers the edge cases, and [errors](https://docs.perceptlayer.com/errors) lists every code.

## What does polling cost in credits?

Every poll is one `search` call at 1 credit, so the cost is the number of polls plus any `posting` lookups. You choose the interval, and the arithmetic is simple enough to do before you start. The table assumes one search per poll and no detail lookups.

| Interval | Calls per day | Credits over 30 days |
| --- | --- | --- |
| Every 60 minutes | 24 | 720 |
| Every 15 minutes | 96 | 2,880 |
| Every 5 minutes | 288 | 8,640 |

The free tier gives 1,000 credits a month after you verify an email address, so an hourly poll of one search (720 credits) fits inside it with 280 credits to spare. Polling every 15 minutes for 30 days is 96 × 30 = 2,880 credits, which is within the smallest pack of 10,000 credits. Purchased credits last 12 months, there's no subscription, and [pricing](https://perceptlayer.com/pricing) has what credits cost.

Add detail lookups on top. If 150 new postings turn up in a month and you fetch each one with `posting`, that's 150 × 1 credit = 150 more credits. Every response carries `X-Credits-Used` and `X-Credits-Remaining`, so the loop above can stop itself when the balance gets low. See [credits](https://docs.perceptlayer.com/credits) for how billing works.

A job like this is also a request-rate question. One search every few minutes is far below the 1 request a second a free account gets, but if you run many sites and categories at once, stagger them. The [rate limits](https://docs.perceptlayer.com/rate-limits) page has the headers to watch.

## When should you not use this?

If you only need a handful of postings once, the free tier and the playground are enough, and you don't need a loop. If you're building something that reaches out to the people who posted, stop and read the site's terms and the law that applies to you first: the data here is for analysis such as price tracking and market research, not for contacting individuals.

For a different marketplace shape, the same pattern applies to [eBay search results](https://perceptlayer.com/blog/ebay-item-search-data-json), and [deduplicating listings across sources](https://perceptlayer.com/blog/deduplicate-listings-across-sources) helps when the same item appears in more than one place. Next step: [get a free API key](https://perceptlayer.com/signup), then run the `sites` call above in the playground on the [Craigslist API page](https://perceptlayer.com/apis/craigslist).

## APIs in this post

-   [Craigslist **1 credit per call** · 4 endpoints](https://perceptlayer.com/apis/craigslist)

## Frequently asked questions

### Is there a Craigslist API for searching postings?

Craigslist documents a bulk posting interface for submitting ads, but we know of no documented endpoint for searching them. Perceptlayer's Craigslist API covers that with four endpoints ( `sites`, `categories`, `search` and `posting`) that return postings as JSON, each costing 1 credit per successful call.

### How much does a Craigslist API call cost?

Every Craigslist endpoint costs 1 credit per successful call, and failed calls cost nothing and return `X-Credits-Used: 0`. The free tier includes 1,000 credits every month after email verification, with no credit card. A 15-minute polling loop uses 2,880 credits over 30 days.

### Can I filter Craigslist results by price and location?

Yes. `search` accepts `min_price` and `max_price` as integers, plus `site` and `subarea` for location, `category` for the section and `query` for text. Each result also carries a `location` object with a name, site, sub-area, `latitude` and `longitude`.

### How do I get more than one page of results?

Pass `page` as an integer on `search`. The response includes `total`, `page`, `per_page` and `has_more`, so loop while `has_more` is `true`. Each page is a separate call at 1 credit, so budget pages the same way you budget polls.

### What happens when I hit the rate limit?

You get a 429 with a `Retry-After` header giving the seconds to wait, and the rejected call isn't billed. Free accounts get 1 request a second, and paying accounts run at least 5 a second. Limits are per account, shared by all its keys.

### Should I build a Craigslist scraper instead?

You can, but a Craigslist scraper is yours to keep working: page markup changes, request limits apply, and Craigslist's terms govern how you collect postings. The search endpoint returns postings as named JSON fields for 1 credit per successful call. A scraper makes sense only for data the endpoint doesn't return; run the endpoint in the playground first to compare.

craigslist classifieds marketplaces listings polling

## More from the blog

1.  [API guides](https://perceptlayer.com/blog/category/api-guides) 16 Oct 2026 Perceptlayer 7 min read
    
    ## [Kleinanzeigen API: Search German Classifieds as JSON](https://perceptlayer.com/blog/kleinanzeigen-api-search-json)
    
    Search Kleinanzeigen ads by query, city, category and price range with one GET call. Parameters, a real response and the cost: 1 credit per call.
    
2.  [API guides](https://perceptlayer.com/blog/category/api-guides) 27 Jul 2026 Updated 26 Oct 2026 Perceptlayer 8 min read
    
    ## [eBay API: Search Results, Items and Autocomplete as JSON](https://perceptlayer.com/blog/ebay-item-search-data-json)
    
    Use the eBay API to get search results, items and autocomplete as JSON with one key. Real parameters, a recorded response and the cost: 1 credit per call.
    
3.  [API guides](https://perceptlayer.com/blog/category/api-guides) 15 Oct 2026 Perceptlayer 7 min read
    
    ## [Redfin API: Search Homes, Then Property and Valuation](https://perceptlayer.com/blog/redfin-api-property-valuation)
    
    Four Redfin endpoints at 1 credit each: find a region, search homes with a real JSON response, then call property and valuation by property\_id.
