# OfferUp Scraper $1💰 Listings, Sellers, Geo & Categories (`abotapi/offerup-scraper`) Actor

Scrape OfferUp listings by keyword, location and radius. Returns 40+ fields per item: price, condition, GPS, full photo set, category tree and rich seller profiles (rating, items sold, join date, verification). Search and URL modes, 5 sort orders, price and condition filters.

- **URL**: https://apify.com/abotapi/offerup-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** E-commerce, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 listing results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## OfferUp Scraper

Scrape public listings from OfferUp, the largest mobile-first local marketplace in the United States. Give it a keyword and a location, or paste OfferUp URLs, and get clean structured JSON: prices, conditions, photos, GPS coordinates, category trees and full seller profiles. Search and URL modes, five sort orders, and price and condition filters are all built in.

### Why this scraper

- Returns 40+ fields per listing, far more than typical OfferUp scrapers (geo coordinates, full photo set, category L1/L2/L3, and a complete seller profile).
- Two modes: keyword search with filters, or paste OfferUp search and item URLs directly.
- Five native sort orders: best match, newest, closest, price low to high, price high to low.
- Price range and multi-condition filtering, applied at the source.
- Location by city name, ZIP code, or raw coordinates, with adjustable radius.
- Rich seller data: rating, review count, items sold, join date, response time and verification status.
- Fast HTTP extraction with automatic connection rotation and retry.
- **Walks the whole catalogue by default.** `maxPages` defaults to `0` (unlimited): the run walks every result page per query/URL, stopping on its own once OfferUp's own last page is reached or a page repeats listings already seen this walk. `maxItems` is the sole cap on volume.
- **Resumable runs.** Set `resumeFromRunId` to a previous run or dataset id to collect only listings that run doesn't already have (a delta). Long runs also checkpoint their progress, so a platform migration or a Resurrect of a failed run picks up where it left off instead of starting over.

### Data you get

> Sample shape: values are illustrative placeholders, not from a live listing.

| Field | Example |
|---|---|
| listingId | `00000000-0000-0000-0000-000000000000` |
| title | `Sample Listing Title` |
| price | `799` |
| isFirmPrice | `false` |
| conditionText | `Used` |
| locationName | `Miami, FL` |
| url | `https://offerup.com/item/detail/00000000-0000-0000-0000-000000000000` |
| imageUrl | `https://images.offerup.com/000000000-254x250.jpg` |
| flags | `["LOCAL_PICKUP"]` |
| latitude | `25.0000` |
| longitude | `-80.0000` |
| distanceMiles | `4.9` |
| categoryL1Name | `Electronics & Media` |
| categoryL2Name | `Cell phones & Accessories` |
| description | `Full item description appears here when Fetch full details is on.` |
| originalPrice | `899` |
| isOnSpecial | `true` |
| savingsAmount | `100` |
| discountPercent | `11` |
| photos | `[{ "uuid": "0000", "url": "https://images.offerup.com/000-1440x1920.jpg" }]` |
| postDate | `2026-01-01T00:00:00.000Z` |
| localPickupEnabled | `true` |
| shippingEnabled | `false` |
| sellerName | `Jane Doe` |
| sellerRating | `5` |
| sellerReviewCount | `89` |
| sellerItemsSold | `564` |
| sellerDateJoined | `2016-01-01T00:00:00Z` |
| sellerResponseTime | `Responds in a few minutes` |
| sellerIsTruYou | `true` |

### How to use

Search a city with filters:

```json
{
  "mode": "search",
  "searchQueries": ["iphone 14"],
  "location": "Miami, FL",
  "radiusMiles": 30,
  "priceMin": 200,
  "priceMax": 600,
  "condition": ["USED", "REFURBISHED"],
  "sortBy": "-price",
  "maxItems": 50,
  "proxy": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "US" }
}
```

Search multiple keywords with full details:

```json
{
  "mode": "search",
  "searchQueries": ["mountain bike", "road bike"],
  "location": "33101",
  "fetchDetails": true,
  "maxItems": 40
}
```

Search for genuine price drops only (detail fetch is auto-enabled for this):

```json
{
  "mode": "search",
  "searchQueries": ["couch"],
  "location": "Miami, FL",
  "priceDropOnly": true,
  "maxItems": 40
}
```

Paste OfferUp URLs (search pages and item pages):

```json
{
  "mode": "url",
  "urls": [
    "https://offerup.com/search?q=sofa&radius=20",
    "https://offerup.com/item/detail/00000000-0000-0000-0000-000000000000"
  ],
  "fetchDetails": true
}
```

Resuming a previous run (only the listings you don't already have):

```json
{
  "mode": "search",
  "searchQueries": ["iphone 14"],
  "location": "Miami, FL",
  "maxItems": 0,
  "resumeFromRunId": "<a previous run id or dataset id from this account>"
}
```

### Input parameters

| Parameter | Type | Default | Description |
|---|---|---|---|
| mode | string | `search` | `search` (keyword + location) or `url` (paste OfferUp URLs). |
| searchQueries | array | `["iphone"]` | Keywords to search (search mode). |
| location | string | `Miami, FL` | City, ZIP, or `lat,lon`. Empty searches near the connection location. |
| radiusMiles | integer | `50` | Radius in miles around the location (1 to 500). |
| priceMin / priceMax | integer | none | Price range in USD. |
| condition | array | none | One or more of NEW, OPEN\_BOX, REFURBISHED, USED, BROKEN, OTHER. |
| priceDropOnly | boolean | `false` | Keep only listings with a genuine seller price drop (search mode). Price-drop data lives on the listing detail page, not the search results, so turning this on auto-enables `fetchDetails` for the run (and its per-item surcharge) even if left off. |
| sortBy | string | `best_match` | best\_match, -posted, distance, price, -price. |
| urls | array | none | OfferUp search or item URLs (url mode). |
| fetchDetails | boolean | `false` | Visit each listing for description, GPS, photos, category and seller profile. Always on when `priceDropOnly` is set. |
| maxItems | integer | `20` | The single run cap. Set 0 for unlimited. |
| maxPages | integer | `0` | Optional safety limit on result pages walked per query. Leave at 0 (default) to walk the whole catalogue: the run stops at Max items, OfferUp's own last page, or a repeat-page guard, not an artificial page cap. |
| resumeFromRunId | string | (empty) | ID of a previous run (or dataset) of this actor. Listings already in that dataset are skipped, so this run returns only NEW listings (a delta). |
| proxy | object | Residential US | Proxy configuration. |

`maxItems` is the single cap. `maxPages` is only an optional safety bound (0 = unlimited) and does not limit below `maxItems`. Progress is checkpointed automatically, so a run interrupted by a platform migration or restarted via Resurrect picks up where it left off instead of re-collecting and re-charging already-pushed listings.

### Output example

> Sample shape: values are illustrative placeholders, not from a live listing.

```json
{
  "listingId": "00000000-0000-0000-0000-000000000000",
  "url": "https://offerup.com/item/detail/00000000-0000-0000-0000-000000000000",
  "title": "Sample Listing Title",
  "price": 799,
  "isFirmPrice": false,
  "conditionText": "Used",
  "conditionCode": 40,
  "locationName": "Miami, FL",
  "latitude": 25.0000,
  "longitude": -80.0000,
  "distanceMiles": 4.9,
  "flags": ["LOCAL_PICKUP"],
  "categoryL1Name": "Electronics & Media",
  "categoryL2Name": "Cell phones & Accessories",
  "description": "Full item description appears here when Fetch full details is on.",
  "originalPrice": 899,
  "isOnSpecial": true,
  "savingsAmount": 100,
  "discountPercent": 11,
  "photos": [{ "uuid": "0000", "url": "https://images.offerup.com/000-1440x1920.jpg", "width": 1440, "height": 1920 }],
  "photoCount": 7,
  "postDate": "2026-01-01T00:00:00.000Z",
  "localPickupEnabled": true,
  "shippingEnabled": false,
  "sellerName": "Jane Doe",
  "sellerRating": 5,
  "sellerReviewCount": 89,
  "sellerItemsSold": 564,
  "sellerDateJoined": "2016-01-01T00:00:00Z",
  "sellerResponseTime": "Responds in a few minutes",
  "sellerIsTruYou": true
}
```

### Send results into your apps (MCP connectors)

Optionally pipe results into the apps you already use, through Model Context Protocol (MCP) connectors. Authorize a connector under Apify, Settings, API and Integrations, then select it in the `mcpConnectors` input. For Notion, also set `notionParentPageUrl`. Each item is written as a condensed, human-readable summary (title plus key fields), not the full JSON; the complete record always stays in the Apify dataset. Leave the field empty to skip; it never changes the dataset output.

### Plan requirement

OfferUp only serves connections from the United States and blocks non-US and datacenter connections. Use Apify Residential proxy with country US (Starter plan or higher includes Residential proxy access). On a free plan or a non-residential connection, runs will usually return zero items and a notice explaining the upgrade path.

# Actor input Schema

## `mode` (type: `string`):

How to start the scrape. 'search' builds OfferUp searches from your keywords + location + filters. 'url' walks the OfferUp search or detail URLs you paste (filter fields below are ignored in URL mode).

## `searchQueries` (type: `array`):

One or more keywords to search OfferUp for (e.g. 'iphone 14', 'mountain bike', 'sofa'). Each query is scraped independently.

## `location` (type: `string`):

Center the search on a place: a US city ('Miami, FL'), a ZIP code ('33101'), or raw coordinates ('25.7617,-80.1918'). Leave empty to search near the connection's location (roughly nationwide).

## `radiusMiles` (type: `integer`):

Radius in miles around the location to include listings from.

## `priceMin` (type: `integer`):

Only return listings priced at or above this amount.

## `priceMax` (type: `integer`):

Only return listings priced at or below this amount.

## `condition` (type: `array`):

Filter by item condition. Select one or more. Leave empty for any condition.

## `priceDropOnly` (type: `boolean`):

Only keep listings where the seller has lowered the price from OfferUp's own recorded original price (a genuine markdown, not just a low asking price). Only applies when mode = search. Price-drop data lives on the listing detail page, not the search results page, so turning this on auto-enables 'Fetch full details' for the run (and its per-item charge) even if left off below.

## `sortBy` (type: `string`):

Order OfferUp returns results in.

## `urls` (type: `array`):

Paste OfferUp search URLs (https://offerup.com/search?q=...) or item detail URLs (https://offerup.com/item/detail/<id>). Multi-URL supported. Search URLs page forward automatically; filter-mode fields are ignored.

## `fetchDetails` (type: `boolean`):

Visit each listing page to add description, GPS coordinates, full photo set, category tree, fulfillment details and the complete seller profile (rating, items sold, join date, verification). Slower and adds a small per-item charge; leave off for fast basic listings. Always on when 'Price drops only' is set.

## `maxItems` (type: `integer`):

The single cap on this run: stop after collecting this many listings (across all queries/URLs). Set 0 for unlimited.

## `maxPages` (type: `integer`):

Optional safety limit on result pages walked per query. Leave at 0 to walk the whole catalogue: the run stops at Max items, the site's own last page, or a repeat-page guard, not an artificial page cap.

## `resumeFromRunId` (type: `string`):

Optional. ID of a previous run of this actor (or a dataset ID). Listings already in that dataset are skipped, so this run returns only NEW listings (a delta). Combine both runs' datasets for the full set. Max items then counts only the new listings.

## `proxy` (type: `object`):

OfferUp geo-blocks non-US traffic. Use Apify Residential proxy with country US for reliable results.

## `mcpConnectors` (type: `array`):

Optionally send results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize one under Apify → Settings → API & Integrations, then select it here. Notion gets a rich page-per-item export; other connectors get a best-effort write/digest. Leave empty to skip; never changes the dataset output. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "searchQueries": [
    "iphone"
  ],
  "location": "Miami, FL",
  "radiusMiles": 50,
  "priceDropOnly": false,
  "sortBy": "best_match",
  "urls": [
    "https://offerup.com/search?q=sofa&radius=20"
  ],
  "fetchDetails": false,
  "maxItems": 20,
  "maxPages": 0,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  },
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "mode": "search",
    "searchQueries": [
        "iphone"
    ],
    "location": "Miami, FL",
    "radiusMiles": 50,
    "urls": [
        "https://offerup.com/search?q=sofa&radius=20"
    ],
    "maxItems": 20,
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/offerup-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "mode": "search",
    "searchQueries": ["iphone"],
    "location": "Miami, FL",
    "radiusMiles": 50,
    "urls": ["https://offerup.com/search?q=sofa&radius=20"],
    "maxItems": 20,
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/offerup-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "mode": "search",
  "searchQueries": [
    "iphone"
  ],
  "location": "Miami, FL",
  "radiusMiles": 50,
  "urls": [
    "https://offerup.com/search?q=sofa&radius=20"
  ],
  "maxItems": 20,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call abotapi/offerup-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=abotapi/offerup-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/IfuMG5ZLN6OkcqRa2/builds/8IdQcM2C0SMBidr4J/openapi.json
