Skip to main content

Marketplace

Author: Josh S. Sakweli, Backend Lead Team
Last Updated: 2026-06-04
Version: v1.0

Base URL: api/v1/e-commerce/marketplace

Short Description: The Marketplace API is the primary product discovery layer of the Nexgate e-commerce platform. It exposes personalised product feeds, trending rankings, hot deals, live group purchases, and a powerful advanced filter with keyword search — all driven by a scoring formula built on real purchase, view, and cart signals.

Hints:

  • All endpoints work for anonymous users (no token required). Authenticated users automatically receive personalised results where applicable — no extra parameter needed.
  • TRENDING and FOR_YOU feeds re-rank within each page using the full scoring formula. Other sort modes (NEWEST, PRICE_ASC, etc.) are sorted at the database level and return exact ordering.
  • hasMultipleColors, maxGroupSeatsLeft, and minGroupDiscountPercent filters are applied after the database query, so the totalElements count in the response reflects the pre-filtered DB count — not the post-filtered count.
  • viewCount increments each time GET /shops/{shopId}/products/{productId} is called. cartAddCount increments when a product is added to a cart for the first time (not on quantity updates).
  • Pages are 1-based (page=1 returns the first page).

Standard Response Format

All API responses follow a consistent structure using the Globe Response Builder pattern:

Success Response Structure

{
  "success": true,
  "httpStatus": "OK",
  "message": "Operation completed successfully",
  "action_time": "2026-06-04T10:30:45",
  "data": {
    "content": [...],
    "currentPage": 1,
    "pageSize": 20,
    "totalElements": 158,
    "totalPages": 8,
    "hasNext": true,
    "hasPrevious": false
  }
}

Error Response Structure

{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Error description",
  "action_time": "2026-06-04T10:30:45",
  "data": "Error description"
}

Standard Response Fields

Field Type Description
success boolean true for success, false for errors
httpStatus string HTTP status name (OK, BAD_REQUEST, NOT_FOUND, etc.)
message string Human-readable operation result
action_time string ISO 8601 timestamp of response generation
data object Paginated payload for success, error detail for failures

Scoring Formulas

These formulas are the engine behind TRENDING, FOR_YOU, and BEST_DEAL sort modes. Understanding them helps predict how products are ranked.

Log Normalization

All count-based signals (soldQuantity, viewCount, cartAddCount) are log-normalized before applying weights. This prevents one product with 100,000 sales from dominating everything else.

normalize(value) = min(1.0,  log(1 + value) / log(1 + 10,000))

Examples:
  0 sales     →  0.000
  100 sales   →  0.501
  1,000 sales →  0.750
  10,000 sales → 1.000   ← reference ceiling
  50,000 sales → 1.000   ← capped at 1.0

┌──────────────────────────────────────────────────────────────────────┐
│                      TRENDING SCORE FORMULA                          │
├──────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  trendingScore =                                                     │
│      normalize(soldQuantity)   × 0.30   ← real money paid           │
│    + normalize(viewCount)      × 0.25   ← real browsing intent      │
│    + groupHeat                 × 0.20   ← social urgency            │
│    + normalize(cartAddCount)   × 0.15   ← purchase intent           │
│    + discountStrength          × 0.07   ← deal attractiveness       │
│    + recencyBonus              × 0.03   ← freshness tiebreaker      │
│                                                                      │
│  groupHeat       = seatsOccupied / totalSeats  (hottest live group)  │
│  discountStrength= (comparePrice - price) / comparePrice            │
│  recencyBonus    = 1.0 if ≤ 7 days old                              │
│                    0.5 if ≤ 30 days old                             │
│                    0.0 otherwise                                     │
└──────────────────────────────────────────────────────────────────────┘

Formula 2 — Personalized Trending Score (authenticated users)

┌──────────────────────────────────────────────────────────────────────┐
│                  PERSONALIZED TRENDING SCORE FORMULA                 │
├──────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  personalizedScore =                                                 │
│      trendingScore                                                   │
│    + 0.25   (if product's shop is in user's subscriptions)          │
│    + 0.00   (otherwise)                                              │
│                                                                      │
│  This is Option A — soft priority. Subscribed-shop products float   │
│  near the top but a genuinely viral product still beats them.       │
└──────────────────────────────────────────────────────────────────────┘

Formula 3 — Relevance Score (For You feed)

┌──────────────────────────────────────────────────────────────────────┐
│                       RELEVANCE SCORE FORMULA                        │
├──────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  relevanceScore =                                                    │
│      categoryMatch  × 0.40   ← product's category is in cart        │
│    + favShopBoost   × 0.40   ← shop is in user's subscriptions      │
│    + trendingScore  × 0.20   ← global popularity tiebreaker         │
│                                                                      │
│  categoryMatch = 1.0 if product's category matches any category     │
│                  currently in the user's cart, else 0.0             │
│  favShopBoost  = 1.0 if shop is subscribed, else 0.0               │
│                                                                      │
│  Fallback: if user has no cart items and no subscriptions,          │
│  the endpoint falls back to the Personalized Trending feed.         │
└──────────────────────────────────────────────────────────────────────┘

Advanced Filter — UI Reference

The advanced filter groups its parameters into logical sections. Below is a reference layout showing how a frontend filter panel would be structured:

┌───────────────────────────────────────────────────────────┐
│                    MARKETPLACE FILTERS                    │
├───────────────────────────────────────────────────────────┤
│  🔍  SEARCH                                               │
│  ┌─────────────────────────────────────────────────────┐  │
│  │  ?q=  keyword search across name & description...  │  │
│  └─────────────────────────────────────────────────────┘  │
├───────────────────────────────────────────────────────────┤
│  💰  PRICE RANGE                                          │
│      Min [ minPrice _________ ]                           │
│      Max [ maxPrice _________ ]                           │
├───────────────────────────────────────────────────────────┤
│  🏷️  PRODUCT                                              │
│      Category    [ categoryId ▼ ]                         │
│      Condition   ○ NEW   ○ USED   ○ REFURBISHED           │
│      Type        ○ Physical   ○ Digital                   │
│      Urgency     ○ NONE  ○ LIMITED_TIME  ○ LOW_STOCK      │
│                  ○ FLASH_SALE                             │
│      [ ] hasMultipleColors  — show colour variants only   │
├───────────────────────────────────────────────────────────┤
│  📦  AVAILABILITY                                         │
│      [ ] inStock           — in-stock only               │
│      Min stock [ minStockQuantity ]  — bulk buyers        │
├───────────────────────────────────────────────────────────┤
│  🤝  GROUP DEALS                                          │
│      [ ] hasGroupBuying    — supports group buying        │
│      [ ] hasActiveGroup    — live OPEN group right now   │
│      Max seats left  [ maxGroupSeatsLeft ]  ← urgency    │
│      Min group disc. [ minGroupDiscountPercent ]%         │
├───────────────────────────────────────────────────────────┤
│  💳  PAYMENT                                              │
│      [ ] onSale            — currently discounted         │
│      [ ] hasInstallments   — instalment plans available   │
├───────────────────────────────────────────────────────────┤
│  🏪  SHOP TRUST                                           │
│      [ ] shopVerified      — verified shops only          │
│      Min trust score  [ minTrustScore ] / 5.00            │
├───────────────────────────────────────────────────────────┤
│  📈  POPULARITY                                           │
│      Min sold count  [ minSoldCount ]                     │
├───────────────────────────────────────────────────────────┤
│  ↕️  SORT BY                                              │
│      ○ TRENDING      ← formula-ranked                     │
│      ○ FOR_YOU       ← personalised relevance             │
│      ○ NEWEST        ← createdAt DESC                     │
│      ○ PRICE_ASC     ← cheapest first                     │
│      ○ PRICE_DESC    ← most expensive first               │
│      ○ MOST_SOLD     ← soldQuantity DESC                  │
│      ○ BEST_DEAL     ← highest discount % first           │
│      ○ MOST_VIEWED   ← viewCount DESC                     │
│      ○ MOST_CARTED   ← cartAddCount DESC                  │
└───────────────────────────────────────────────────────────┘

Endpoints


1. Main Discovery Feed

Purpose: Returns the primary marketplace product feed with optional filters and all sort strategies. Works for both anonymous and authenticated users — authenticated users receive personalised scoring automatically.

Endpoint: GET api/v1/e-commerce/marketplace/feed

Access Level: 🌐 Public (personalised when authenticated)

Authentication: Bearer Token (optional — improves results when provided)

Query Parameters:

Parameter Type Required Description Default
sortBy enum No Sort strategy. Values: TRENDING, FOR_YOU, NEWEST, PRICE_ASC, PRICE_DESC, MOST_SOLD, BEST_DEAL, MOST_VIEWED, MOST_CARTED TRENDING
page integer No Page number (1-based) 1
size integer No Items per page 20
minPrice decimal No Minimum product price
maxPrice decimal No Maximum product price
categoryId UUID No Filter by product category
condition enum No NEW, USED, REFURBISHED
productType enum No PHYSICAL, DIGITAL
inStock boolean No true = in-stock products only
onSale boolean No true = discounted products only
hasActiveGroup boolean No true = products with a live OPEN group right now
shopVerified boolean No true = verified shops only

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Feed retrieved successfully",
  "action_time": "2026-06-04T10:30:45",
  "data": {
    "content": [
      {
        "productId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "productName": "Samsung Galaxy S24",
        "productSlug": "samsung-galaxy-s24-techstore",
        "primaryImage": "https://cdn.nexgate.com/products/galaxy-s24.jpg",
        "productType": "PHYSICAL",
        "price": 850000.00,
        "comparePrice": 1050000.00,
        "discountPercentage": 19.05,
        "stockQuantity": 42,
        "soldQuantity": 318,
        "viewCount": 2741,
        "cartAddCount": 195,
        "urgencyTag": "LOW_STOCK",
        "condition": "NEW",
        "inStock": true,
        "onSale": true,
        "hasInstallments": true,
        "shopId": "7cb3a812-1234-4abc-b3fc-9d84f55bce12",
        "shopName": "TechStore Tanzania",
        "shopSlug": "techstore-tanzania",
        "shopLogoUrl": "https://cdn.nexgate.com/shops/techstore-logo.jpg",
        "shopVerified": true,
        "shopTrustScore": 4.80,
        "categoryId": "a1b2c3d4-1234-5678-abcd-ef0123456789",
        "categoryName": "Smartphones",
        "hasActiveGroup": true,
        "activeGroupHeat": 0.78,
        "activeGroupPrice": 720000.00,
        "activeGroupSeatsLeft": 4,
        "activeGroupExpiresAt": "2026-06-04T18:00:00",
        "createdAt": "2026-05-28T09:15:00"
      }
    ],
    "currentPage": 1,
    "pageSize": 20,
    "totalElements": 1284,
    "totalPages": 65,
    "hasNext": true,
    "hasPrevious": false
  }
}

Success Response Fields:

Field Description
content Array of product cards for this page
content[].productId Unique product identifier
content[].primaryImage URL of the first product image
content[].price Current selling price
content[].comparePrice Original price (present only if product is on sale)
content[].discountPercentage Calculated discount % — null if not on sale
content[].soldQuantity Total units sold — visible only if shop has enabled this
content[].viewCount Cumulative public views since tracking began
content[].cartAddCount Cumulative times added to any cart
content[].urgencyTag NONE, LIMITED_TIME, LOW_STOCK, FLASH_SALE
content[].hasActiveGroup true if there is a live OPEN group purchase right now
content[].activeGroupHeat Group fill ratio — 0.0 (empty) to 1.0 (full). null if no active group
content[].activeGroupPrice Discounted price available inside the active group
content[].activeGroupSeatsLeft Remaining seats in the active group
content[].activeGroupExpiresAt When the active group purchase expires
content[].shopTrustScore Shop trust rating from 0.00 to 5.00
currentPage Current page number (1-based)
totalElements Total matching products across all pages
hasNext / hasPrevious Pagination navigation flags

Standard Error Types:

  • 500 INTERNAL_SERVER_ERROR: Unexpected server failure

Purpose: Returns products ranked by the trending score formula. Anonymous users receive the global trending score. Authenticated users receive a personalised ranking — products from subscribed shops receive a +0.25 score boost so they float near the top.

Endpoint: GET api/v1/e-commerce/marketplace/trending

Access Level: 🌐 Public (personalised when authenticated)

Authentication: Bearer Token (optional)

Scoring applied:

Note: The database pre-sorts by soldQuantity DESC as an approximation, then the full formula re-ranks within each page. This means the absolute order across pages may differ slightly from a pure formula sort — which is intentional (pagination stability).

Query Parameters:

Parameter Type Required Description Default
page integer No Page number (1-based) 1
size integer No Items per page 20
categoryId UUID No Filter by category
minPrice decimal No Minimum price
maxPrice decimal No Maximum price
inStock boolean No In-stock only
onSale boolean No On sale only
shopVerified boolean No Verified shops only

Success Response: Same structure as the Main Feed endpoint.

Standard Error Types:

  • 500 INTERNAL_SERVER_ERROR: Unexpected server failure

3. For You — Personalised Recommendations

Purpose: Returns products ranked by relevance to the authenticated user based on their cart categories and shop subscriptions. Falls back to the trending feed for unauthenticated users or users with no cart items and no subscriptions.

Endpoint: GET api/v1/e-commerce/marketplace/for-you

Access Level: 🌐 Public (best results when authenticated)

Authentication: Bearer Token (optional — falls back to trending if absent)

Scoring applied:

relevanceScore =
    categoryMatch  × 0.40   (product's category is in user's cart)
  + favShopBoost   × 0.40   (shop is in user's subscriptions)
  + trendingScore  × 0.20   (global popularity tiebreaker)

Pool strategy: fetches 3× the requested page size (max 150), scores in Java,
then slices the requested page from the ranked result.

Query Parameters:

Parameter Type Required Description Default
page integer No Page number (1-based) 1
size integer No Items per page 20
categoryId UUID No Narrow recommendations to a specific category
inStock boolean No In-stock products only
shopVerified boolean No Verified shops only

Success Response: Same structure as the Main Feed endpoint.

Standard Error Types:

  • 500 INTERNAL_SERVER_ERROR: Unexpected server failure

4. Hot Deals

Purpose: Returns products currently on sale, ranked by highest discount percentage (biggest saving first).

Endpoint: GET api/v1/e-commerce/marketplace/hot-deals

Access Level: 🌐 Public

Authentication: None required

Scoring applied:

discountPercentage = (comparePrice - price) / comparePrice × 100

Products with no comparePrice or comparePrice ≤ price are excluded.
Sorted by discountPercentage DESC within each page.

Query Parameters:

Parameter Type Required Description Default
page integer No Page number (1-based) 1
size integer No Items per page 20
categoryId UUID No Filter by category
minPrice decimal No Minimum price after discount
maxPrice decimal No Maximum price after discount
shopVerified boolean No Verified shops only

Success Response: Same structure as the Main Feed endpoint. discountPercentage is always present and non-null in this feed.

Standard Error Types:

  • 500 INTERNAL_SERVER_ERROR: Unexpected server failure

5. New Arrivals

Purpose: Returns recently published products, newest first. Ideal for users who want to discover what just dropped.

Endpoint: GET api/v1/e-commerce/marketplace/new-arrivals

Access Level: 🌐 Public

Authentication: None required

Sorting: createdAt DESC — exact database-level sort, no formula applied.

Query Parameters:

Parameter Type Required Description Default
page integer No Page number (1-based) 1
size integer No Items per page 20
categoryId UUID No Filter by category
productType enum No PHYSICAL or DIGITAL
shopVerified boolean No Verified shops only

Success Response: Same structure as the Main Feed endpoint.

Standard Error Types:

  • 500 INTERNAL_SERVER_ERROR: Unexpected server failure

6. Live Group Purchases

Purpose: Returns products that currently have an active OPEN group purchase, sorted by group heat (most seats filled = most urgent = shown first). Useful for showing users time-sensitive social buying opportunities.

Endpoint: GET api/v1/e-commerce/marketplace/live-groups

Access Level: 🌐 Public

Authentication: None required

Sorting applied:

groupHeat = seatsOccupied / totalSeats

Products sorted by groupHeat DESC — a group at 90% capacity appears
before one at 30%, creating urgency awareness for the user.

Query Parameters:

Parameter Type Required Description Default
page integer No Page number (1-based) 1
size integer No Items per page 20

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Live group purchases retrieved successfully",
  "action_time": "2026-06-04T10:30:45",
  "data": {
    "content": [
      {
        "productId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "productName": "Samsung Galaxy S24",
        "price": 850000.00,
        "hasActiveGroup": true,
        "activeGroupHeat": 0.92,
        "activeGroupPrice": 720000.00,
        "activeGroupSeatsLeft": 2,
        "activeGroupExpiresAt": "2026-06-04T18:00:00"
      }
    ],
    "currentPage": 1,
    "pageSize": 20,
    "totalElements": 37,
    "totalPages": 2,
    "hasNext": true,
    "hasPrevious": false
  }
}

Standard Error Types:

  • 500 INTERNAL_SERVER_ERROR: Unexpected server failure

7. Advanced Filter

Purpose: The most powerful endpoint in the marketplace. Combines every available filter with an optional keyword search (?q=) so users can express highly specific queries like: "show me NEW Samsung smartphones under 1M from verified shops with an active group deal saving at least 20%, sorted by trending."

Endpoint: GET api/v1/e-commerce/marketplace/advanced-filter

Access Level: 🌐 Public (personalised scoring when authenticated)

Authentication: Bearer Token (optional — enables FOR_YOU and personalised TRENDING)

Scoring applied per sortBy:

┌────────────────┬────────────────────────────────────────────────────────┐
│ sortBy         │ How it works                                           │
├────────────────┼────────────────────────────────────────────────────────┤
│ TRENDING       │ Full formula re-rank in Java after DB fetch            │
│ FOR_YOU        │ Relevance formula re-rank in Java after DB fetch       │
│ NEWEST         │ createdAt DESC — exact DB sort                         │
│ PRICE_ASC      │ price ASC — exact DB sort                              │
│ PRICE_DESC     │ price DESC — exact DB sort                             │
│ MOST_SOLD      │ soldQuantity DESC — exact DB sort                      │
│ BEST_DEAL      │ discount % DESC — re-ranked in Java after DB fetch     │
│ MOST_VIEWED    │ viewCount DESC — exact DB sort                         │
│ MOST_CARTED    │ cartAddCount DESC — exact DB sort                      │
└────────────────┴────────────────────────────────────────────────────────┘

Post-fetch Java filters (applied after DB query — totalElements reflects pre-filter count):

  • hasMultipleColors — checks colors.size() > 1 in memory
  • maxGroupSeatsLeft — checks active group seats remaining in memory
  • minGroupDiscountPercent — calculates group discount % in memory

Query Parameters:

Group Parameter Type Required Description Default
Sort & Page sortBy enum No Sort strategy (see table above) TRENDING
page integer No Page number (1-based) 1
size integer No Items per page 20
Search q string No Keyword — searches product name and description (case-insensitive LIKE)
Price minPrice decimal No Minimum price
maxPrice decimal No Maximum price
Product categoryId UUID No Filter by product category
condition enum No NEW, USED, REFURBISHED
productType enum No PHYSICAL, DIGITAL
urgencyTag enum No NONE, LIMITED_TIME, LOW_STOCK, FLASH_SALE
hasMultipleColors boolean No true = colour variant products only (post-fetch)
Availability inStock boolean No true = in-stock only
minStockQuantity integer No Minimum stock units (bulk buyers)
Deals onSale boolean No true = discounted only
hasGroupBuying boolean No true = group buying enabled on product
hasActiveGroup boolean No true = live OPEN group right now
maxGroupSeatsLeft integer No Max seats remaining in active group (post-fetch)
minGroupDiscountPercent integer No Min group discount % e.g. 20 (post-fetch)
hasInstallments boolean No true = instalment plans available
Shop Trust shopVerified boolean No true = verified shops only
minTrustScore decimal No Min shop trust score e.g. 4.00 (0.00–5.00)
Popularity minSoldCount integer No Min total units sold

Example Requests:

GET /marketplace/advanced-filter
  ?q=samsung
  &categoryId=a1b2c3d4-...
  &onSale=true
  &shopVerified=true
  &sortBy=TRENDING

Find live group deals saving at least 25% with fewer than 5 seats left:

GET /marketplace/advanced-filter
  ?hasActiveGroup=true
  &minGroupDiscountPercent=25
  &maxGroupSeatsLeft=5
  &sortBy=BEST_DEAL

Find digital products under 50,000 newly published, for anonymous browsing:

GET /marketplace/advanced-filter
  ?productType=DIGITAL
  &maxPrice=50000
  &sortBy=NEWEST

Success Response: Same structure as the Main Feed endpoint.

Standard Error Types:

  • 500 INTERNAL_SERVER_ERROR: Unexpected server failure

MarketplaceProductResponse — Full Field Reference

Field Type Nullable Description
productId UUID No Unique product identifier
productName string No Product display name
productSlug string No URL-safe unique slug
primaryImage string Yes URL of first product image
productType enum No PHYSICAL or DIGITAL
price decimal No Current selling price
comparePrice decimal Yes Original price — present only when product is on sale
discountPercentage decimal Yes (comparePrice - price) / comparePrice × 100null if not on sale
stockQuantity integer No Available stock units
soldQuantity integer No Total units sold
viewCount long No Cumulative public views
cartAddCount long No Cumulative first-time cart adds
urgencyTag enum No NONE, LIMITED_TIME, LOW_STOCK, FLASH_SALE
condition enum No NEW, USED, REFURBISHED
inStock boolean No true if stockQuantity > 0
onSale boolean No true if comparePrice > price
hasInstallments boolean No true if instalment plans exist
shopId UUID No Owning shop identifier
shopName string No Shop display name
shopSlug string No Shop URL slug
shopLogoUrl string Yes Shop logo image URL
shopVerified boolean No Whether shop has passed verification
shopTrustScore decimal No Shop trust rating 0.00–5.00
categoryId UUID Yes Product category identifier
categoryName string Yes Product category display name
hasActiveGroup boolean No true if a live OPEN group exists
activeGroupHeat decimal Yes Fill ratio 0.0–1.0 of hottest live group
activeGroupPrice decimal Yes Discounted group price
activeGroupSeatsLeft integer Yes Remaining seats in active group
activeGroupExpiresAt datetime Yes Expiry of active group purchase
createdAt datetime No When the product was published

Quick Reference — All Marketplace Endpoints

# Endpoint Auth Sort Mode
1 GET /marketplace/feed Optional All MarketplaceSortBy values
2 GET /marketplace/trending Optional Formula-ranked
3 GET /marketplace/for-you Optional Relevance-ranked
4 GET /marketplace/hot-deals None Discount % DESC
5 GET /marketplace/new-arrivals None createdAt DESC
6 GET /marketplace/live-groups None Group heat DESC
7 GET /marketplace/advanced-filter Optional All MarketplaceSortBy values