# OfferUp Vehicles Scraper (`fmchisti/offerup-vehicles-scraper`) Actor

Scrape OfferUp.com Cars & Trucks listings nationwide or for a specific US state.

- **URL**: https://apify.com/fmchisti/offerup-vehicles-scraper.md
- **Developed by:** [Fahim Mahmud Chisti](https://apify.com/fmchisti) (community)
- **Categories:** Automation, Developer tools, Integrations
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 Vehicles Scraper

Scrapes [OfferUp](https://offerup.com/) Cars & Trucks listings into the same vehicle dataset contract used by the eBay, Craigslist, and Autotrader Actors in this monorepo.

### Features

- Nationwide Cars & Trucks browse (`/explore/k/5/1`) or a specific US state (`/explore/sk/{state}/cars-trucks`)
- Keyword searches and custom explore/search/detail start URLs
- Optional detail scraping for VIN, mileage, drivetrain, transmission, colors, seller, and images
- `listedAt` from relative post times when OfferUp shows them
- State verification before saving when a state filter is selected
- Live progress on `/` and `/status` while the run is active
- Debug HTML dumps for empty or failed pages

### Input

| Field                     | Description                                                           |
| ------------------------- | --------------------------------------------------------------------- |
| `searchKeywords`          | Vehicle keywords such as `Honda Civic`                                |
| `startUrls`               | OfferUp explore, search, or item detail URLs                          |
| `locationScope`           | `nationwide` or `state`                                               |
| `state`                   | US state code when scope is `state`                                   |
| `maxItems`                | Cap on saved listings (`0` = unlimited)                               |
| `maxPagesPerSearch`       | Max result pages per keyword/URL                                      |
| `maxListingAgeDays`       | Keep only recently posted listings                                    |
| `scrapeItemDetails`       | Open each item page for richer fields                                 |
| `duplicateCheck`          | Skip detail scraping for listing URLs already known (default `false`) |
| `duplicateCheckApiUrl`    | Optional POST exists API; leave empty for Apify storage only          |
| `duplicateCheckStoreName` | Named KV store (default `vehicle-listing-urls`)                       |
| `proxyConfiguration`      | Use US residential proxies                                            |

Example:

```json
{
    "searchKeywords": ["Toyota Camry"],
    "startUrls": [
        {
            "url": "https://offerup.com/explore/sck/tx/dallas/cars-trucks"
        }
    ],
    "locationScope": "state",
    "state": "TX",
    "maxItems": 50,
    "scrapeItemDetails": true
}
```

### Skip existing listings (duplicate check)

OfferUp detail scraping opens each item in a browser with residential proxies. On scheduled re-runs, most results are often listings you already stored. Duplicate check skips those URLs so you do not pay again for detail pages you already have.

Enable it with `duplicateCheck: true` (default `false`).

If N consecutive listing URLs in a search are known duplicates (N = `duplicateCheckLeadingStop`, default **20**), that search stops (no further pages) to avoid paying for known inventory.

**Modes**

- When **`duplicateCheckApiUrl`** is set, the Actor POSTs batches of up to 500 listing URLs to your endpoint, then skips URLs returned in `existing`. It also reads and updates the named Apify Key-Value store. A URL is skipped if **either** your API or the store marks it as known.
- When **`duplicateCheckApiUrl`** is empty, the Actor uses the **Apify Key-Value store only** (no external API).

**API contract**

```json
// Request
{ "listingUrls": ["https://example.com/listing/1", "https://example.com/listing/2"] }

// Response
{
  "existing": ["https://example.com/listing/1"],
  "missing": ["https://example.com/listing/2"]
}
```

`listingUrls` may also be a single string. URLs are normalized (query string and trailing slash ignored). No auth header is required for public endpoints. If the API call fails or exceeds a **20s timeout**, the Actor fail-opens and scrapes the batch.

After a listing is saved successfully, its URL is written to the named store under the `KNOWN_LISTING_URLS` record so future runs skip it even without an API. Cached URLs expire after **30 days**, and the store is capped at **50,000** entries (oldest first). Legacy boolean `true` entries are migrated to timestamps on load.

Example:

```json
{
    "searchKeywords": ["Toyota Camry"],
    "locationScope": "state",
    "state": "TX",
    "maxItems": 100,
    "duplicateCheck": true,
    "duplicateCheckApiUrl": "https://your-api.example.com/listings/exists",
    "duplicateCheckStoreName": "vehicle-listing-urls"
}
```

### Output

```json
{
    "itemId": "779357ec-5c27-3e26-8210-0c0b189db896",
    "title": "2014 Chevrolet Silverado 1500",
    "listedAt": "2026-07-17T12:00:00.000Z",
    "price": "17899",
    "currency": "USD",
    "year": "2014",
    "make": "Chevrolet",
    "model": "Silverado 1500",
    "mileage": "118621",
    "vin": "3GCUKSEC1EG174897",
    "location": "Plano, TX",
    "seller": "Example Seller",
    "imageUrl": "https://images.offerup.com/example.jpg",
    "images": ["https://images.offerup.com/example.jpg"],
    "url": "https://offerup.com/item/detail/779357ec-5c27-3e26-8210-0c0b189db896"
}
```

When the Actor is started from an Apify Task, each dataset item also includes `taskId` and `taskName` so you can track which Task produced it. Direct Actor runs set both to `null`.

### Tips

- Keep the default US residential proxy. OfferUp redirects non-US traffic to a geolocation unavailable page.
- Prefer Cars & Trucks explore URLs for category-focused crawls.
- Enable detail scraping when you need VIN/specs or reliable state filtering.
- Enable `duplicateCheck` on scheduled re-runs to skip listings you already stored.
- Bound cost with `maxItems` and `maxPagesPerSearch`.
- Set `scrapeItemDetails: false` when search-card fields are enough.
- Prefer skipping known listings over raising memory.
- Keep browser concurrency low.

### Legal notice

Scrape only public information and comply with applicable law, OfferUp's terms, and reasonable request rates. The Actor does not intentionally collect private contact information.

# Actor input Schema

## `searchKeywords` (type: `array`):

Used only when Start URLs are empty. Vehicle keywords such as Toyota Camry, Ford F-150, or Honda Civic. Each keyword creates a separate OfferUp search.

## `startUrls` (type: `array`):

OfferUp Cars & Trucks explore/search URLs or direct item detail URLs. When provided, the Actor crawls only these URLs and ignores keywords. City and category paths are preserved.

## `locationScope` (type: `string`):

Search nationwide OfferUp inventory or restrict saved results to one US state.

## `state` (type: `string`):

Required when Location scope is Specific state. Results are verified against listing location before being saved.

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

Maximum listings to save across all searches. Set to 0 for no item limit.

## `maxPagesPerSearch` (type: `integer`):

Maximum OfferUp result pages to process for each keyword or start URL.

## `maxListingAgeDays` (type: `integer`):

Optional. Save only listings with a known posted date within this many days. Listings without a date are excluded.

## `scrapeItemDetails` (type: `boolean`):

Open each listing for VIN, mileage, specs, seller, description, and images. Recommended for state filtering.

## `duplicateCheck` (type: `boolean`):

When enabled, skip detail scraping for listing URLs already known from your duplicate-check API and/or a named Apify Key-Value store. Saves proxy and compute cost on re-runs.

## `duplicateCheckApiUrl` (type: `string`):

Optional. POST endpoint that accepts { "listingUrls": string|string\[] } and returns { "existing": string\[], "missing": string\[] }. Leave empty to use Apify Key-Value store only.

## `duplicateCheckStoreName` (type: `string`):

Named Apify Key-Value store that remembers listing URLs across runs. Used whenever Skip existing listings is enabled.

## `duplicateCheckLeadingStop` (type: `integer`):

When Skip existing listings is enabled, stop that search after this many consecutive listing URLs are known duplicates (in a row). Default 20. Lower to stop sooner; raise to keep scanning longer.

## `proxyConfiguration` (type: `object`):

US residential proxies are required. OfferUp blocks non-US and many datacenter IPs with geolocation checks.

## Actor input object example

```json
{
  "searchKeywords": [],
  "startUrls": [
    {
      "url": "https://offerup.com/explore/k/5/1"
    }
  ],
  "locationScope": "nationwide",
  "maxItems": 5,
  "maxPagesPerSearch": 1,
  "scrapeItemDetails": false,
  "duplicateCheck": false,
  "duplicateCheckApiUrl": "https://ccscraperapi.up.railway.app/api/listings/exists",
  "duplicateCheckStoreName": "vehicle-listing-urls",
  "duplicateCheckLeadingStop": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `scrapeState` (type: `string`):

No description

## `debugItems` (type: `string`):

No description

## `debugPages` (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 = {
    "searchKeywords": [],
    "startUrls": [
        {
            "url": "https://offerup.com/explore/k/5/1"
        }
    ],
    "maxItems": 5,
    "maxPagesPerSearch": 1,
    "scrapeItemDetails": false,
    "duplicateCheckApiUrl": "https://ccscraperapi.up.railway.app/api/listings/exists",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("fmchisti/offerup-vehicles-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 = {
    "searchKeywords": [],
    "startUrls": [{ "url": "https://offerup.com/explore/k/5/1" }],
    "maxItems": 5,
    "maxPagesPerSearch": 1,
    "scrapeItemDetails": False,
    "duplicateCheckApiUrl": "https://ccscraperapi.up.railway.app/api/listings/exists",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("fmchisti/offerup-vehicles-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 '{
  "searchKeywords": [],
  "startUrls": [
    {
      "url": "https://offerup.com/explore/k/5/1"
    }
  ],
  "maxItems": 5,
  "maxPagesPerSearch": 1,
  "scrapeItemDetails": false,
  "duplicateCheckApiUrl": "https://ccscraperapi.up.railway.app/api/listings/exists",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call fmchisti/offerup-vehicles-scraper --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/5WpGuBu1IBpiMPfpV/builds/hhjcF4WqNE5967Rcu/openapi.json
