New: Application API docs
Pick the API you are reading here — the Merchant API for your own store, the Application API for partner apps.
For partner applications: browse merchants and products, hand customers a checkout link, and follow the orders it creates.
Sellable products from your visible merchants: in stock (or pre-order) and visible in the catalogue. One product model parses both the list and the detail.
Product search and browse.
/api/third-party/v1/applications/products
| Field | Type | Required | Description |
|---|---|---|---|
query
|
string | Optional | Search over product and merchant name. Multilingual — iphone and ايفون find the same products; common misspellings and synonyms are handled. |
client_id
|
integer | Optional | Narrow to one of your merchants. Outside your scope → empty page. |
categories
|
string | Optional | Comma-separated category ids, e.g. 1,4,7. CSV only — categories[]=1 is a 400. |
sub_categories
|
string | Optional | Comma-separated subcategory ids. CSV only. |
min_price
|
integer | Optional | Inclusive price floor. |
max_price
|
integer | Optional | Inclusive price ceiling. |
is_gini_partner
|
1 / 0 | Optional | 1 → only products in an active campaign (see Campaign commission rates below). |
sort_by
|
string | Optional | trending, newest, lowest_price, highest_price, old, max_review. Without it, a typed query ranks by relevance; browsing follows each merchant's configured order. |
page
|
integer | Optional | Default 1. |
page_size
|
integer | Optional | Max 100. Always send it — the default differs by search backend (7 or 10). |
Each item is the full storefront product payload — id, name, slug, price, discounted_price,
images, ratings, stock, category/subcategory ids, installment fields and an embedded client
summary. Parse list items and product details with one
model.
The embedded client carries one field specific to this API:
| Field | Notes |
|---|---|
| client.median_response_minutes | The merchant's typical reply time, in minutes. null when not yet measured (new merchants, or no measurable replies) — render it as unknown, never as 0, which would read as an instant-reply badge. |
If your application is granted campaign access, every product carries two extra keys. Without the grant they
are absent — not null — so their presence is itself the signal that you have it.
{
"id": 266401,
"name": "Wireless Headphones",
"has_campaign": true,
"campaign": {
"gini_market_commission": 5,
"influencer_gini_rate": 0.02,
"influencer_merchant_rate": 0.01,
"ends_at": "2026-12-31T00:00:00+00:00"
}
}
| Field | Notes |
|---|---|
| has_campaign | Whether this product has a running campaign. false for a product without one, and for one whose campaign has ended. |
| campaign | The rates, or null when has_campaign is false. |
| campaign.gini_market_commission | The commission Gini takes on this product, as a percentage. |
| campaign.influencer_gini_rate | The influencer's share, paid from Gini's side. |
| campaign.influencer_merchant_rate | The influencer's share, paid from the merchant's side. |
| campaign.ends_at | ISO-8601, or null for an open-ended campaign. |
One product, addressed by its slug (the slug field from List products), not
its id. No query parameters are read — is_gini_partner has no effect here; the campaign block
appears whenever you hold the grant.
/api/third-party/v1/applications/products/{slug}
{
"data": {
"id": 266401,
"name": "iPhone 15 Pro 256GB",
"slug": "44e9f361-bff2-4f13-9002-9ec214ce4e7f",
"price": 1750000,
"discounted_price": 1650000,
"quantity": 12,
"images": [
"https://.../1.webp",
"https://.../2.webp"
],
"client": {
"id": 42,
"business_name": "SoundWave",
"median_response_minutes": 14
}
}
}
The body is the same product payload as a list item, so a detail response is a superset of it.
| Status | Body | Meaning |
|---|---|---|
| 404 | {"key": "errors.item_not_found", "message": "…"} |
No such slug, or its merchant is not live. |
| 404 | {"message": "Product not found"} |
The product exists but its merchant is outside your scope. |