OfferUp Scraper - Listings, Prices, Sellers & Location Data avatar

OfferUp Scraper - Listings, Prices, Sellers & Location Data

Pricing

from $1.00 / 1,000 listing results

Go to Apify Store
OfferUp Scraper - Listings, Prices, Sellers & Location Data

OfferUp Scraper - Listings, Prices, Sellers & Location Data

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.

Pricing

from $1.00 / 1,000 listing results

Rating

0.0

(0)

Developer

Abot API

Abot API

Maintained by Community

Actor stats

0

Bookmarked

7

Total users

1

Monthly active users

3 hours ago

Last modified

Share

OfferUp Scraper: Listings, Sellers, Prices & Category Data

OfferUp Scraper turns OfferUp, the US mobile-first local marketplace, into structured JSON. Search by keyword and location, or paste OfferUp search and item links, and get price, condition, geo coordinates, a full photo set, a three-level category tree and a complete seller profile per listing. Five native sort orders, price and condition filters, genuine price-drop detection, resumable runs and Incremental mode for recurring monitoring are all built in, ready to export to JSON, CSV or Excel, or pull through the API.

Why This Scraper?

  • Two ways in. Keyword search with location, radius, price and condition filters, or paste OfferUp search and item URLs directly.
  • 40+ fields when Fetch full details is on, including GPS coordinates, the full photo set, a three-level category tree and a complete seller profile (rating, items sold, join date, response time, verification).
  • Five native sort orders, plus price range and multi-condition filtering, applied at the source rather than filtered after the fact.
  • Genuine price-drop detection. Compares OfferUp's own recorded original price against the current price, not just a low asking price.
  • Walks the whole catalogue by default. Max pages defaults to unlimited: the run walks every result page per query/URL until Max items, OfferUp's own last page, or a repeat-page guard stops it.
  • Resumable, delta-friendly runs. Point Resume from a previous run at an earlier run or dataset to collect only what it doesn't already have, and Incremental mode tracks a recurring search itself and tags what changed.
  • Checkpointed automatically. A platform migration or a manual restart of an interrupted run picks up where it left off instead of re-collecting and re-charging listings already pushed.

Use Cases

  • Resale and arbitrage: monitor a category or keyword for genuine seller price drops to spot flip opportunities before they're gone.
  • Local market research: track pricing and condition mix for a product category across cities, ZIP codes or radii.
  • Recurring seller/category watch: schedule a search with Incremental mode on and review only new, updated or disappeared listings.
  • Lead generation for local services: pull listings in categories like furniture, electronics or vehicles alongside seller responsiveness and verification signals.
  • Marketplace data science: build a dataset of local pricing behavior over time using scheduled Incremental runs.

Data You Get

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

FieldExample
listingId00000000-0000-0000-0000-000000000000
titleSample Listing Title
price799
priceText"$799"
isFirmPricefalse
conditionTextUsed
locationNameMiami, FL
urlhttps://offerup.com/item/detail/00000000-0000-0000-0000-000000000000
imageUrlhttps://images.offerup.com/000000000-254x250.jpg
flags["LOCAL_PICKUP"]
description (when Fetch full details is on)Full item description text.
latitude / longitude (when enabled)25.0000 / -80.0000
distanceMiles (when enabled)4.9
categoryL1Name / categoryL2Name / categoryL3Name (when enabled)Electronics & Media / Cell phones & Accessories / Smartphones
photos (when enabled)[{ "uuid": "0000", "url": "https://images.offerup.com/000-1440x1920.jpg" }]
postDate (when enabled)2026-01-01T00:00:00.000Z
originalPrice / isOnSpecial (when enabled)899 / true
savingsAmount / discountPercent (when enabled)100 / 11
localPickupEnabled / shippingEnabled (when enabled)true / false
sellerName (when enabled)Jane Seller
sellerRating / sellerReviewCount (when enabled)5 / 89
sellerItemsSold / sellerDateJoined (when enabled)564 / 2016-01-01T00:00:00Z
sellerResponseTime (when enabled)Responds in a few minutes
sellerIsTruYou (when enabled)true

The remaining fields cover secondary detail: conditionCode (numeric), quantity, photoCount, badges, discussionCount, isLocal, isMerchantItem, canShipToBuyer, shippingPrice, categoryL1Id/categoryL2Id/categoryL3Id, sellerId, sellerAvatar, sellerPrimaryBadge/sellerSecondaryBadge, sellerItemsPurchased, sellerPublicLocation, sellerIsAutosDealer, sellerIsBusinessAccount, sellerIsPremium, sellerWebsiteLink, and sellerPhone when OfferUp exposes it on the seller's profile. All of these "when enabled" fields need Fetch full details on (search mode) or come from the detail page automatically (a pasted item URL, or Price drops only). vehicleMiles (vehicle listings only) comes from the search-result card instead, so it's populated in search mode without needing Fetch full details, and is null on a pasted item URL. One more distinction worth knowing: a pasted single item URL always reads the detail page itself, so priceText, imageUrl, imageWidth, imageHeight and vehicleMiles come back null and flags comes back [], since those fields exist only on search-result cards, which a direct item-page fetch never produces.

How to Use

  1. Pick a mode: search (keyword + location) or url (paste OfferUp search or item links).
  2. Fill in the fields for that mode, and switch on Fetch full details if you need description, GPS, category and seller data.
  3. Set Max items to control run size and cost, then click Start.
  4. Download the dataset as JSON, CSV or Excel, or read it through the API.

Search a city with filters:

{
"mode": "search",
"searchQueries": ["iphone 14"],
"location": "Miami, FL",
"radiusMiles": 30,
"priceMin": 200,
"priceMax": 600,
"condition": ["USED", "REFURBISHED"],
"sortBy": "-price",
"maxItems": 50
}

Paste OfferUp URLs (a search page and an item page), with full details:

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

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

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

Recurring monitoring of the same search (Incremental mode), including EXPIRED detection:

{
"mode": "search",
"searchQueries": ["iphone 14"],
"location": "Miami, FL",
"incrementalMode": true,
"emitExpired": true,
"maxItems": 0
}

Run it from your code

Python:

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/offerup-scraper").call(run_input={
"mode": "search",
"searchQueries": ["iphone 14"],
"location": "Miami, FL",
})
for listing in client.dataset(run["defaultDatasetId"]).iterate_items():
print(listing["title"], listing["price"])

JavaScript:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/offerup-scraper').call({
mode: 'search',
searchQueries: ['iphone 14'],
location: 'Miami, FL',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.

Max items is the real cap, Max pages is just a safety bound

maxItems is the single stopping point for a run (across every query/URL); set it to 0 for unlimited. maxPages (also 0 by default) is only an optional per-query ceiling on how many result pages get walked; it never raises the cap set by maxItems, it can only lower it. Leave maxPages at 0 to walk an entire query to its natural end.

Resume and recurring updates

  • Resume (resumeFromRunId) continues one interrupted or earlier run: paste its run or dataset id and this run skips every listing it already holds, so you don't pay twice. Progress is also checkpointed automatically during a run, so a platform migration or a restart of an interrupted run resumes exactly where it stopped.
  • Incremental mode (incrementalMode) is for scheduled runs over the same search. Each listing is classified NEW, UPDATED (with changedFields), or UNCHANGED (suppressed and not billed unless emitUnchanged is on) by default. A listing is only marked gone (and an EXPIRED row emitted) when emitExpired is on and the run completed a full, uncapped scan of the tracked search, never on a Resume, a Max items cap, or a run stopped by an error; REAPPEARED therefore requires a prior run like that, and never happens with emitExpired off. stateKey names or shares the stored baseline; leave it empty to auto-derive one from your search/URLs and filters. Five fields (discussionCount, sellerLastActive, sellerResponseTime, distanceMiles, isLocal) are excluded from change detection because they drift on their own between runs, independent of the listing itself; their real current values are still returned in every row. Rotating query strings on image URLs (imageUrl, photos[].url, photos[].listUrl, sellerAvatar) are also not compared, so a CDN parameter change is never an UPDATE. Add your own with ignoreFieldsForChanges (top-level names or dotted paths such as photos.listUrl); UNCHANGED is decided by re-checking the stored record with the current ignore list, so changing it does not cause a wave of UPDATED rows. A pasted URL starting at ?page=2 or later never produces EXPIRED rows. With Incremental mode off, output is exactly as before.

URL shapes (url mode)

Behaviour change: in url mode, leaving the URLs field untouched now scrapes one example of each supported shape below instead of nothing. Replace the list with your own links to scrape only those.

ShapeExample
Keyword searchhttps://offerup.com/search?q=sofa&radius=20
Keyword search, trailing-slash formhttps://offerup.com/search/?q=sofa
Bare search (Offers Nearby)https://offerup.com/search
Paginated search (starts at that page)https://offerup.com/search?q=sofa&radius=20&page=2
Search with category slughttps://offerup.com/search?q=sofa&radius=20&category=home-and-garden
Filtered / sorted searchhttps://offerup.com/search?q=sofa&SORT=-posted&PRICE_MIN=10&PRICE_MAX=500&CONDITION=40&delivery_param=p
Top-level category pagehttps://offerup.com/c/home-and-garden
Category keyword pagehttps://offerup.com/c/furniture/sofa
Legacy explore keywordhttps://offerup.com/explore/k/sofa
Item detail pagehttps://offerup.com/item/detail/<id>

Upper-case facet keys (SORT, PRICE_MIN, PRICE_MAX, CONDITION) are read the same as the lower-case ones. A pasted &page=2 URL never produces EXPIRED rows. Bare listing ids can be given in listingIds (or inside urls); when only ids are given the nine default example URLs are not scraped. Item detail pages are subject to the bot challenge described under Plan Requirement. Other OfferUp pages (for example rentals or jobs) and non-OfferUp URLs are skipped with a warning in the run log, never silently.

Views and output tabs

The dataset has three views: Overview (id, title, price, condition, location, image), Detail (description, category tree, GPS, seller profile, photos) and Changes (changeType, changedFields, firstSeenAt, lastSeenAt for Incremental mode). Every field is typed and nullable in the dataset schema, so a field OfferUp does not provide is null rather than missing or malformed.

Send results into your apps (MCP connectors)

Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape: the Apify dataset is never changed.

What gets written to the connector: a condensed, human-readable summary of each listing, not the full JSON. Each item becomes one entry with a title and its key fields (price, condition, location, category, seller, URL) flattened to plain text. The complete record always stays in the Apify dataset.

  1. Authorize a connector once under Apify β†’ Settings β†’ Integrations (Notion, Linear, Airtable, or Apify).
  2. Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
  3. For Notion, also set notionParentPageUrl to the page where items should be created.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

Input Parameters

ParameterTypeDefaultDescription
modestringsearchsearch (keyword + location) or url (paste OfferUp URLs); filter fields below are ignored in url mode.
searchQueriesarray["iphone"]Keywords to search (search mode). Each query is scraped independently.
locationstringno default (prefill: Miami, FL)City ('Miami, FL'), ZIP ('33101'), or raw 'lat,lon'. Empty searches near the connection's own location.
radiusMilesinteger50Radius in miles around the location (1 to 500).
priceMinintegerno defaultOnly return listings priced at or above this amount (USD).
priceMaxintegerno defaultOnly return listings priced at or below this amount (USD).
conditionarrayno defaultFilter by condition: NEW, OPEN_BOX, REFURBISHED, USED, BROKEN, OTHER. Leave empty for any condition.
priceDropOnlybooleanfalseKeep only listings with a genuine seller price drop (search mode). Auto-enables fetchDetails (and its per-item charge) even if left off, since price-drop data only lives on the detail page.
sortBystringbest_matchbest_match, -posted (newest), distance (closest), price (low to high), -price (high to low).
listingIdsarrayno defaultBare OfferUp listing ids or item detail URLs; each is read from its detail page (billed the detail-enrichment surcharge). Whenever ids are given the run reads them in URL mode, whatever Mode says; the default sample URLs and the default search keyword are not run, while URLs you typed yourself in urls are still scraped alongside the ids.
urlsarrayone example of each supported shape (see URL shapes below)OfferUp search, category or item detail URLs (url mode). Search URLs page forward automatically; a &page=N URL starts at page N.
fetchDetailsbooleanfalseVisit each listing page for description, GPS, full photo set, category tree, fulfillment details and the complete seller profile. Adds a per-item charge. Always on when priceDropOnly is set.
maxItemsinteger20Stop after this many listings total (across all queries/URLs). 0 = unlimited.
maxPagesinteger0Optional safety cap on result pages walked per query/URL. 0 walks the whole catalogue until maxItems, the site's own last page, or a repeat-page guard stops it.
resumeFromRunIdstringno defaultID of a previous run (or dataset) of this actor. Listings already in that dataset are skipped, so this run returns only new ones.
incrementalModebooleanfalseFor a search you run on a schedule: tracks this search's own listings between runs and tags each row's changeType as NEW / UPDATED / UNCHANGED / REAPPEARED / EXPIRED.
stateKeystringno defaultOptional label naming the tracked Incremental mode baseline. Leave empty to auto-derive one from your search/URLs and filters.
ignoreFieldsForChangesarray[]Incremental mode: extra field names or dotted paths excluded from change detection (still returned in rows).
emitUnchangedbooleanfalseIncremental mode: also return and bill listings with no change since the last run.
emitExpiredbooleanfalseIncremental mode: also return and bill a row for each previously tracked listing no longer found, only once a run completes a full scan (never on Resume, a Max items cap, or an error-stopped scan).
proxyobjectResidential USConnection settings. OfferUp only serves US connections. Residential with country US is the default; search and category pages also load over a US datacenter connection. The actor reads your proxy choice first and falls back to a backup connection if it is refused.
mcpConnectorsarrayno defaultOptional: send a summary of each listing to apps you authorized under Integrations.
notionParentPageUrlstringno defaultNotion connector only: page under which item pages are created.
maxNotifyListingsinteger50Cap on items exported to each connector per run. Does not affect the dataset.

Output Example

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

{
"listingId": "00000000-0000-0000-0000-000000000000",
"url": "https://offerup.com/item/detail/00000000-0000-0000-0000-000000000000",
"title": "Sample Listing Title",
"price": 799,
"priceText": "$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",
"categoryL3Name": "Smartphones",
"description": "Full item description text.",
"originalPrice": 899,
"isOnSpecial": true,
"savingsAmount": 100,
"discountPercent": 11,
"photos": [{ "uuid": "0000", "url": "https://images.offerup.com/000-1440x1920.jpg", "width": 1440, "height": 1920 }],
"photoCount": 1,
"postDate": "2026-01-01T00:00:00.000Z",
"localPickupEnabled": true,
"shippingEnabled": false,
"sellerName": "Jane Seller",
"sellerRating": 5,
"sellerReviewCount": 89,
"sellerItemsSold": 564,
"sellerDateJoined": "2016-01-01T00:00:00Z",
"sellerResponseTime": "Responds in a few minutes",
"sellerIsTruYou": true
}

With incrementalMode on, every row also carries changeType (NEW / UPDATED / UNCHANGED / REAPPEARED / EXPIRED), changedFields, firstSeenAt and lastSeenAt.

Plan Requirement

OfferUp only serves US connections and geo-blocks non-US exits (a non-US connection gets a "Geolocation Unavailable" page). The default proxy is Apify Residential with country US, but it is not strictly required: in our tests search pages, category pages and the bare search page also loaded over an Apify datacenter connection that exits in the US. The run reads your proxy choice first. If that connection is refused for your account (for example residential on a free plan) the run logs the notice "The selected connection is unavailable to this run" and continues on a backup connection; pages may still be unavailable there. If no page can be loaded at all the run fails with a clear message instead of an empty dataset.

Listing detail pages (Fetch full details, Price drops only, pasted item URLs and listing ids) are protected by a bot challenge that we measured on every connection tier. When a detail page is served that challenge the run logs "A listing detail page was served a bot challenge on this connection; its details were skipped and no detail-enrichment charge applies to it." The search-card fields are still returned where available, and detail enrichment is not billed for that listing. Expect detail-based fields to be missing or intermittent until OfferUp relaxes the challenge.

FAQ

How much does it cost?

Pricing is pay per event: a start event per run, a dataset-item event for each listing returned, and a detail-enrichment event for each listing whose detail page was actually read when Fetch full details (or Price drops only, which turns it on automatically, or a pasted item URL or listing id) is active. A detail page that was challenged or failed is not charged. The Pricing tab shows the current rates. Use Max items to cap the cost of any run.

This actor collects only publicly available marketplace listings. You are responsible for how you use the data: follow OfferUp's terms and the laws that apply to you, get legal advice before any commercial use, and never use seller names, locations or contact details in ways a seller didn't invite (for example unsolicited outreach).

Can I get only new or changed listings on a schedule?

Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only NEW and UPDATED rows for that search by default; unchanged listings are skipped unless Emit unchanged listings is on, and turning on Emit expired listings also gets you EXPIRED rows once a run completes a full scan, plus REAPPEARED rows on a later run for a listing that was marked gone that way.

Why does pasting one item URL cost more than a plain search listing?

A pasted item URL can only be read from its own detail page, so that fetch always happens and is always billed the detail-enrichment surcharge, even though Fetch full details defaults to off for search mode. A search-mode listing only gets that fetch (and charge) when Fetch full details or Price drops only is turned on. A pasted item URL record also never carries priceText, imageUrl, the image dimension fields or vehicleMiles, and its flags comes back empty, because those only ever come from a search-result card, not the detail page.

Why did my run return an empty dataset instead of failing?

A run that cannot load any page ends as failed with a clear status message (a connection problem, or the detail-page challenge, not an empty search), so you can fix the connection and rerun rather than parse a generic error. The messages are: "The proxy connection was refused for this account, so no page could be read" (your plan has no proxy access), "No results: residential proxies are unavailable on this run ... and the backup connection could not load the requested OfferUp pages", "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search.", and "Every requested listing detail page was served a bot challenge; nothing was collected or charged." (a run that only read detail pages, for example listing ids, and every one was challenged: the connection works, use a search or category URL) A run that returns zero rows without failing means the search genuinely matched nothing. Also check the run log for the detail-page challenge message described under Plan Requirement. The run does fail outright, with a clear message, when Resume from a previous run points at an id that can't be read, or when Incremental mode already has saved state for a search and you also set Resume from a previous run against it.

Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push listing summaries into Notion, Linear or Airtable.

πŸ”— Want more e-commerce data?

Pair this actor with these related scrapers from the same team:

🏷️ Kijiji.ca Scraper
Scrape Kijiji.ca listings across property, vehicles, jobs, electronics, furniture...
🏷️ Marktplaats.nl Scraper
Scrape Marktplaats.nl listings by keyword or URL. Extract title, price, condition...
πŸš— Cars.com Scraper
Scrape cars.com listings by make, ZIP and radius. 90+ fields per car: price, MSRP...
🏷️ Daangn Scraper
Scrape Daangn (λ‹Ήκ·Ό) marketplace listings by keyword and region. Returns 35+ fields per...
πŸ›’ Depop Scraper
Scrape Depop search results, listings, shops, and reviews to JSON, CSV, or Excel. Extract...
πŸ›’ Mercari Japan Scraper
Scrape Mercari Japan listings, sellers and reviews at scale. Extract names, prices...

πŸ‘‰ Browse all abotapi scrapers

πŸ’¬ Support

  • 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
  • πŸ› οΈ Need another site, extra fields or a private build? Email contact@abotapi.com.
  • ⭐ Enjoying it? A quick review on the actor page helps other users find it.