Looking for the overview — what this API returns, what it costs, and a call you can run without a key? See the KupujemProdajem API page →
Classifieds & Second-hand

KupujemProdajem API & Scraper

The KupujemProdajem API returns Serbia's largest general classifieds site as clean JSON, in four actions: search, listing, count and categories.

4 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.

KupujemProdajem is where Serbia actually buys and sells: on 2026-10-01 its live catalogue held 127,585 white-goods and home-appliance ads, 76,629 under sport and leisure, 64,090 in womenswear, 53,103 records and CDs, 49,267 flats and houses for sale, 48,904 furniture ads, 39,959 women's shoes, 35,174 mobile phones, 14,337 bicycles, 11,129 cars, 11,033 magazines and comics, 10,289 properties to rent, 3,116 commercial vehicles, 505 job posts and 365 ads in a single one of its 22 service categories. search narrows that by keywords, category, sub-group, town, seller, a price band in EUR or RSD, item condition, whether the ad is an offer or a wanted ad, and whether it has a price, has a photo, accepts a swap or is available immediately; it sorts by newest, cheapest or most expensive and returns up to 500 rows per page. Every row carries the ad id, the public ad URL, the title, the description snippet, the price with the currency the seller chose, the category and sub-group, the town, the condition, whether it is an offer or a wanted ad, whether it is goods, a service or a job, when it was posted and last renewed, its view and favourite counts, the seller id, whether it is a paid placement, and a photo. listing adds the full description, every photo at full size and as a thumbnail, the ad status and expiry date, courier-delivery and local-pickup flags, ISBN, OEM code, VIN, mileage and registration status where the category has them, and the seller's public profile: display name, person or company, town, member-since date, positive and negative review counts, phone-verified and bank-account-verified badges and company tax and registry ids. count answers how many live ads match a filter set in one tiny call, and it is the uncapped number. categories returns all 89 live categories with the flags that tell you whether the condition and swap filters mean anything inside each one. Verified on 2026-10-01: 22 of 22 live checks behaved as expected on two separate runs, all 13 exposed filters measurably narrowed the same-run control, all 10 main categories returned rows and resolved their first ad to a full detail record, and on 15 ads the price and currency matched the figure KupujemProdajem prints on its own page 15 of 15, with zero mismatches. No KupujemProdajem account — one ReefAPI key and the standard { ok, data, meta, error } envelope.

Reference

What is actually on the site — live ad counts on 2026-10-01

These are counts read off the live index, one call each, not estimates. They move as ads are posted and expire; the count action returns today's number the same way, and every search response carries the matching total in the response body.

categorycategory idlive adsprice published?
Bela tehnika i kućni aparati (white goods, home appliances)15127,585yes, EUR or RSD
Sport i razonoda (sport and leisure)3476,629yes, EUR or RSD
Odeća | Ženska (womenswear)74364,090yes, EUR or RSD
Audio | Vinili, CD i kasete (records, CDs, tapes)117653,103yes, EUR or RSD
Nekretnine | Prodaja (property for sale)282149,267yes, usually EUR
Nameštaj (furniture)126848,904yes, EUR or RSD
Obuća | Ženska (women's shoes)107739,959yes, EUR or RSD
Mobilni telefoni (mobile phones)2335,174yes, EUR or RSD
Bicikli (bicycles)91214,337yes, EUR or RSD
Automobili (cars)291911,129yes, usually EUR
Časopisi i stripovi (magazines, comics)129611,033yes, mostly RSD
Nekretnine | Izdavanje (property to rent)285010,289yes, monthly rent
Transportna vozila (commercial vehicles)23293,116yes, EUR or RSD
Poslovi (jobs)2546505no — a job ad has no price
Usluge | Auto-moto (one of 22 service categories)1410365yes, usually RSD

That is 15 of the 89 live categories; the categories action lists all of them (66 goods, 22 services, 1 jobs) with their ids. Keyword searches are much larger than any single category: audi 309,442 live ads, iphone 84,726, stan (flat) 72,115, bicikl 41,714, ps5 9,754 on the same day. 🔴 A keyword search can only be paged to 20,000 rows: iphone reports 20,000 over 667 pages and page 668 is empty, while count says 84,726 for the same words — so the API returns both figures and flags which one is capped. A category search has no such ceiling: category 743 reported 64,085 rows over 2,137 pages and page 2,137 served its last 7 rows.

Live example

Real request and response JSON

Captured from the indexed primary action, search, on .

Captured request
{
  "method": "POST",
  "url": "https://api.reefapi.com/kupujemprodajem/v1/search",
  "headers": {
    "x-api-key": "$REEF_KEY",
    "content-type": "application/json"
  },
  "body": {
    "query": "iphone",
    "max_results": 20
  }
}
Captured response
{
  "ok": true,
  "meta": {
    "api": "kupujemprodajem",
    "endpoint": "search",
    "mode": "live",
    "latency_ms": 3660.4,
    "record_count": 30,
    "bytes": 61399,
    "cache_hit": false,
    "upstream_requests": 2,
    "source_url": "https://www.kupujemprodajem.com/api/web/v1/search?keywords=iphone&page=1&perPage=30",
    "charged_credits": 1,
    "version": "1.0.0",
    "request_id": "1a680e9465754bff",
    "queue_ms": 1.5
  },
  "data": {
    "total_results": 20000,
    "total_capped": true,
    "total_available": 84936,
    "page": 1,
    "page_count": 667,
    "page_size": 30,
    "returned": 30,
    "page_clamped": false,
    "has_more": true,
    "dropped_non_listing_tiles": 0,
    "filter_id": 9047815648,
    "listings": [
      {
        "listing_id": "152902916",
        "url": "https://www.kupujemprodajem.com/mobilni-telefoni/apple-iphone/iphone-18-pro-18-pro-max-17-pro-max-17-16-plus-15/oglas/152902916",
        "title": "iPhone 18 PRO/ 18 PRO MAX/ 17 PRO MAX/ 17/ 16 PLUS/ 15/",
        "description_excerpt": "INFORMACIJE: POZIV i PORUKA (WA, Viber, Telegram, SMS) iPHONE ...",
        "category_id": 23,
        "category_name": "Mobilni telefoni",
        "group_id": 489,
        "group_name": "Apple iPhone",
        "location_id": 1,
        "location_name": "Beograd",
        "condition": "as-new",
        "listing_type": "sell",
        "listing_kind": "goods",
        "posted_at": "2026-10-01 16:44:23",
        "renewed_at": "2026-10-01 16:44:23",
        "view_count": 501971,
        "favorite_count": 1357,
        "seller_id": 1396317,
        "promoted": true,
        "highlighted": false,
        "immediate_available": true,
        "has_video": false,
        "is_vehicle": false,
        "image": "https://images.kupujemprodajem.com/photos/oglasi/6/91/152902916/152902916_6ab25298f19a28-98318075image.webp",
        "image_thumbnail": "https://images.kupujemprodajem.com/photos/oglasi/6/91/152902916/tmb-300x300-152902916_6ab25298f19a28-98318075image.webp",
        "attributes": [],
        "price": 1460,
        "price_currency": "EUR",
        "price_kind": "negotiable",
        "price_display": null,
        "price_label": null,
        "price_suffix": null,
        "exchange_accepted": false
      },
      {
        "listing_id": "137465119",
        "url": "https://www.kupujemprodajem.com/mobilni-telefoni/apple-iphone/iphone-18-17-pro-max-16-15-13-512gb-256gb-plus-17e-16-air/oglas/137465119",
        "title": "Iphone 18/17/Pro/Max/16/15/13/512gb/256gb/Plus/17e/16+/Air",
        "description_excerpt": ". (EN) New Original Apple iPhone Unpacked -- Vacuum ...",
        "category_id": 23,
        "category_name": "Mobilni telefoni",
        "group_id": 489,
        "group_name": "Apple iPhone",
        "location_id": 16,
        "location_name": "Novi Sad",
        "condition": "as-new",
        "listing_type": "sell",
        "listing_kind": "goods",
        "posted_at": "2026-10-01 09:57:24",
        "renewed_at": "2026-10-01 09:57:24",
        "view_count": 477120,
        "favorite_count": 1950,
        "seller_id": 929958,
        "promoted": true,
        "highlighted": false,
        "immediate_available": true,
        "has_video": false,
        "is_vehicle": false,
        "image": "https://images.kupujemprodajem.com/photos/oglasi/9/11/137465119/137465119_6a156c27a21089-25392871image.webp",
        "image_thumbnail": "https://images.kupujemprodajem.com/photos/oglasi/9/11/137465119/tmb-300x300-137465119_6a156c27a21089-25392871image.webp",
        "attributes": [],
        "price": 999,
        "price_currency": "EUR",
        "price_kind": "fixed",
        "price_display": null,
        "price_label": null,
        "price_suffix": null,
        "exchange_accepted": false
      },
      {
        "listing_id": "137463316",
        "url": "https://www.kupujemprodajem.com/mobilni-telefoni/ostalo/otkup-vakum-telefona-ns-samo-novo-iphone-samsung-novi-sad/oglas/137463316",
        "title": "Otkup VAKUM Telefona NS samo NOVO Iphone Samsung Novi Sad",
        "description_excerpt": "100% Brzo i Sigurno! ------------... NUŽNO! OBOSTRANO ZADOVOLJSTVO! MAKSIMALNO ...",
        "category_id": 23,
        "category_name": "Mobilni telefoni",
        "group_id": 111,
        "group_name": "Ostalo",
        "location_id": 16,
        "location_name": "Novi Sad",
        "condition": "as-new",
        "listing_type": "buy",
        "listing_kind": "goods",
        "posted_at": "2026-09-28 08:56:07",
        "renewed_at": "2026-09-28 08:56:07",
        "view_count": 32247,
        "favorite_count": 98,
        "seller_id": 929958,
        "promoted": true,
        "highlighted": false,
        "immediate_available": false,
        "has_video": false,
        "is_vehicle": false,
        "image": "https://images.kupujemprodajem.com/photos/oglasi/6/31/137463316/137463316_6986fcb278c433-22464378image.webp",
        "image_thumbnail": "https://images.kupujemprodajem.com/photos/oglasi/6/31/137463316/tmb-300x300-137463316_6986fcb278c433-22464378image.webp",
        "attributes": [],
        "price": null,
        "price_currency": "EUR",
        "price_kind": "not_priced_wanted",
        "price_display": null,
        "price_label": null,
        "price_suffix": null,
        "exchange_accepted": false
      }
    ],
    "query": "iphone",
    "sort": "default",
    "filters": {
      "keywords": "iphone"
    }
  }
}
Actions

What the KupujemProdajem API does

ActionDescriptionConcrete use caseKey params
searchSearch KupujemProdajem classifieds. Needs `query` (keywords) OR `category` (a numeric id from the `categories` action) — either alone is enough, and the rest of the filters narrow it: sub-group, town, seller, price band with currency, condition, offer direction, has-price, has-photo, accepts-swap, available-immediately. Returns the source's own total plus, when that total is capped at 20 000, its uncapped count in `total_available`.Price-intelligence teams call search to search KupujemProdajem classifieds.query, category, group, location, seller_id, ...
listingFull detail of one ad by id or URL: the complete description, every photo at full size and as a thumbnail, price with the source's own printed string, condition, location, posting and renewal time, view and favourite counts, delivery and local-pickup flags, vehicle/ISBN/OEM fields where the category has them, and the seller's public profile (display name, person or company, town, member-since, review counts, phone-verified and bank-account-verified badges, company tax and registry ids). A removed or non-existent ad returns NOT_FOUND. The seller's phone number is never requested and never returned — only the source's own `has_phone`.Classifieds aggregators call listing to get full detail of one ad by id or URL.listing_id, url
countHow many live ads match a filter set, without downloading any of them. One ~60-byte upstream call, and it is the source's UNCAPPED number: the same query whose `search` total stops at 20 000 counts 84 718 here. Takes exactly the same filters as `search` (page and sort are ignored).Resale and arbitrage tools call count to get how many live ads match a filter set, without downloading any of them.query, category, group, location, seller_id, ...
categoriesThe site's live category table — the resolver `search` needs, because `category` takes a numeric id. 89 categories across three kinds (66 goods, 22 services, 1 jobs), each with the flags that tell you whether the condition and swap filters mean anything in it.Lead-generation teams call categories to get the site's live category table.kind
Code samples

Call search from your stack

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

Who uses this API and why

  • Track Serbian residential property supply and asking prices: 49,267 flats and houses for sale and 10,289 to rent on 2026-10-01, filterable by town and price band, each with the full description, every photo and the lister's review history.
  • Price used stock for a Balkan e-commerce or repair business: pull the same model across 35,174 phone ads or 127,585 appliance ads, keep each seller's own currency, and use has_price and condition to compare like with like.
  • Find demand instead of supply: 2,487 of a 4,595-ad control were people asking to buy. Filter listing_type=buy by category and town to see what the market is short of before you stock it.
  • Monitor a competitor or a shop account: pass their seller_id to search for every ad they have live, and read their public review counts and verification badges from any one of their ads.
  • Build a saved-search alert service: sort by newest, page the first results on a schedule, and use count as a cheap heartbeat that tells you the matching total moved before you pay for a full page.
FAQ

Questions developers ask before integrating

Which currency are the prices in?

The one the seller chose, per ad — KupujemProdajem lets a seller quote in EUR or in RSD, and both appear in the same result set. We measured furniture rows coming back as 7,899 RSD and 150 EUR side by side. So every row carries price together with price_currency, and the listing action also returns price_display, which is the exact string KupujemProdajem prints on the ad page ("8.500 €", "2.500 din"), so you can check our number against the source's own rendering. On 15 ads sampled across ten categories the two agreed 15 of 15, with zero mismatches. We do not convert anything: a converted price is our arithmetic, not the seller's offer.

Why is price null on some ads?

Because those ads have no price, and a zero would be a lie. Across 300 sampled rows in ten categories, 227 carried a price and 73 did not: job posts, wanted ads where the poster is buying rather than selling, and ads where the seller asks you to enquire. KupujemProdajem writes a 0 into those, so the API returns null and puts the reason in price_kind — not_priced_job, not_priced_wanted or not_priced — alongside the site's own label ("Posao" for a job, "Kupujem" for a wanted ad). If you only want ads with a real number, set has_price: it narrowed a 4,595-ad control to 1,989.

Does a filter actually do anything, or does the site just ignore it?

Every filter we expose was measured against the same-run unfiltered control, and only the ones that moved the total are offered. Against a control of 4,595 ads (keywords telefon, category 23): location=1 (Belgrade) 2,842, a 100-200 EUR band 137, condition=new 481, condition=as-new 1,909, condition=used 1,956, condition=damaged 47, listing_type=sell 2,108, listing_type=buy 2,487, has_price 1,989, has_photo 4,565, exchange_only 128, immediate_available 1,249, group=489 (Apple iPhone) 721. Thirteen for thirteen. Two filters the site accepts but does not usefully apply are deliberately not exposed, because a handle that does nothing is worse than no handle. And an unknown condition or sort value is rejected with INVALID_PARAM rather than passed through, because the site would answer a typo with a convincing empty page.

How many ads can I actually page through?

All of them in a category search, and 20,000 in a keyword search. Measured both ways: category 743 reported 64,085 matching ads over 2,137 pages and page 2,137 really did serve the last 7 rows, with the site's own counter agreeing to the row. The keyword search keywords=iphone reported 20,000 over 667 pages, page 667 returned 19 rows and page 668 returned none — exactly 20,000 — while count for the same words said 84,726. So search returns total_results, total_capped and total_available, and you can see at a glance whether you are looking at the whole set. If you need past 20,000 on a keyword, add a category, a town or a price band and the ceiling stops mattering.

How many rows per page, and is it fixed?

Thirty by default, and it is a real parameter, not a fixed page size: we measured 30, 60, 100, 120, 200 and 500 rows per page all honoured exactly, at roughly 59 KB, 118 KB, 193 KB, 231 KB, 384 KB and 956 KB of response. One search credit covers whichever you pick, so a bulk pull is cheaper at 200 or 500 and a UI feed is leaner at 30.

What do I get about the seller?

What the ad page itself shows, unmodified: the display name, whether the account is a person or a company, their town, the date they joined, their positive and negative review counts, whether their phone and their bank account are verified, their stated reply habit, their profile note, and — for company accounts — the Serbian tax (PIB) and registry (MBR) ids. On one sampled seller that was 1,326 reviews, 1,326 positive, 0 negative, a member since 2017-06-03, phone and bank account both verified. The seller block was present on 10 of 10 details we sampled. You can also pull every ad one seller has: pass their seller_id to search.

Do I get the seller's phone number?

No. KupujemProdajem keeps the phone behind a separate click on its own site, and this API never asks for it and never returns it. What you do get is the site's own has_phone flag, so you know whether a phone exists on the ad without us going after it.

What does listing add over a search row?

The full description instead of the snippet — 221 to 3,635 characters on the ads we sampled, with the site's bold tags and HTML entities already cleaned out — plus every photo at full size and as a 300x300 thumbnail (1 to 15 per ad in our sample), the ad status and its expiry date, courier-delivery and local-pickup availability, the paid-placement type, and the category-specific fields where they exist: ISBN for books, OEM code for parts, VIN, mileage and registration status for vehicles. Everything in the search row is in there too, and an ad id taken from search resolved to the same record 15 of 15 times.

Why is condition empty on some ads?

Because the site only offers a condition where it makes sense. Property, jobs and services have no condition field at all, so it came back on 180 of 300 sampled rows and we return null rather than inventing "used". You do not have to guess which categories have it: the categories action returns a shows_condition flag (and a shows_exchange flag) per category, so you can tell before you filter.

Are category-specific attributes included?

Where the site publishes them, and we are honest about how often that is: the attributes array was filled on 4 of the 10 details we sampled — vehicles and property — and empty on most goods ads, where the detail lives in the free-text description instead. We return the empty array as an empty array. Mileage, VIN and registration status for vehicles come back as their own named fields regardless.

Can I search for people who are BUYING, not selling?

Yes, and on this site that is a real market rather than a rounding error: in a 4,595-ad control, 2,487 were wanted ads and 2,108 were offers. Set listing_type to buy to get only the wanted ads, sell for only the offers, or leave it off for both. Wanted ads carry no price by definition, and the API labels them not_priced_wanted instead of showing a zero.

What happens if I ask for an ad that has been removed?

You get NOT_FOUND with a message saying the ad is removed or never existed, never a blank success you have to interpret. A search that genuinely matches nothing is a different thing and is treated as one: ok with zero rows and total_results 0, because an empty answer is still an answer. We measured both — a dead id and a nonsense keyword — on two separate runs.

How do I find the right category or town id?

The categories action returns all 89 category ids with their names and kinds, so category needs no guessing. Town and sub-group are different, and we say so rather than implying a catalogue exists: KupujemProdajem publishes no public list of either, so location_id and group_id come back on every search row next to their names, and you take the id from there. Both filters are measured working — Belgrade narrowed a 4,595-ad control to 2,842, and the Apple iPhone sub-group to 721.

What is the KupujemProdajem API?

KupujemProdajem API is a ReefAPI endpoint group for serbia's biggest classifieds site as json: cars, flats for sale and to rent, phones, furniture, clothing, services and jobs, with the seller's own currency, review history and verification badges. It returns live JSON through POST requests under /kupujemprodajem/v1.

docs / kupujemprodajem

KupujemProdajem

Serbia's biggest classifieds site as JSON: cars, flats for sale and to rent, phones, furniture, clothing, services and jobs, with the seller's own currency, review history and verification badges.

base /kupujemprodajem/v14 endpoints
post/kupujemprodajem/v1/listing1 credit

Full detail of one ad by id or URL: the complete description, every photo at full size and as a thumbnail, price with the source's own printed string, condition, location, posting and renewal time, view and favourite counts, delivery and local-pickup flags, vehicle/ISBN/OEM fields where the category has them, and the seller's public profile (display name, person or company, town, member-since, review counts, phone-verified and bank-account-verified badges, company tax and registry ids). A removed or non-existent ad returns NOT_FOUND. The seller's phone number is never requested and never returned — only the source's own `has_phone`.

ParameterAllowed / rangeDescription
listing_idoptional—Ad id — the number at the end of every ad URL and the `listing_id` of every search row. Give this or `url`.
urloptional—Full ad URL exactly as `search` returns it; the id is taken from its `/oglas/<id>` tail.
Try in playground →
post/kupujemprodajem/v1/count1 credit

How many live ads match a filter set, without downloading any of them. One ~60-byte upstream call, and it is the source's UNCAPPED number: the same query whose `search` total stops at 20 000 counts 84 718 here. Takes exactly the same filters as `search` (page and sort are ignored).

ParameterAllowed / rangeDescription
queryoptional—Free-text keywords, exactly as typed into the site's own search box (Serbian Latin or Cyrillic both work). Give this or `category`, or both.
categoryoptional1–Numeric category id. 23 Mobilni telefoni, 2919 Automobili, 2821 Nekretnine | Prodaja, 2850 Nekretnine | Izdavanje, 2546 Poslovi, 1268 Nameštaj. The `categories` action lists all 89. Give this or `query`, or both.
groupoptional1–Sub-group id inside a category (489 = Apple iPhone inside category 23). Group ids are not published as a standalone table; they come back on every search row as `group_id`/`group_name`. Measured bite: 4 595 → 721.
locationoptional1–Town/city id (1 = Beograd). The source publishes no public location table (five endpoint spellings probed, all 404), so ids come from search rows' own `location_id`/`location_name`. Measured bite: 4 595 → 2 842.
seller_idoptional1–Every ad of one seller. The id comes back as `seller_id` on search rows and inside the `seller` block of `listing`.
price_minoptional0–Minimum price. A band is only meaningful together with `currency` because sellers quote in EUR *or* RSD on the same page; when you omit `currency` the source's own default (`rsd`) is used and echoed back in `filters`. Measured bite: 4 595 → 137 for 100–200 EUR. A band excludes ads with no price.
price_maxoptional0–Maximum price. A band is only meaningful together with `currency` because sellers quote in EUR *or* RSD on the same page; when you omit `currency` the source's own default (`rsd`) is used and echoed back in `filters`. Measured bite: 4 595 → 137 for 100–200 EUR. A band excludes ads with no price.
currencyoptionaleur · rsdCurrency the price band is expressed in. Does not translate the ads: each row still comes back in the currency its seller chose.
conditionoptionalnew · as-new · used · damagedThe source's own item condition. Measured bite on a 4 595-row control: new 481, used 1 960. An unknown value is rejected here because the source would answer it with a misleading empty page.
listing_typeoptionalsell · buyOffer direction. Omit for both. Measured: sell 2 108, buy 2 487 of a 4 595-row control — wanted ads are a real part of this site, and they carry no price.
has_price = falseoptional—Only ads that actually carry a number (measured 4 595 → 1 989).
has_photo = falseoptional—Only ads with at least one photo (measured 4 595 → 4 565 — on this site almost everything has a photo, so it rarely narrows).
exchange_only = falseoptional—Only ads whose seller accepts a swap (measured 4 595 → 128).
immediate_available = falseoptional—Only ads flagged available right away (measured 4 595 → 1 249).
Try in playground →
post/kupujemprodajem/v1/categories1 credit

The site's live category table — the resolver `search` needs, because `category` takes a numeric id. 89 categories across three kinds (66 goods, 22 services, 1 jobs), each with the flags that tell you whether the condition and swap filters mean anything in it.

ParameterAllowed / rangeDescription
kindoptionalgoods · service · jobNarrow the category list to one kind. Omit for all 89.
Try in playground →
Built for volume
5M+ requests a day

Measured at 60 requests a second across the fleet, with no central bottleneck. Volume pricing is on request, and per-key limits are raised for high-volume accounts.

Missing a source?
We build it

Tell us a site we do not cover yet and it becomes an engine. A customer asked for bestprice.gr on a Sunday and it was in the catalog the next day.

Support
2 minute median reply

Median time from a question in the live chat to the first answer, measured across every answered conversation. Setup help included, no support tier to buy.

One key, one balance
Every API included

No per-site plans and no separate subscriptions. One key and one credit pool across the whole catalog, so adding a source costs nothing up front.

Planning something large? Tell us the volume and the sources and we will come back with what it costs and what we would have to build.