Looking for the overview — what this API returns, what it costs, and a call you can run without a key? See the OfferUp API page →
E-commerce & Marketplaces

OfferUp API & Scraper

The OfferUp API returns the large US local classifieds marketplace as clean JSON, in two actions: search and product/detail.

2 actionsLive JSON1,000 free credits$0.67–$1.50 / 1,000 creditsMCP-ready
Get a free keyOpen in playground

🤖 Using an AI assistant? Copy this link into ChatGPT / Claude / Cursor — it reads every endpoint and parameter instantly and tells you if this API fits your use case.

A search is one upstream request at 24.2 KB and a median 875 ms, and a product record is 3.0 KB at 685 ms; across 128 live engine calls in three runs there were 0 unexpected failures. Every search row carries the listing id and URL, the title, the price as a number in USD with OfferUp's own formatted string beside it, the town, the photo and the tile flags - counted by value across 704 rows in 8 categories and 2 locations, every one of those fields was filled on every row except the vehicle prices OfferUp deliberately hides on a tile. product/detail adds the seller's own description, the condition with OfferUp's own code, the posted date, the full-size photo gallery, the category with its whole path, the listing's coordinates, the pickup and shipping terms, the vehicle attributes when it is a vehicle, and the seller's public profile - display name, items sold and bought, join date, stated response time and verification badges. The price was cross-checked against each listing page's own structured data on 14 listings: 14 match, 0 mismatch. No OfferUp account, no browser, no login - one ReefAPI key and the standard { ok, data, meta, error } envelope.

Reference

What scopes a search is the coordinates you send - not your IP, and not a URL parameter

This is the single most important thing to know before you build against OfferUp, and it is the thing most callers get wrong first. OfferUp assigns EVERY exit the geographic centre of the United States as a fallback location, so an un-scoped search returns Kansas no matter where the call comes from. All five rows below were measured from ONE exit inside ONE minute on 2026-09-06 - the only thing that changed was the location sent with the query.

Location sent with the queryTowns that came back
nothingWichita KS, Pretty Praire KS - OfferUp's default US centre, not your location
40.7484 / -73.9857 (New York)Parlin NJ, Edison NJ, Union Beach NJ, Matawan NJ, New York NY
34.0522 / -118.2437 (Los Angeles)South Gate CA, Rosemead CA, South Pasadena CA, Garden Grove CA
25.7617 / -80.1918 (Miami)Miami FL, Ind Crk Vlg FL, Fort Lauderdale FL
47.6062 / -122.3321 (Seattle)Burien WA, Everett WA, Lynnwood WA, Fall City WA

A ZIP code works too and is resolved by OfferUp's own geocoder rather than by a table of ours, at the cost of one extra request - a live call on 98101 came back scoped to 47.6067 / -122.3354, Seattle WA, with 16 of 44 rows in Seattle itself. The response always reports which of the three happened, so you are never guessing whether your location took: the location block names the coordinates actually used and a source of zip_code, coordinates, or offerup_default_us_centre. Free-text place names are deliberately NOT offered, because OfferUp's geocoder refuses that arm without a bias position - it would have been a parameter that silently did nothing.

Live example

Real request and response JSON

Captured from the indexed primary action, search, on .

Captured request
{
  "method": "POST",
  "url": "https://api.reefapi.com/offerup/v1/search",
  "headers": {
    "x-api-key": "$REEF_KEY",
    "content-type": "application/json"
  },
  "body": {
    "query": "couch",
    "latitude": 34.0522,
    "longitude": -118.2437
  }
}
Captured response
{
  "ok": true,
  "meta": {
    "api": "offerup",
    "endpoint": "search",
    "mode": "live",
    "latency_ms": 1014.4,
    "record_count": 44,
    "bytes": 25548,
    "cache_hit": false,
    "pagination": {
      "cursor": "H4sIAAAAAAAAAI1WW49bxw3-K4WeTWDuF78F6zYw4MRGkz4EsbHgDMm1kM1K1ZGauMH-93yj3bRBEAR-kc6FwyG_C-f8stuUT_PjN7pt-8PDa9m93G2495V9dD2U7nvSmFoafbrQm1Uno7rdi-eF_1CVm8tpO5yw8pf3u4PZpuf3u5cpPSLI8PqbM5_Ou5fZvdgdT_qf_eGyveM7fbPfzvuHu9ey7V5-v6u-5VB8IpndKGYxGloy1RQsiCsxjoJ8NTcOzQ1KSQrFFI16cYlcmma5ZAlhICxxn6wV2XzxFJsrNLzzlDTHmmLNfQSEtZYc22SqapNi6JNGnEpWfcmp2fDNXzfVIdUbTW-ozfVCHHKlLj7Y9Kwc0grjNrT6ihwtrNoiceVETVp2gvrzbAjLWqa4XomLCMXBTM1moqBes9baI0eEFRnFFx4UY8z4ASptiCPOtdRWXO6yWkgJlfgySVxem6IZZjbKVsosvehwK0ydWEzFSGJSir51tBDqwriKpoAltlooMtvMjYI17Ny00Zg1U0vahEdikUW9CFrCQiqAnaJpIEDpqUosVVvoyIMwZ67NOSOV6NBpckhklsjjoVoa",
      "has_more": true,
      "page_size": 44,
      "total": null
    }
  },
  "data": {
    "results": [
      {
        "listing_id": "71852614-dc9f-35df-be65-742f2d0633b6",
        "title": "Loveseat Sofa Couch",
        "price": 240,
        "price_display": "$240",
        "currency": "USD",
        "call_for_price": false,
        "is_firm_price": null,
        "condition": null,
        "location": {
          "name": "[redacted-name]",
          "latitude": null,
          "longitude": null
        },
        "image": {
          "url": "https://images.offerup.com/cF6uBoNugi8YyewmhrbH7UxIQFY=/253x250/3599/3599056cd7a54c10ad5c103197e58838.jpg",
          "width": 253,
          "height": 250
        },
        "vehicle_miles": null,
        "flags": [
          "LOCAL_PICKUP"
        ],
        "url": "https://offerup.com/item/detail/71852614-dc9f-35df-be65-742f2d0633b6",
        "seller": null,
        "rating": null,
        "review_count": null,
        "rating_scale": null,
        "position": 1
      },
      {
        "listing_id": "758a280b-44d6-343f-9604-04cff565d22b",
        "title": "Modular Sectional Sofa Couch",
        "price": 1350,
        "price_display": "$1,350",
        "currency": "USD",
        "call_for_price": false,
        "is_firm_price": null,
        "condition": null,
        "location": {
          "name": "[redacted-name]",
          "latitude": null,
          "longitude": null
        },
        "image": {
          "url": "https://images.offerup.com/WZiRf0hh3ZKxyp0yQxY_uiEHnt4=/250x250/339c/339cbc0403bc4506bf99fc9eb9ab945e.jpg",
          "width": 250,
          "height": 250
        },
        "vehicle_miles": null,
        "flags": [
          "LOCAL_PICKUP"
        ],
        "url": "https://offerup.com/item/detail/758a280b-44d6-343f-9604-04cff565d22b",
        "seller": null,
        "rating": null,
        "review_count": null,
        "rating_scale": null,
        "position": 2
      },
      {
        "listing_id": "4a9cae74-d161-3806-b101-4e53743759b2",
        "title": "Love seat and sofa",
        "price": 0,
        "price_display": "$0",
        "currency": "USD",
        "call_for_price": false,
        "is_firm_price": null,
        "condition": null,
        "location": {
          "name": "[redacted-name]",
          "latitude": null,
          "longitude": null
        },
        "image": {
          "url": "https://images.offerup.com/vCksVrKDX6E8l0y5YVHBU7fqZzc=/333x250/56ba/56ba6abcf1054e569fda47d47b3f4381.jpg",
          "width": 333,
          "height": 250
        },
        "vehicle_miles": null,
        "flags": [
          "LOCAL_PICKUP"
        ],
        "url": "https://offerup.com/item/detail/4a9cae74-d161-3806-b101-4e53743759b2",
        "seller": null,
        "rating": null,
        "review_count": null,
        "rating_scale": null,
        "position": 3
      }
    ],
    "count": 44,
    "next_cursor": "H4sIAAAAAAAAAI1WW49bxw3-K4WeTWDuF78F6zYw4MRGkz4EsbHgDMm1kM1K1ZGauMH-93yj3bRBEAR-kc6FwyG_C-f8stuUT_PjN7pt-8PDa9m93G2495V9dD2U7nvSmFoafbrQm1Uno7rdi-eF_1CVm8tpO5yw8pf3u4PZpuf3u5cpPSLI8PqbM5_Ou5fZvdgdT_qf_eGyveM7fbPfzvuHu9ey7V5-v6u-5VB8IpndKGYxGloy1RQsiCsxjoJ8NTcOzQ1KSQrFFI16cYlcmma5ZAlhICxxn6wV2XzxFJsrNLzzlDTHmmLNfQSEtZYc22SqapNi6JNGnEpWfcmp2fDNXzfVIdUbTW-ozfVCHHKlLj7Y9Kwc0grjNrT6ihwtrNoiceVETVp2gvrzbAjLWqa4XomLCMXBTM1moqBes9baI0eEFRnFFx4UY8z4ASptiCPOtdRWXO6yWkgJlfgySVxem6IZZjbKVsosvehwK0ydWEzFSGJSir51tBDqwriKpoAltlooMtvMjYI17Ny00Zg1U0vahEdikUW9CFrCQiqAnaJpIEDpqUosVVvoyIMwZ67NOSOV6NBpckhklsjjoVoa",
    "total_results": null,
    "location": {
      "latitude": 34.0522,
      "longitude": -118.2437,
      "zip_code": null,
      "city": null,
      "state": null,
      "source": "coordinates",
      "radius_miles": 30
    },
    "query": "couch",
    "category": null,
    "sort": "best_match",
    "ad_tiles_dropped": 6,
    "duplicate_rows_dropped": 0
  }
}
Actions

What the OfferUp API does

ActionDescriptionConcrete use caseKey params
searchSearch OfferUp listings near a US location by keyword, by category, or both. Returns one row per listing with its id, title, numeric price, town, photo and OfferUp's own tile flags — about 44-50 rows per call — plus a `next_cursor` for the next page. The condition and whether the price is firm are NOT on a feed tile (measured 0 of 704 rows); `product/detail` fills both. Location is what scopes the query: give `zip_code` (resolved by OfferUp's own geocoder) or `latitude`+`longitude`. Use the returned `listing_id` with `product/detail` for the description, the photo gallery and the seller.Pricing teams call search to search OfferUp listings near a US location by keyword, by category, or both.query, category, zip_code, latitude, longitude, ...
product/detailThe full OfferUp listing record by `listing_id` (or listing URL): title, the seller's own description, numeric price and whether it is firm, condition, posted date, the complete photo gallery at full size, the category with its whole path, the listing's coordinates and town, pickup/shipping terms, vehicle attributes when it is a vehicle, and the seller's PUBLIC profile — display name, how many items they have sold and bought, when they joined, their stated response time and their verification badges.Marketplace operators call product/detail to get the full OfferUp listing record by `listing_id` (or listing URL).listing_id, max_rotations
Code samples

Call search from your stack

curl -X POST https://api.reefapi.com/offerup/v1/search \
  -H "x-api-key: $REEF_KEY" \
  -H "content-type: application/json" \
  -d '{"query":"couch","latitude":34.0522,"longitude":-118.2437}'
MCP one-liner
Ask your MCP-connected assistant: call reefapi.offerup.search with {"query":"couch","latitude":34.0522,"longitude":-118.2437}.
Use cases

Who uses this API and why

  • Local-market pricing tools scope a search to a metro with coordinates or a ZIP and read the whole 44-row feed at 24 KB a call.
  • Resale and arbitrage teams sweep a category across several US metros from one exit, since the location travels in the request rather than in the proxy.
  • Auto-listing aggregators pull dealer inventory from the vehicles categories and call product/detail for the mileage, transmission, title status and the real price on the rows where the tile hides it.
  • Marketplace research teams walk a query with the cursor - 184 distinct listings over 4 pages with zero overlap - and enrich each id with the seller's public sold count, join date and badges.
FAQ

Questions developers ask before integrating

Why is my search returning results from Kansas?

Because you did not send a location, and OfferUp's fallback is the geographic centre of the United States - Pretty Prairie, Kansas. It applies that fallback to every exit, so the IP the call comes from does not scope anything, and neither do the lat and lon parameters on OfferUp's own search URL, which are ignored. Send zip_code, or latitude and longitude, on the search action. The response tells you which it used and what coordinates it resolved to, so a mis-scoped search is visible in the payload rather than something you discover from the towns three days later.

Does pagination actually return new listings?

Yes, and it was checked by comparing id sets rather than by trusting the cursor. Pass the next_cursor from one response back as the cursor on the next call: 4 pages of one query returned 44 + 44 + 49 + 47 = 184 rows and 184 DISTINCT listing ids, with zero overlap against everything already seen. There is no page NUMBER - OfferUp pages this feed by cursor only - and there is no result total either, so total_results is null rather than a guess. Two things not to assume: the page size is OfferUp's, not yours (a rows-per-call parameter is not offered because it does nothing upstream - 5, 10, 20, 50 and 100 all returned the same roughly 50-tile page), so read count; and the cursor belongs to the query that produced it, so change a filter and start a fresh walk.

Can I get a seller's rating or reviews?

No, and this one is worth stating plainly because it is the field people expect. OfferUp's schema has a reviews object on a user profile and it measured null on 12 of 12 profiles, including one furniture seller with 10,769 items sold and another whose own display name is "5 Star Seller". So the seller rating, the review count and the rating scale are all null rather than a fabricated number. What OfferUp does publish comes back instead, and it is a better reputation signal than an absent rating: items sold, items purchased, the join date, the stated response time, and the verification badge strip - "95% reply rate", "Confirmed phone", "Confirmed email" - as labels only, never as contact details.

Why do some vehicle listings have no price?

Because a dealer asked OfferUp to hide it on the tile, and OfferUp sends an empty string rather than a number. That was chased to the mechanism rather than shrugged off: the rows with no price are exactly the rows whose flags contain CALL_FOR_PRICE - 7 of 1,062 rows in one run and 10 of 1,409 in the regression, and in BOTH runs the count of missing prices equalled the flag count exactly, so there are zero unexplained nulls. Every one was an auto-dealer vehicle. Those rows come back with the price as null and call_for_price set to true, never as a misleading 0, and product/detail on the SAME listing ids returns the real numbers - 12,790, 22,290, 17,999 and 25,477 among them. OfferUp hides the figure on the tile, not on the listing.

Is the price the same number the page shows?

It was checked directly rather than assumed. Fourteen listings were fetched twice - once through the API and once as the listing page - and compared against the page's own structured-data price: 14 match, 0 mismatch, and no listing where the page had no price at all. The set deliberately included a genuine 0.00 listing, which is returned as 0 rather than being treated as missing, because a free item is a real thing on a classifieds site. Across 1,501 search rows, 24 detail payloads and a 1,409-row regression, the scan for raw HTML tags and HTML entities in any string found zero of each.

Which filters actually work?

The ones that were measured to change the result set, and only those. Price bounds bite exactly - a 100 to 200 band returned 44 of 44 rows inside it. Radius bites: at 5 miles a Los Angeles search kept 41 of 44 rows in Los Angeles across 4 towns, at 50 miles it spread over 30 towns. Sorting bites hard - a descending price sort led with 1,000,000 and 999,999 while ascending led with five zero-priced listings, and the distance and newest orderings shared 0 and 15 rows respectively with the default. Category ids bite, verified by fetching each returned listing's own category back from product/detail. Three condition parameter spellings were tried against OfferUp's own filter drawer and not one changed a single row over 44, so condition is NOT offered as a search filter - it is on product/detail instead. One interaction is worth knowing: a category plus a keyword lets the category win where the keyword has no local supply, so a phone keyword inside the vehicles category returns cars.

Does it work outside the United States?

No, and the failure is typed as a market limit rather than dressed up as a block. OfferUp is a US marketplace and answers a non-US request with a 403 page titled "Geolocation Unavailable", identically from every kind of exit, so it is a country gate rather than anti-bot behaviour. The engine returns MARKET_UNAVAILABLE for that, which tells you the truth: there is nothing to retry and no configuration that opens it. Everything else about the surface is unusually open - OfferUp's robots.txt names the data endpoint as allowed, and the call needs no cookie, no token, no account and no browser.

What does OfferUp NOT publish?

No seller rating, review count or rating scale anywhere. No total result count - the feed is cursor-paged and publishes no total, so total_results is null rather than an estimate. No seller at all on a search ROW: the feed tile has no owner field, so the seller comes back null there and is filled by product/detail. No condition and no is-the-price-firm flag on a search row either - both measured absent on 704 rows and both filled 24 of 24 on detail. And no per-listing rating or review count, because OfferUp has no product reviews: every listing is one second-hand object. The tile flag vocabulary observed over 1,062 rows was just two values, and one of them appeared on all 1,062 rows, so it is passed through verbatim rather than re-published as a boolean that would tell you nothing.

What is the OfferUp API?

OfferUp API is a ReefAPI endpoint group for us local classifieds: listings, prices and seller profiles near any zip. It returns live JSON through POST requests under /offerup/v1.

Is the OfferUp API free to try?

Yes. ReefAPI starts with 1,000 free credits, no card required. OfferUp calls use the same shared credit balance as every other ReefAPI engine.

Do I need an OfferUp login or account?

No login to OfferUp is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.

How fresh is the OfferUp data?

The page example is captured from a live search call, and production requests fetch live data through ReefAPI rather than a static sample.

How many credits does the OfferUp API use?

OfferUp actions currently cost 1 credit per successful call. Failed or blocked calls are free, and all APIs draw from one credit pool.

Can I call OfferUp from an AI assistant or MCP client?

Yes. Connect ReefAPI once through MCP and your assistant can call offerup actions with the same key, credit pool and JSON envelope used by normal REST requests.

docs / offerup

OfferUp

US local classifieds: listings, prices and seller profiles near any ZIP.

base /offerup/v12 endpoints
post/offerup/v1/product/detail1 credit

The full OfferUp listing record by `listing_id` (or listing URL): title, the seller's own description, numeric price and whether it is firm, condition, posted date, the complete photo gallery at full size, the category with its whole path, the listing's coordinates and town, pickup/shipping terms, vehicle attributes when it is a vehicle, and the seller's PUBLIC profile — display name, how many items they have sold and bought, when they joined, their stated response time and their verification badges.

ParameterAllowed / rangeDescription
listing_idrequiredOfferUp listing id — the uuid in the listing URL (https://offerup.com/item/detail/ca6fcc12-… → ca6fcc12-…). A full listing URL is also accepted. Every `search` row returns it as `listing_id`.
max_rotations = 3optional1–6Advanced: how many times to retry on a fresh exit (1-6, default 3 — leave unchanged for typical use).
Try in playground →