Trendyol is the dominant e-commerce marketplace in Turkey and the largest by order volume, with expansions into the Gulf, Central and Eastern Europe and Azerbaijan. The Partner API, documented at developers.trendyol.com, is refreshingly conventional after the Alibaba-derived platforms: plain HTTPS basic authentication, no request signing, a single gateway host and REST resources nested under a seller id. The two things that catch first-time integrators are a mandatory User-Agent header with a prescribed format, which returns 403 if omitted, and the fact that every catalogue write is asynchronous and returns a batchRequestId that must then be polled. Orders are modelled as shipment packages rather than orders, which changes the shape of the data more than it first appears.
At a glance
Two dated migrations are in force. V1 product services are switched off on 15 October 2026 and sellers must move to the V2 product services. On the same date, getShipmentPackages gains a hard 10,000 record query window and is limited to the last one month of data, with getShipmentPackagesStream as the replacement for any full scan or periodic sync. Separately, new product service rate limits take effect on 14 September 2026, moving from per-endpoint limits to shared limit groups. Build against V2 and the stream endpoint from the start. All dates verified on 2026-09-21.
What it is
Trendyol was founded in 2010 and is majority owned by Alibaba Group, with Turkish institutional and sovereign fund shareholders. It runs a marketplace where third party sellers list into a shared catalogue with a buybox, alongside Trendyol's own private labels, plus Trendyol Express as an in-house carrier, Trendyol GO for food and quick commerce, and Dolap for resale.
Geography matters for the connector. The core marketplace is Turkey. Beyond it, Trendyol operates a Gulf region, a Central and Eastern Europe region and Azerbaijan, and cross border models called Mikro İhracat (micro export) and Trendyol Yurt Dışı Aracılığı (Trendyol acting as the overseas intermediary). Those models change what arrives in the order: in the intermediary model the invoice address carries Trendyol's own company details rather than the buyer's, and customer names are replaced with Trendyol entity names. A storeFrontCode header, TR by default, selects the storefront on several endpoints, and there is a full address reference API with separate shapes for Turkey, the Gulf, CEE and Azerbaijan.
Fulfilment is seller fulfilled with a Trendyol-assigned carrier in most cases. Trendyol creates the shipment package, assigns the cargo tracking number and provides the label; the seller moves the package through statuses.
API access
There is no developer programme and no app review. A seller who can log in as the master user gets credentials immediately.
- Log into the Trendyol seller panel as the master user (admin role). Sub-users cannot see the credentials.
- Go to Hesap Bilgilerim, then the Entegrasyon Bilgileri tab, at partner.trendyol.com/account/info.
- Read off the supplier id (also called seller id), the API key and the API secret key.
That is the whole onboarding. Production needs no IP allowlisting. The stage environment does: Trendyol must add your servers' egress addresses before stage will answer, and an unlisted address gets HTTP 503 rather than 401. Stage credentials are entirely separate from production and come from a separate panel at stagepartner.trendyol.com. Stage access and IP allowlisting are requested through a seller support ticket.
For a multi-tenant connector this shape is unusually easy: there is no OAuth redirect and nothing to install, so onboarding a seller is a form with three fields. The cost is that the credential is a long-lived secret the seller pastes in, with no scoping and no per-app revocation.
Versions: V1 and V2 product services coexist today, with V1 retiring on 15 October 2026. Order services are on a v2 path with a v3 package listing also documented. The documentation is in Turkish; every page is available as markdown by appending .md to its URL, and developers.trendyol.com/llms.txt is a complete index of those markdown URLs, which is the fastest way to read the whole reference.
Authentication
Hosts
Everything lives under /integration/, then a service segment, then sellers/{sellerId}. For example https://apigw.trendyol.com/integration/order/sellers/123456/orders.
Credentials and headers
Basic authentication, with the API key as the username and the API secret key as the password. There is no token exchange, no expiry and no refresh. Incorrect credentials return HTTP 401 with "exception": "ClientApiAuthenticationException".
The User-Agent header is mandatory and its format is prescribed. A request without it is rejected with HTTP 403. The value is the seller id, a space, a hyphen, a space, and then either the integrator company name or the literal SelfIntegration if the seller built the integration themselves. The integrator name is alphanumeric, maximum 30 characters.
Seller 1234, integrator TrendyolSoft -> User-Agent: 1234 - TrendyolSoft Seller 4321, own software -> User-Agent: 4321 - SelfIntegration
A complete authenticated call:
curl -X GET \ "https://apigw.trendyol.com/integration/order/sellers/123456/orders?page=0&size=200&orderByField=PackageLastModifiedDate&orderByDirection=DESC" \ -u "MY_API_KEY:MY_API_SECRET" \ -H "User-Agent: 123456 - SelfIntegration" \ -H "Content-Type: application/json"
Some endpoints take two further headers: storeFrontCode to select the storefront (TR for Turkey) and Accept-Language for localised reference data.
Multi-account
Each seller is one set of credentials and one sellerId, which appears in the path of almost every endpoint. There is no parent or merchant concept to model. Store sellerId, key, secret and storefront code per connection, and include the seller id in the User-Agent for that connection.
Objects we can read
Orders
Trendyol does not expose an order object. It exposes shipment packages: after payment clears, the system automatically packs an order into one or more packages, and a package is the unit you list, update and ship. One orderNumber can produce several shipmentPackageId values, and a partial cancellation or a split creates more.
GET /integration/order/sellers/{sellerId}/orders is the page based listing. Parameters: startDate and endDate as millisecond timestamps, page zero based, size up to 200, orderNumber, status, shipmentPackageIds, orderByField (PackageLastModifiedDate) and orderByDirection. Only the last one month of history is queryable through this service.
Package statuses: Awaiting, Created, Picking, Invoiced, Shipped, Cancelled, Delivered, UnDelivered, Returned, UnSupplied. Awaiting means payment has not cleared; the documentation is explicit that such packages must be used for stock reservation only and not shipped, and that Trendyol accepts no responsibility for cancellations if you ship them anyway.
From 15 October 2026 this endpoint is capped at a 10,000 record query window, counted by shipmentPackageId. With size=200 that means page=0 through page=49 are the only safely reachable pages, and totalElements in the response tells you whether the filter matched more than that. The documented remedies are to narrow the date range, narrow the filter, or switch to the stream endpoint.
GET /integration/order/sellers/{sellerId}/orders/stream is the cursor based replacement, and the one to build on. Parameters: size, nextCursor, packageItemStatuses, lastModifiedStartDate, lastModifiedEndDate, plus the storeFrontCode header. Results come back in a fixed lastModifiedDate descending order; pass the returned nextCursor until hasMore is false.
A trimmed package, from the official sample:
{
"totalElements": 1,
"totalPages": 1,
"page": 0,
"size": 1,
"content": [
{
"orderNumber": "10654411111",
"shipmentPackageId": 3330111111,
"supplierId": 99999999,
"orderDate": 1762253333685,
"currencyCode": "TRY",
"orderCountryCode": "TR",
"channelId": 25,
"paymentMethod": "Kredi Kartı",
"packageGrossAmount": 498.90,
"packageSellerDiscount": 0.00,
"packageTyDiscount": 0.00,
"packageTotalDiscount": 0.00,
"discountDisplays": [{ "displayName": "Sepette %20 İndirim", "discountAmount": 100 }],
"cargoProviderName": "Trendyol Express",
"cargoTrackingNumber": 7280027504111111,
"cargoTrackingLink": "https://tracking.trendyol.com/?id=111111111-1111-1111-1111-11111111",
"cargoSenderNumber": "210090111111",
"customerId": 888888888,
"customerFirstName": "John",
"customerLastName": "Doe",
"customerEmail": "pf+j2jm8x99@trendyolmail.com",
"identityNumber": "11111111111",
"shipmentAddress": {
"id": 11111111,
"fullName": "John Doe",
"phone": "333333333",
"address1": "John Doe's House",
"neighborhood": "Maslak Mahallesi",
"district": "Sarıyer",
"districtId": 54,
"city": "İstanbul",
"cityCode": 34,
"postalCode": "34200",
"countryCode": "TR"
},
"invoiceAddress": { "fullName": "John Doe", "taxOffice": "...", "taxNumber": "..." },
"lines": [
{
"lineId": 4765111111,
"barcode": "8683772071724",
"stockCode": "111111",
"contentId": 1239111111,
"productName": "Kuş ve Çiçek Desenli Tepsi - Yeşil, Tek Ebat",
"productSize": "Tek Ebat",
"productColor": "Yeşil",
"productCategoryId": 2710,
"quantity": 1,
"currencyCode": "TRY",
"lineGrossAmount": 498.90,
"lineUnitPrice": 498.90,
"lineTotalDiscount": 0.00,
"lineSellerDiscount": 0.00,
"lineTyDiscount": 0.00,
"discountDetails": [
{ "lineItemId": "11111111", "lineItemPrice": 498.90, "lineItemSellerDiscount": 0.00, "lineItemTyDiscount": 0.00 }
],
"vatRate": 20.00,
"commission": 13,
"orderLineItemStatusName": "Delivered",
"cancelledBy": "",
"cancelReason": ""
}
],
"packageHistories": [{ "createdDate": 1762242537624, "status": "Created" }]
}
]
}
Watch the timezones: orderDate is a millisecond timestamp expressed in GMT+3, while createdDate values inside packageHistories are GMT. That is stated explicitly in the documentation and is an easy three hour bug.
customerEmail, customerFirstName, customerLastName, identityNumber, shipmentAddress and invoiceAddress are all PII and, unusually for a marketplace, they arrive unmasked. The email is a Trendyol relay address on trendyolmail.com rather than the buyer's own. identityNumber is a Turkish national identity number. Treat this record as high sensitivity.
invoiceStatus on the package tells you whether the seller's invoice has been accepted: NotInvoiced, Received, Rejected or Invoiced. Only an Invoiced package returns a populated invoiceLink, and for micro export and intermediary orders the shipping label is not returned at all until that status is reached.
Order items
Items are the lines array on the package. Each line is a barcode with a quantity, so a line is a line and not a unit. barcode is the join key to the catalogue, stockCode is the seller's own code and contentId is Trendyol's product content identifier. Money is per unit: lineGrossAmount is the undiscounted unit price, lineUnitPrice is the net unit price after both discounts, and discountDetails breaks the discount down per individual item within the line, which is what you need if a three-unit line was partly cancelled.
commission on the line is the commission rate as a percentage, not an amount. orderLineItemStatusName carries a per-line status, so a package can be partly delivered and partly cancelled.
Products and listings
The V2 product services split reads by approval state, which is a real distinction on Trendyol: a newly created product sits unapproved until content moderation clears it.
GET /integration/product/sellers/{sellerId}/products/approvedfor live productsGET /integration/product/sellers/{sellerId}/products/unapprovedfor pending onesGET /integration/product/sellers/{sellerId}/products/approved/inventory-and-pricefor a lighter price and stock only viewGET /integration/product/sellers/{sellerId}/product/{barcode}for one product
The approved filter takes barcode, startDate, endDate, dateQueryType (for example CONTENT_MODIFIED_DATE or CREATED_DATE), page, size up to 1000, stockCode, productMainId, brandIds, status, supplierId and nextPageToken, plus the storeFrontCode and Accept-Language headers. Note that it offers both page based paging and a nextPageToken; prefer the token.
{
"totalElements": 1,
"totalPages": 1,
"page": 0,
"size": 20,
"nextPageToken": "eyJzb3J0IjpbMTI3MTU4MTVdfQ==",
"content": [
{
"contentId": 12715815,
"productMainId": "12613876842A60",
"title": "Açık Gri T-",
"description": "...",
"brand": { "id": 315675, "name": "GUEYA" },
"category": { "id": 91266, "name": "..." },
"creationDate": 1760531038063,
"lastModifiedDate": 1760938781669,
"images": [{ "url": "/mediacenter-stage3/stage/QC_PREP/.../1.jpg" }],
"attributes": [
{ "attributeId": 47, "attributeName": "Renk", "attributeValue": "Black" },
{ "attributeId": 296, "attributeName": "Cinsiyet", "attributeValueId": 2873, "attributeValue": "Erkek" }
],
"variants": [
{
"variantId": 70228905,
"barcode": "12613876842A60",
"stockCode": "STK-stokum-1",
"supplierId": 99999999,
"commission": 7.83,
"vatRate": 0,
"origin": "AD",
"onSale": false,
"channels": ["CORE", "LUXE"],
"productUrl": "https://stage.trendyol.com/abc/xyz-p-12715815?&merchantId=99999999",
"attributes": [{ "attributeId": 293, "attributeName": "Beden", "attributeValue": "77 x 200 cm" }],
"stock": { "quantity": 0, "lastModifiedDate": 1774948958844 },
"price": { "salePrice": 222, "listPrice": 222, "priceSeenByCustomer": 169 },
"deliveryOption": { "deliveryDuration": 0 },
"locked": false,
"lockReason": null,
"archived": false,
"hasViolation": false,
"blacklisted": false
}
]
}
]
}
The hierarchy is productMainId, the grouping key the seller chooses, then contentId, Trendyol's content record, then variants[] each identified by a barcode. The barcode is the sellable unit and the key every write uses. priceSeenByCustomer is the price after Trendyol-funded discounts, which can be below the seller's salePrice, and it is the number to use when comparing against a competitor's shelf price. locked with lockReason means Trendyol has frozen the listing, and hasViolation and blacklisted flag compliance problems.
Reference data: GET /integration/product/brands and /brands/by-name, GET /integration/product/product-categories, GET /integration/product/categories/{categoryId}/attributes and /attributes/{attributeId}/values, and GET /integration/product/cargo-providers.
Inventory
Stock is on the variant as stock.quantity with its own lastModifiedDate, and inventory-and-price is the cheap endpoint for polling it. The documentation is explicit that quantity is sellable stock: it decreases when an order is taken and only rises again when the seller sends a new value. There is no per-warehouse breakdown on the marketplace listing, and the documented maximum stock per product is 20,000 units.
Shipments and tracking
There is no separate shipment object and no tracking event API. The package carries cargoProviderName, cargoTrackingNumber, cargoTrackingLink and cargoSenderNumber, and packageHistories[] is the status timeline: an array of {createdDate, status} entries covering the package's own lifecycle rather than carrier scans. For carrier level events, follow cargoTrackingLink.
cargoTrackingNumber is rendered as a CODE128 barcode in the seller panel, which matters if you print labels yourself. GET /integration/sellers/{sellerId}/common-label/{cargoTrackingNumber} requests and then retrieves a Trendyol-generated common label.
Returns and cancellations
Returns are claims. GET /integration/order/sellers/{sellerId}/claims takes claimIds, claimItemStatus, startDate and endDate (both filtering on the claim package's lastModifiedDate), orderNumber, page and size. Passing claimIds overrides every other filter.
{
"totalElements": 2099,
"page": 0,
"size": 1,
"content": [
{
"id": "f9da2317-876b-4b86-b8f7-0535c3b65731",
"claimId": "f9da2317-876b-4b86-b8f7-0535c3b65731",
"orderNumber": "65745805",
"orderDate": 1524826343886,
"claimDate": 1525844162827,
"lastModifiedDate": 1723275767111,
"orderShipmentPackageId": 3853354,
"orderOutboundPackageId": 58835111,
"customerFirstName": "John",
"customerLastName": "Doe",
"cargoProviderName": "Aras Kargo Marketplace",
"cargoTrackingNumber": 72602420957047272632,
"items": [
{
"orderLine": {
"id": 28717255,
"barcode": "99999999999",
"merchantSku": "2052551",
"productName": "Erkek Bebek Soket Çorap 4'lü",
"productSize": "17-18 (6-12 Ay)",
"productColor": "MIX YARN DYED K00",
"productCategory": "Çorap",
"price": 9.95,
"vatBaseAmount": 8,
"vatRate": 8
},
"claimItems": [
{
"id": "b71461e3-d1a0-4c1d-9a6d-18ecbcb5432d",
"orderLineItemId": 29815494,
"customerClaimItemReason": { "id": "451", "name": "Diğer", "externalReasonId": 23, "code": "UNFIT" },
"trendyolClaimItemReason": { "id": "451", "name": "Diğer", "externalReasonId": 23, "code": "UNFIT" },
"claimItemStatus": { "name": "Rejected" },
"customerNote": "Müşteri notu",
"resolved": false,
"autoAccepted": false,
"acceptedBySeller": true
}
]
}
]
}
]
}
Two reason objects appear per claim item: the buyer's stated reason and Trendyol's own assessment, which can differ. autoAccepted records that the claim was accepted automatically because the seller took no action within 48 hours, which is worth tracking as an operational metric in its own right. replacementOutboundpackageinfo and rejectedPackageInfo appear when the claim produces an exchange or a rejected return being shipped back.
Claim writes: PUT .../claims/{claimId}/items/approve accepts, POST .../claims/{claimId}/issue raises a rejection, GET .../claims/issue-reasons lists the valid rejection reasons, and GET .../claims/{claimId}/items/{claimItemId}/audits returns the audit trail.
Cancellations before shipping are not claims. They appear on the package as status Cancelled, and on the line as cancelledBy, cancelReason and cancelReasonCode.
Payments and settlements
GET /integration/finance/sellers/{sellerId}/settlements is the ledger. transactionType or transactionTypes is required, startDate and endDate are millisecond timestamps and the range cannot exceed 15 days, or paymentDate can be used instead. size goes to 500.
The transactionType enumeration, verbatim: Sale, Return, Discount, DiscountCancel, Coupon, CouponCancel, ProvisionPositive, ProvisionNegative, ManualRefund, ManualRefundCancel, TyDiscount, TyDiscountCancel, TyCoupon, TyCouponCancel, SellerRevenuePositive, SellerRevenueNegative, CommissionPositive, CommissionNegative, SellerRevenuePositiveCancel, SellerRevenueNegativeCancel, CommissionPositiveCancel, CommissionNegativeCancel, DeliveryFee, DeliveryFeeCancel, PayByLink.
The documentation pairs them explicitly: SellerRevenuePositive must be read together with CommissionNegative, SellerRevenueNegative with CommissionPositive, and the two cancel variants likewise. A revenue line without its matching commission line is an incomplete picture.
Each FinancialTransaction carries id, transactionDate, transactionType, barcode, orderNumber, receiptId, description, debt, credit, commissionRate, commissionAmount, commissionInvoiceSerialNumber, sellerRevenue, paymentPeriod, paymentOrderId, paymentDate, sellerId, storeId, storeName and country. Money is split into debt and credit columns rather than a single signed amount.
Three companion endpoints: /other-financials for non-sale movements, filtered by transactionType and transactionSubType; /payment-orders for the bank payout level, which is where paymentOrderId resolves to an actual transfer; and /cargo-invoice-items for the shipping invoice breakdown.
Customers
No customer endpoint, but the order carries more identifying data than most marketplaces: customerId (stable per Trendyol account), first and last name, a relay email, a phone number on the address and identityNumber. That is enough to build a real customer record, and enough that it needs deliberate handling under data protection rules.
GET /integration/qna/sellers/{sellerId}/questions/filter returns buyer questions, with /questions/{questionId} and /questions/{questionId}/answers for detail and reply.
Locations
GET /integration/sellers/{sellerId}/addresses returns the seller's return, shipment and invoice addresses. Reference geography comes from the member service: /countries, /cities/{countryCode}, /districts/{countryCode}/{cityCode} for the international shapes, and /cities, /cities/{cityCode}/districts, /cities/{cityCode}/districts/{districtCode}/neighborhoods for Turkey, which goes down to neighbourhood level. Azerbaijan has its own pair.
Writing back: listings, price and stock
Every catalogue write is queued. The call returns a batchRequestId immediately and you poll for the outcome.
Price and stock
POST https://apigw.trendyol.com/integration/inventory/sellers/{sellerId}/products/price-and-inventory
Content-Type: application/json
{
"items": [
{ "barcode": "8680000000", "quantity": 100, "salePrice": 112.85, "listPrice": 113.85 }
]
}
{ "batchRequestId": "fa75dfd5-6ce6-4730-a09e-97563500000-1529854840" }
The documented rules, all of which will bite a naive sync loop:
- Maximum 1000 items per request.
listPriceis the struck-through reference price andsalePriceis the selling price. Either may be sent alone; the other is not required.quantityis sellable stock, maximum 20,000 per product.- Sending an identical request body again within 15 minutes is rejected with a message to that effect. Only send changed rows. This is a hard constraint on any "push the whole catalogue every hour" design.
- A per-barcode limit of 30 price update requests per minute applies; exceeding it surfaces as a per-barcode error in the batch result rather than a rejected call.
Checking the result
GET https://apigw.trendyol.com/integration/product/sellers/{sellerId}/products/batch-requests/{batchRequestId}
The response carries a top level status (COMPLETED), itemCount, failedItemCount, batchRequestType, creationDate, lastModification, and an items[] array where each element has the submitted requestItem echoed back plus its own status (SUCCESS or a failure) and a failureReasons[] array.
Two important caveats. A batch result is only retrievable for four hours. And for price and stock updates specifically, the top level batch status is not returned, so you must inspect the per-item statuses rather than waiting for an overall completion flag.
Listing content
POST /integration/product/sellers/{sellerId}/v2/products creates products. Before calling it, resolve the category with /product-categories, the mandatory attributes with /categories/{categoryId}/attributes, and the brand with /brands/by-name. A product body carries barcode, title, productMainId, brandId, categoryId, quantity, stockCode, origin, dimensionalWeight, description, currencyType, listPrice, salePrice, vatRate, cargoCompanyId, an images[] array of URLs and an attributes[] array of {attributeId, attributeValueId, customAttributeValue} where the value is either a Trendyol value id or free text depending on the attribute.
Updates are split by approval state and by what is changing: /products/unapproved-bulk-update, /products/content-bulk-update for approved content, /products/variant-bulk-update for approved variants and /products/delivery-info-bulk-update. GET /products/{contentId}/update-audits reports the outcome of a content update. DELETE /products removes products, PUT /products/archive-state archives them and PUT /products/unlock clears a lock. POST /products/buybox-information returns buybox position, which is the endpoint for a repricer.
Order fulfilment
All under /integration/order/sellers/{sellerId}/shipment-packages/{packageId}: PUT on the package root moves status, PUT /items/unsupplied declares an item cannot be supplied, PUT /box-info reports desi and box count, PUT /cargo-providers changes carrier, PUT /warehouse changes the dispatching warehouse, PUT /extended-agreed-delivery-date buys more preparation time, PUT /labor-costs reports a workmanship charge, and four split variants (/split, /split-by-quantity, /multi-split, /split-multi-package) break a package apart. Invoices go through POST /integration/sellers/{sellerId}/seller-invoice-links or /seller-invoice-file.
Webhooks and notifications
Trendyol webhooks are managed entirely through the API, which is unusual and welcome: no console step.
POST https://apigw.trendyol.com/integration/webhook/sellers/{sellerId}/webhooks
Content-Type: application/json
{
"url": "https://connectors.example.com/trendyol/hook",
"authenticationType": "API_KEY",
"apiKey": "123456",
"subscribedStatuses": ["CREATED", "PICKING", "INVOICED", "SHIPPED", "DELIVERED"]
}
GET, PUT and DELETE on the same collection list, update and remove a webhook, and PUT .../{webhookId}/activate and /deactivate toggle it.
Subscribable statuses: CREATED, PICKING, INVOICED, SHIPPED, CANCELLED, DELIVERED, UNDELIVERED, RETURNED, UNSUPPLIED, AWAITING, UNPACKED, AT_COLLECTION_POINT, VERIFIED. One webhook can subscribe to all of them.
Authentication on the callback. authenticationType is either BASIC_AUTHENTICATION, which requires username and password, or API_KEY, which requires apiKey and sends it in an x-api-key header. There is no HMAC signature over the body, so the shared secret is the only thing authenticating the caller. Combine it with a hard-to-guess path and, if possible, verify the source address.
Content. The request is a POST with a JSON body, and the body is the full order package in the same model getShipmentPackages returns, regardless of which status fired it, plus a createdBy field taking order-creation, cancel, split or transfer. That last value is notable: transfer means Trendyol reassigned the order to a different seller. Because the body is the whole package, a webhook handler can persist directly without a follow-up read, unlike most marketplaces.
Retry and deactivation. A failed delivery is retried every 5 minutes until it succeeds. If failures persist, Trendyol deactivates the webhook and sends two emails: one warning with the webhook id and how long delivery will continue to be attempted, and a second confirming deactivation. A deactivated webhook must be reactivated manually after the endpoint is fixed.
Constraints. Maximum 15 webhooks per seller, including deactivated ones. The callback URL must not contain the strings Trendyol, Dolap or Localhost. Trendyol's own documentation recommends running a periodic getShipmentPackages poll alongside webhooks rather than relying on them alone.
Rate limits and pagination
Trendyol publishes real numbers, and they are tiered by the seller's listing limit (50,000 / 75,000 / 150,000 / 500,000 products, or unlimited). There is also a blanket rule: maximum 50 requests to the same endpoint in any 10 second window, and the 51st returns HTTP 429 with too.many.requests.
From 14 September 2026, product limits are enforced per shared group rather than per endpoint. A seller on the 50,000 tier with a 200 request per minute Product Integration Write allowance who makes 50 creates, 100 updates and 50 deletes in one minute has exhausted the whole group.
Plus a per-barcode limit of 30 price updates per minute.
Order services, current published figures:
Other published limits: claims list 1000 per minute, but claim approve and claim issue only 5 per minute, which is a severe constraint on bulk return processing. Returns audit 1000 per minute. Finance, both the account statement and cargo invoice services, 100 per minute. Buyer questions 1000 per minute to read and 500 to answer. Common label 100 per minute each way. Suppliers addresses is 1 per hour, so cache it.
Pagination comes in three flavours: page and size with totalElements and totalPages for orders, claims and settlements; a nextPageToken on the approved product filter; and a nextCursor with hasMore on the order stream. The 15 day cap on settlements and the one month cap on order history both force a slicing loop for any backfill.
A workable cadence for a 50,000 tier seller: subscribe a webhook to all statuses for near real time capture; run getShipmentPackagesStream by lastModifiedStartDate every 15 minutes as the reconciliation pass, comfortably inside the 30 per minute ceiling; poll products/approved/inventory-and-price hourly and the full approved filter daily; pull settlements daily in 15 day slices when backfilling and single day slices when incremental; poll claims every 30 minutes and cache the addresses call for a day.
Mapping to the unified model
orders
order_items
listings and inventory
inventory: quantity_available is stock.quantity; quantity_reserved and quantity_inbound are not published and location_id is not returned on the listing.
shipments, returns and settlements
shipments: with no shipment object, derive it from the package. shipment_id from shipmentPackageId, carrier from cargoProviderName, awb from cargoTrackingNumber, status from the package status, and events[] from packageHistories[] mapping status and createdDate. shipped_at is the createdDate of the Shipped history entry and delivered_at that of Delivered. weight_grams is not returned; the seller reports desi through PUT /box-info. shipping_cost and ndr_reason are not available, though an UnDelivered status is the signal for a failed delivery.
returns: return_id from claimId, order_id from orderShipmentPackageId with orderNumber alongside, items[] from items[].claimItems[] keyed by orderLineItemId, reason from customerClaimItemReason.code and name with trendyolClaimItemReason kept separately because they can disagree, status from claimItemStatus.name, refund_amount derived from orderLine.price times the claimed quantity since no refund total is returned, initiated_at from claimDate and received_at not published.
settlements: one row per FinancialTransaction. settlement_id from paymentOrderId, order_id from orderNumber, period_start and period_end from the query window, gross_amount from credit on a Sale row, net_amount from sellerRevenue, paid_at from paymentDate, and fees[] from the commission and fee rows with type from transactionType and amount from debt or credit. Remember the documented pairing rule: revenue and commission rows must be reconciled together.
products: sku from stockCode, gtin from barcode where the seller used a real GTIN, brand and category from the nested objects, attributes from attributes[] and images from images[].url. hsn_code has no equivalent; vatRate is the nearest tax field.
locations: from GET /integration/sellers/{sellerId}/addresses, remembering the 1 request per hour limit.
Gaps and open questions
- Buyer shipping fee is missing from the order.
packageGrossAmountcovers goods. Delivery charges surface only asDeliveryFeerows in settlements, so order level economics cannot be closed from the order alone. - No refund total on a claim. It has to be reconstructed from the order line price and the claimed quantity, then reconciled against
Returnrows in settlements. - Carrier tracking events are not exposed.
packageHistoriesis Trendyol's own package lifecycle, not carrier scans. A tracking pipeline needscargoTrackingLinkor a carrier integration. - Webhook bodies are not signed. Only a shared secret in a header or basic auth. Ask whether signature support is planned.
- The 15 minute duplicate rejection on price and stock is stated as a message, not a documented status code. Confirm what the response actually looks like so the connector can classify it as a soft skip rather than a failure.
- Two migrations land within a month of each other (14 September and 15 October 2026), and the 10,000 record window plus one month history cap on
getShipmentPackageschanges what a backfill can even see. Anything built against the page based order endpoint needs a stream rewrite. - Per-endpoint order limits are per listing tier, so the same connector code has a five times different ceiling across two sellers. The tier is not exposed through the API and has to be asked for or inferred by hitting 429.
- Documentation is Turkish only. Field semantics in the comments inside the official JSON samples carry real meaning, and the markdown export preserves them, but there is no English reference.
Sources
- developers.trendyol.com, the official Trendyol developer documentation. Every page is available as markdown by appending
.md, and developers.trendyol.com/llms.txt indexes all of them. - Authorization guide (
/docs/2-authorization.md), for basic auth, the credential location in the seller panel, theUser-Agentformat and the 50 requests per 10 seconds rule. - Service limits (
/docs/1-servis-limitleri.md), for the tiered per-group and per-endpoint rate limit tables and the 14 September 2026 change. - Live and test environment information (
/docs/3-canlı-test-ortam-bilgileri.md), for the production and stage hosts and stage IP allowlisting. - getShipmentPackages (
/docs/sipariş-paketlerini-çekme-getshipmentpackages.md), for the order package model, the full official response sample, the status list, invoice statuses and the 15 October 2026 query window change. - Webhook Model (
/docs/webhook-model.md), for subscribable statuses, callback authentication, retry and deactivation behaviour and the 15 webhook cap. - updatePriceAndInventory (
/docs/stok-ve-fiyat-güncelleme-updatepriceandinventory.md) and getBatchRequestResult (/docs/toplu-i̇şlem-kontrolü-getbatchrequestresult.md), for the write body, batch semantics, the 1000 item cap and the 15 minute duplicate rule. - Approved product filtering V2 (
/docs/ürün-filtreleme-onaylı-ürün-v2.md), for the catalogue response sample and the variant model. - getClaims (
/docs/i̇adesi-oluşturulan-siparişleri-çekme-getclaims.md), for the claim model and sample. - getSettlements OpenAPI reference (
/reference/getsettlements.md), for the transaction type enumeration, theFinancialTransactionschema and the 15 day range limit. - reealys/trendyol-marketplace-api-postman, a Postman collection built from the official reference, used to cross check the complete endpoint and path list including finance, webhook, address, question and common label services.