# Facebook local listings

> GET /v1/facebook/listings searches US Facebook Marketplace listings near a ZIP code or exact point, with radius, price, condition, delivery and sort filters.

```text
GET https://api.soldgraph.com/v1/facebook/listings
```

Search active Facebook Marketplace listings near a place. Give a five-digit `zip`, or `lat` and `lon` for an exact point, and optionally a `radius` in miles. One request returns one source page of up to about 24 listings. Sold search and completed sale prices are not supported; `/v1/facebook/sold` is not an API endpoint.

`displayed_price` is the seller's ask in USD. It is not a sale price, and buyers on Facebook Marketplace often negotiate. A price of `0` means the seller listed the item as free.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `q` | string | Required | Search keywords, 1–200 characters, as you'd type them into the marketplace's search box. Be specific: model, size, grade, edition. |
| `page` | integer, 1–1 | `1` | Always `1`. Facebook serves one page per search, so there is no page 2. Optional. |
| `min_price` | integer, 0–1000000 |  | Lowest price, in whole units of the country's currency (US dollars for `us`). Optional. Must not be more than `max_price`. You can send it without `max_price`. |
| `max_price` | integer, 0–1000000 |  | Highest price, in whole units of the country's currency (US dollars for `us`). Optional. You can send it without `min_price`. |
| `marketplace` | `facebook` | `facebook` | Optional. If sent, it must be `facebook`. |
| `country` | `us` | `us` | Only `us` is supported here. eBay, Vinted, Poshmark, Depop and Whatnot endpoints also take other countries. Mercari, OfferUp, Facebook Marketplace and TCGplayer are US only. |
| `zip` | string |  | Five-digit US ZIP code to search around. Send `zip`, or both `lat` and `lon`. Unknown ZIP codes, and places more than 75 miles from any Facebook Marketplace city, return 422. |
| `lat` | number, 17–72 |  | Latitude of the search center in decimal degrees. Use with `lon` instead of `zip`. Rounded to 4 decimals (about 11 m). |
| `lon` | number, -180–-64 |  | Longitude of the search center in decimal degrees. Use with `lat`. |
| `radius` | integer, 1–100 |  | Search radius in miles, 1 to 100. Default is Facebook's own, about 40 miles. Rows whose city is farther than the radius plus 10 miles are dropped. |
| `sort` | `best_match`, `newest`, `price_asc` or `price_desc` | `best_match` | Facebook ranking, newest listed first, or asking price low to high or high to low. Sorted searches return up to 24 rows; best match often returns about 15. |
| `condition` | One or more of `new`, `used_like_new`, `used_good`, `used_fair`, comma-separated |  | One condition, or several separated by commas, such as `used_like_new,used_good`. |
| `delivery` | `local_pickup` or `shipping` |  | Only listings offering local pickup, or only listings that ship. |
| `days_since_listed` | `1`, `7` or `30` |  | Only listings posted in the last 1, 7 or 30 days. |

```bash title="cURL"
curl -G https://api.soldgraph.com/v1/facebook/listings \
  -H "Authorization: Bearer $SOLDGRAPH_KEY" \
  --data-urlencode "q=mountain bike" \
  -d zip=28202 \
  -d radius=25 \
  -d max_price=800
```

```typescript title="TypeScript"
const params = new URLSearchParams({
  q: "mountain bike",
  zip: "28202",
  radius: "25",
  max_price: "800",
});

const res = await fetch(`https://api.soldgraph.com/v1/facebook/listings?${params}`, {
  headers: { Authorization: `Bearer ${process.env.SOLDGRAPH_KEY}` },
});
const data = await res.json();
```

```python title="Python"
import os, requests

res = requests.get(
    "https://api.soldgraph.com/v1/facebook/listings",
    params={"q": "mountain bike", "zip": "28202", "radius": 25, "max_price": 800},
    headers={"Authorization": f"Bearer {os.environ['SOLDGRAPH_KEY']}"},
)
data = res.json()
```

A cache miss returns **202** with a job to poll. A completed search costs one request, including a cached result. Failed searches are free. Results are cached for 15 minutes. See [search jobs](https://soldgraph.com/docs/api/overview.md#search-jobs).

## Location and radius

Facebook Marketplace searches around a city, not an arbitrary point. We search from the Facebook city closest to your `zip` or point, and show it as `result.location.source_city`, with its distance from your point.

- `zip` uses the US Census center point of that ZIP code. A ZIP code with no Census area returns `422`. Use `lat` and `lon` instead.
- Places more than 75 miles from any Facebook Marketplace city return `422`. This is under 1% of US ZIP codes, mostly in remote areas.
- `radius` is 1 to 100 miles. Without it, Facebook uses its own default of about 40 miles.
- When the city is a few miles from your point, we widen the radius sent to Facebook so it still covers your whole circle. `result.location.source_radius_km` is the radius Facebook applied.
- Listing rows carry only their city. `approx_distance_miles` is the distance to that city, not to the item. Rows whose city is farther than your radius plus 10 miles are dropped. Use the [item endpoint](https://soldgraph.com/docs/api/facebook-item.md) for a listing's approximate coordinates.

## Filters

- `sort` takes `best_match`, `newest`, `price_asc` or `price_desc`. In our tests, sorted searches returned up to 24 rows and best match about 15.
- `condition` takes one or more of `new`, `used_like_new`, `used_good` and `used_fair`, separated by commas.
- `delivery` takes `local_pickup` or `shipping`.
- `days_since_listed` takes `1`, `7` or `30`.
- `min_price` and `max_price` are whole US dollars. Either can be sent alone.
- Facebook reports the filters it applied on every page. If it didn't apply one you sent, the search fails with `filter_not_applied` and costs nothing. We never return unfiltered results as if they were filtered.
- If no Facebook city page near your location is working, the search fails with `location_not_served` and costs nothing. Try a nearby ZIP code.

## One page per search

Facebook serves one page of results to logged-out visitors, so `next_page` is always null and there is no cursor. To reach more listings, run several narrower searches. Different `sort` orders, price bands such as `0-50`, `50-150` and `150+`, and `days_since_listed=1` each return a different slice. Remove repeats by `id`.

Image links are signed by Facebook and expire after some days. Save the image itself if you need it later.

## Price summary

`summary` is plain maths over the rows on the page: how many show a price, the median asking price, the lowest and the highest.

- A free item is priced `0`, so `low` can be `0`.
- Listings marked `pending` are counted like any other row.
- Nothing is removed from `data`. The summary counts rows; it never filters them.
- It covers the one page Facebook serves. Run narrower searches to cover more listings.

## Example response

```json title="Response"
{
  "request_id": "example-facebook-listings",
  "status": "complete",
  "credits": 1,
  "cached": false,
  "result": {
    "provider": "facebook",
    "country": "us",
    "query": "mountain bike",
    "page": 1,
    "page_size": 14,
    "count": 14,
    "reported_total": null,
    "next_page": null,
    "next_cursor": null,
    "collected_at": "2026-10-01T20:47:19.890286+00:00",
    "schema_version": 1,
    "completeness": "provider_page_only",
    "location": {
      "zip": "28202",
      "latitude": 35.2273,
      "longitude": -80.8442,
      "radius_miles": 25,
      "source_city": {
        "id": "105715936129053",
        "name": "Charlotte",
        "state": "NC",
        "latitude": 35.2269,
        "longitude": -80.8433,
        "distance_miles": 0.1
      },
      "source_radius_km": 41
    },
    "summary": {
      "priced_count": 2,
      "currency": "USD",
      "median": 47.5,
      "low": 25,
      "high": 70,
      "p25": 25,
      "p75": 70
    },
    "data": [
      {
        "id": "2796090964120499",
        "title": "Mountain bike",
        "link": "https://www.facebook.com/marketplace/item/2796090964120499/",
        "pending": false,
        "displayed_price": {
          "amount": 25,
          "currency": "USD"
        },
        "displayed_price_text": "$25.00",
        "original_price": null,
        "condition": null,
        "city": "Belmont",
        "state": "NC",
        "city_id": "106100896087214",
        "approx_distance_miles": 11.3,
        "listed_at": "2026-09-27T12:26:17+00:00",
        "delivery": {
          "local_pickup": true,
          "shipping": false
        },
        "category_id": "1658310421102081",
        "image": "https://scontent-atl3-1.xx.fbcdn.net/v/t39.84726-6/825268359_3678580222299961_2039901778140218879_n.jpg?stp=c0.87.526.526a_dst-jpg_p526x395_tt6&_nc_cat=110&ccb=1-7&_nc_sid=92e707&_nc_ohc=joVdZ7b0uZ4Q7kNvwEyWUUA&_nc_oc=AdqPTGtXbSvNGMNgFG96X88jHAsPYDrRArzo8hogkbQatErRWDmcGqzbxtKmRAwDNa0&_nc_zt=14&_nc_ht=scontent-atl3-1.xx&_nc_gid=kRjS6reyBV6qgDtQ1jeR3Q&_nc_ss=7b289&oh=00_AQPON8uJttwDPhZ58RwJJmfkQMz0PbiJ6mUde-RsizTBCg&oe=6AC487BD"
      },
      {
        "id": "2104457877105943",
        "title": "Mountain Bike For SALE!!🔥📲🌟✅",
        "link": "https://www.facebook.com/marketplace/item/2104457877105943/",
        "pending": false,
        "displayed_price": {
          "amount": 70,
          "currency": "USD"
        },
        "displayed_price_text": "$70.00",
        "original_price": null,
        "condition": null,
        "city": "Charlotte",
        "state": "NC",
        "city_id": "105715936129053",
        "approx_distance_miles": 0.1,
        "listed_at": "2026-09-30T01:10:57+00:00",
        "delivery": {
          "local_pickup": true,
          "shipping": false
        },
        "category_id": "1658310421102081",
        "image": "https://scontent-atl3-1.xx.fbcdn.net/v/t39.84726-6/825266442_1141005175266806_3972953768091373864_n.jpg?stp=c29.0.540.540a_dst-jpg_p180x540_tt6&_nc_cat=109&ccb=1-7&_nc_sid=92e707&_nc_ohc=wgRLiMrF__YQ7kNvwHsnEnm&_nc_oc=AdqGggELeIUc_xd13ChmfJiWbh9pg5zNnZ1EPZTqXdvCUb_Nu5HjrfVGlyf6yC948RY&_nc_zt=14&_nc_ht=scontent-atl3-1.xx&_nc_gid=kRjS6reyBV6qgDtQ1jeR3Q&_nc_ss=7b289&oh=00_AQO_F7vt91i1R7-kxeIjnCjkSUrNDCS2uQW0Cy4O9tM_Ow&oe=6AC49ED3"
      }
    ],
    "field_notes": "Active local listings around the requested point, from the nearest Facebook Marketplace city. Prices are seller asks, not sale prices. city and state are the seller-chosen area; approx_distance_miles is measured to that city, not to the item. One source page, not complete coverage: narrow with sort, price or days_since_listed to reach more listings. Image links are signed by Facebook and expire. No sold search or final sale prices are provided."
  }
}
```

| Field | Type | Description |
| --- | --- | --- |
| `request_id` | string | ID of this request. Poll it at `/v1/jobs/{request_id}`. |
| `status` | string | `pending`, `complete` or `failed`. Always check it, even on HTTP 200. |
| `credits` | integer | Requests charged: 1 when complete, 0 for a page past the last one, while pending, or when failed. |
| `cached` | boolean | True when the result came from the 15-minute cache. |
| `poll_url` | string | Only while pending. A path like `/v1/jobs/{id}`: join it to `https://api.soldgraph.com`, not to the `/v1` base URL. |
| `error.code` | string | Only when failed. See [failed job codes](https://soldgraph.com/docs/api/errors.md#failed-job-codes). |
| `error.message` | string | Only when failed. What the code means, in plain words. |
| `result` | object | Only when complete. One source page from the marketplace. |
| `result.provider` | string | Marketplace ID, like `ebay` or `poshmark`. |
| `result.country` | string | The country searched: `us`, or on eBay the country you sent. |
| `result.query` | string | The `q` you sent, with extra spaces removed. |
| `result.page` | integer | The page you asked for. |
| `result.count` | integer | Rows in `result.data` on this page. |
| `result.reported_total` | integer | Total matches the marketplace reports for the search. It may be rounded or capped. |
| `result.next_page` | integer \| null | Send this as `page` to get the next page. Null on the last page, and on page 100, the deepest page served. |
| `result.collected_at` | string | When we collected the page, as an ISO 8601 timestamp. |
| `result.schema_version` | integer | Version of the result's shape for that marketplace: `1` or `2`. It only rises when a field changes meaning. |
| `result.completeness` | string | `provider_page_only`: one page as the marketplace served it, not a full history. TCGplayer recent sales say `recent_sample_only`. |
| `result.field_notes` | string | Plain-text notes on how to read this page's fields. |
| `result.location` | object | The search center as `zip`, `latitude` and `longitude`, your `radius_miles`, the Facebook city searched as `source_city` (`id`, `name`, `state`, `latitude`, `longitude`, `distance_miles` from your point), and `source_radius_km`, the radius sent to Facebook, widened to cover your circle from that city. |
| `result.page_size` | integer | Listing rows on the source page. |
| `result.summary` | object | Price maths over the rows on this page: `priced_count`, `currency`, `median`, `low`, `high`. No row is removed from `data`. Rows with no single readable price are left out of the maths. |
| `result.count` | integer | Rows returned: the page minus sold or hidden listings and listings outside your radius. |
| `result.next_page` | null | Always null. Facebook serves one page to logged-out visitors; narrow with `sort`, prices or `days_since_listed` to reach more listings. |
| `result.reported_total` | null | Always null: Facebook does not report a total. |
| `result.data[].id` | string | Facebook Marketplace item ID. Pass it to `/v1/facebook/item` for full details. |
| `result.data[].title` | string | Seller title, up to 2,000 characters. |
| `result.data[].link` | string | Public Facebook Marketplace listing URL. |
| `result.data[].pending` | boolean | True when the seller marked a sale in progress. Sold listings are left out. |
| `result.data[].displayed_price` | object | USD asking price as `{ amount, currency }`. `0` means free. Never a sale price. |
| `result.data[].displayed_price_text` | string | Formatted USD asking price. |
| `result.data[].original_price` | object \| null | Earlier asking price shown struck through when the seller lowered it, else null. |
| `result.data[].condition` | null | Always null: Facebook search pages don't show condition. Use `/v1/facebook/item`, or filter with `condition`. |
| `result.data[].city` | string \| null | Seller-chosen city. |
| `result.data[].state` | string \| null | Two-letter state code. |
| `result.data[].city_id` | string \| null | Facebook city page ID for that city. |
| `result.data[].approx_distance_miles` | number \| null | Miles from your search point to the listing's city, not to the item. Null when the city is not in our place table. |
| `result.data[].listed_at` | string \| null | When the listing was posted, in UTC. |
| `result.data[].delivery` | object | `local_pickup` and `shipping` booleans from the listing's delivery options. |
| `result.data[].category_id` | string \| null | Facebook Marketplace category ID. |
| `result.data[].image` | string \| null | Primary photo on Facebook's CDN. The link is signed and expires after some days. |
