# OpenSooq — Middle East & North Africa classifieds: cars, property, phones, jobs

> Search the classifieds of ONE OpenSooq country site (Jordan, Saudi Arabia, Kuwait, Iraq, Oman, Qatar, Bahrain, UAE, Egypt, Libya, Lebanon, Palestine, Syria, Yemen, Sudan, Morocco, Algeria, Tunisia) by free text, category and subcategory, city and neighbourhood, price range, any filter of the category's own form (car make / model / year / mileage / fuel, property rooms / area / furnishing, phone brand / storage…), seller, in OpenSooq's four sort orders. Returns the source's own total and 30 listings per page with price and currency, attribute chips, location, posting date, promotion status, photo and the seller. `country` is required; every other parameter is optional.
> ReefAPI engine `opensooq` · 3 endpoints · clean JSON, no scraping or browsers to manage.

## How to call
- **Endpoint:** `POST https://api.reefapi.com/opensooq/v1/<action>` with a JSON body.
- **Auth:** header `x-api-key: <YOUR_REEFAPI_KEY>` — create one free (1,000 credits, no card): https://reefapi.com/signup
- **Response (every call):** `{ ok: boolean, data: ..., meta: { record_count, credits, ... }, error: { code, message } }` — branch on `ok`. Failed calls are free except verified SHEIN NOT_FOUND on product/detail and price (4 credits).
- **One key + one shared credit pool** across every ReefAPI API. Per-call credits are listed on each endpoint below.
- **Use it from an AI agent (MCP):** connect `https://api.reefapi.com/mcp` (remote streamable-http). Send the key as `Authorization: Bearer <key>`, or put it in the URL (`?key=<key>`) when the client has no header field, as ChatGPT does.

## Endpoints

### POST https://api.reefapi.com/opensooq/v1/search — 3 credits
Search the classifieds of ONE OpenSooq country site (Jordan, Saudi Arabia, Kuwait, Iraq, Oman, Qatar, Bahrain, UAE, Egypt, Libya, Lebanon, Palestine, Syria, Yemen, Sudan, Morocco, Algeria, Tunisia) by free text, category and subcategory, city and neighbourhood, price range, any filter of the category's own form (car make / model / year / mileage / fuel, property rooms / area / furnishing, phone brand / storage…), seller, in OpenSooq's four sort orders. Returns the source's own total and 30 listings per page with price and currency, attribute chips, location, posting date, promotion status, photo and the seller. `country` is required; every other parameter is optional.

**Parameters:**
- `country` (enum, optional) — ONE OpenSooq country site (two-letter code). One call per country. Measured 2026-10-14 listing totals: jo 148,613 · sa 21,771 (see BUILD-LOG for every site). [one of: jo, sa, kw, iq, om, qa, bh, ae, eg, ly, lb, ps, sy, ye, sd, ma, dz, tn]
- `query` (string, optional) — Free text, matched by OpenSooq's own search over titles and descriptions (Arabic or English). Measured: jo `iphone` = 5,122, sa `ايفون` = 834.
- `category` (string, optional) — Category slug as OpenSooq writes it in its URLs (action `categories` lists them per country): cars, property/property-for-sale, property/property-for-rent, mobile-phones-tablets, electronics-appliances, home-garden, jobs, pets… An unknown slug fails with INVALID_PARAM.
- `subcategory` (string, optional) — Subcategory slug under `category` (e.g. cars-for-sale under cars, mobile-phones under mobile-phones-tablets, apartments-for-sale under property/property-for-sale). Needs `category`.
- `city_id` (integer, optional) — City id (action `categories` lists the country's cities with ids). Measured: jo `iphone` in Amman (59) = 3,355 of 5,122.
- `neighborhood_id` (integer, optional) — Neighbourhood id inside `city_id` (action `categories` with `city_id` lists them; every row carries its own neighborhood_id). Measured: jo phones, Amman / Al Gardens (6759) = 219.
- `price_min` (number, optional) — Lowest price in the currency the site prints (JOD, SAR, AED, IQD, LBP…). Measured: jo `iphone` 100-500 = 2,685 of 5,122 (30/30 rows inside the range).
- `price_max` (number, optional) — Highest price in the country's currency.
- `with_price_only` (boolean, optional) — true = only listings that publish a price. Measured: jo `iphone` 5,009 of 5,122.
- `filters` (object, optional) — Any filter of the category's own filter form, by its `key` from action `categories`: a value filter takes the value id(s) — {"car_make": 24851} or {"Phones_Brand": [4157, 4159]} — and a range filter takes {"from": …, "to": …} — {"Car_Year": {"from": 2020, "to": 2022}}. Child filters (car_model) need their parent (car_make). Every filter is checked against the source's own applied-filter echo: a filter OpenSooq did not apply fails the call with INVALID_PARAM instead of returning the unfiltered list. Measured (jo cars): Car_Year 2020-2022 = 2,306 of 18,773; (jo phones) Phones_Brand Apple = 4,639 of 8,607.
- `seller_id` (string, optional) — Only the listings of ONE seller: the `seller.member_id` of a row. Measured: member 38496397 = 95 listings (the source does not echo this filter; the engine checks every returned row).
- `sort` (enum, optional, default "relevance") — Order of the results (OpenSooq's own four). [one of: relevance, newest, price_asc, price_desc]
- `page` (integer, optional, default 1) — Page number, 30 listings per page. `summary.last_page` and `summary.has_more` come from the source's own total; a page past the last returns no rows (measured: page 172 of a 171-page result, 0 rows). Deep pages are served (measured: jo page 1,000 of 4,954).
- `lang` (enum, optional, default "en") — Language of the source's labels (city, category, attribute labels, relative dates): en or ar. Listing titles and descriptions are the seller's own text in whatever language they wrote (often Arabic) and are never translated. [one of: en, ar]

**Returns:** `{rows[], summary{country, lang, currency, total_results, page, rows_per_page, last_page, has_more, past_last_page, promoted_on_page, filters_applied}}`. Each row: id + country (the values `listing` takes), url, title, price + currency (the amount and currency the card prints — on multi-currency sites the site's display currency, see listing_currency) + price_as_published + price_published, listing_currency (the currency the seller listed in) + price_converted (true when the card shows OpenSooq's conversion, e.g. Lebanon prints LBP for listings priced in USD; `listing` returns the seller's own price), highlights, attributes_as_published[] (the card's attribute chips), description_preview, category / category_code / category_slug, subcategory / subcategory_code / subcategory_slug, city + city_id, neighborhood + neighborhood_id, posted_at_as_published (often relative) + posted_date (when absolute), inserted_date, expires_as_published, listing_status, promoted + promotion (vip / turbo / featured), post_badges[], flags[], mileage_km (vehicles), image_url, image_count, has_video, has_360, has_phone, phone_masked (as OpenSooq publishes it, last two digits hidden), seller {member_id, name, username, shop_name, type, badges[], rating_avg, rating_count, verification_level, shop_logo_url}.

**Example request body:**
```json
{
  "country": "jo",
  "query": "iphone"
}
```

### POST https://api.reefapi.com/opensooq/v1/listing — 3 credits
One OpenSooq listing by `country` + `id` (both from a search row) or by its URL: full description, price with currency, the labelled attribute table (e.g. make, model, year, mileage, fuel, transmission for cars; rooms, bathrooms, area, furnishing, floor for property; brand, model, storage, condition for phones), category and subcategory, city and neighbourhood, publication date, promotion status and badges, all photos, favourites count, AI tags, breadcrumbs and the seller block (name, shop flag, rating, number of listings, member since, response time, verification level, profile URL).

**Parameters:**
- `country` (enum, optional) — Country site of the listing — the `country` of the search row. Required with `id`. An id asked on another country's site is NOT_FOUND. [one of: jo, sa, kw, iq, om, qa, bh, ae, eg, ly, lb, ps, sy, ye, sd, ma, dz, tn]
- `id` (string, optional) — OpenSooq listing id from a search row (`id`), e.g. 287227484. Either `id` (+ `country`) or `url`.
- `url` (string, optional) — Alternative to `id`: an OpenSooq listing URL (https://<cc>.opensooq.com/<lang>/search/<id>).
- `lang` (enum, optional, default "en") — Language of the source's labels (city, category, attribute labels, relative dates): en or ar. Listing titles and descriptions are the seller's own text in whatever language they wrote (often Arabic) and are never translated. [one of: en, ar]

**Returns:** One listing: id, country, url, title, description, price, currency, currency_label, price_as_published, price_published, listing_price, listing_currency, price_converted, prices[]{price, currency}, price_valid_until, attributes[]{label, value, field}, attributes_as_published[], category, category_id, category_code, category_slug, subcategory, subcategory_id, subcategory_code, subcategory_slug, city, city_id, neighborhood, neighborhood_id, published_date, published_date_as_published, listing_status, promoted, promotion, is_live, badges[], photo_count, video_count, photos[], favorites_count, phone_masked, phone_enabled, seller{member_id, name, is_shop, rating_avg, rating_count, listings_count, member_since, response_time, verification_level, authorised_seller, profile_url, avatar_url}, ai_tags[], breadcrumbs[]{label, url}, product_reviews{score, count}.

**Example request body:**
```json
{
  "url": "https://jo.opensooq.com/en/search/287227484"
}
```

### POST https://api.reefapi.com/opensooq/v1/categories — 3 credits
For ONE country site (optionally inside a category / subcategory / city / filter): the categories and subcategories with their slugs and listing counts, the cities with ids, the neighbourhoods of a chosen city, and the full filter form of that scope — every filter key with its label, type (values / range / toggle), range bounds, parent filter and value ids with labels (English and Arabic) and counts. These are the values `search` takes as `category`, `subcategory`, `city_id`, `neighborhood_id` and `filters`. Child values (car models of a make) appear once the parent is given in `filters`.

**Parameters:**
- `country` (enum, optional) — ONE OpenSooq country site (two-letter code). One call per country. Measured 2026-10-14 listing totals: jo 148,613 · sa 21,771 (see BUILD-LOG for every site). [one of: jo, sa, kw, iq, om, qa, bh, ae, eg, ly, lb, ps, sy, ye, sd, ma, dz, tn]
- `category` (string, optional) — Category slug as OpenSooq writes it in its URLs (action `categories` lists them per country): cars, property/property-for-sale, property/property-for-rent, mobile-phones-tablets, electronics-appliances, home-garden, jobs, pets… An unknown slug fails with INVALID_PARAM.
- `subcategory` (string, optional) — Subcategory slug under `category` (e.g. cars-for-sale under cars, mobile-phones under mobile-phones-tablets, apartments-for-sale under property/property-for-sale). Needs `category`.
- `city_id` (integer, optional) — City id; when given, the answer lists that city's neighbourhoods.
- `filters` (object, optional) — Any filter of the category's own filter form, by its `key` from action `categories`: a value filter takes the value id(s) — {"car_make": 24851} or {"Phones_Brand": [4157, 4159]} — and a range filter takes {"from": …, "to": …} — {"Car_Year": {"from": 2020, "to": 2022}}. Child filters (car_model) need their parent (car_make). Every filter is checked against the source's own applied-filter echo: a filter OpenSooq did not apply fails the call with INVALID_PARAM instead of returning the unfiltered list. Measured (jo cars): Car_Year 2020-2022 = 2,306 of 18,773; (jo phones) Phones_Brand Apple = 4,639 of 8,607.
- `lang` (enum, optional, default "en") — Language of the source's labels (city, category, attribute labels, relative dates): en or ar. Listing titles and descriptions are the seller's own text in whatever language they wrote (often Arabic) and are never translated. [one of: en, ar]

**Returns:** `{country, lang, currency, scope{category, subcategory, city_id}, total_listings, categories[]{id, label, label_ar, slug}, subcategories[]{id, label, label_ar, count, slug}, cities[]{id, label, label_ar, count, slug}, neighborhoods[]{id, label, label_ar, count, slug}, filters[]{key, label, type, parent, range, send_as, values[]{id, label, label_ar, count, slug}}, sorts[]}`.

**Example request body:**
```json
{
  "country": "ae"
}
```

## At scale
- **Volume:** 5M+ requests a day, measured at 60 requests a second across the fleet with no
  central bottleneck. Per-key limits are raised for high-volume accounts; volume pricing on request.
- **Missing a source:** tell us a site we do not cover and it becomes an engine. A customer asked
  for bestprice.gr on 21 Sep 2026 and it was in the catalog on 22 Sep.
- **Support:** 2 minute median time from a question in the live chat to the first answer. Setup
  help included, no support tier to buy.
- **One key, one credit pool** across every API. No per-site plans, no separate subscriptions.

## More
- Try it live, no code: https://reefapi.com/playground?engine=opensooq
- Human docs page: https://reefapi.com/docs/opensooq
- Overview page: https://reefapi.com/opensooq-api
- Every ReefAPI API in one file (for your AI): https://reefapi.com/llms-full.txt
