Integrating Shopee and Tokopedia Seller APIs

Photo by Zuko.io Images via Wikimedia Commons (CC BY 2.0)
No longer. After the merger, Tokopedia seller integrations run through the TikTok Shop Open API and the Tokopedia Partner Center. You register one app there and use the same Open API to manage both the Tokopedia and Shop apps, rather than the retired Tokopedia-only endpoints.
Shopee Open Platform access tokens last about 4 hours and refresh tokens about 30 days. Refresh proactively on a schedule instead of waiting for a failed call, and always persist both the new access token and the returned refresh token since the refresh token can rotate.
Keep one inventory service as the single source of truth. Every order decrements the master stock first, then a queue fans the new quantity out to every channel selling that SKU. A periodic reconciliation pulls each channel's reported stock and corrects any drift against the master.
The most common cause is the wrong field order in the base string. A shop-level Shopee call signs the concatenation of partner_id, API path, timestamp, access_token, and shop_id with your partner_key using HMAC-SHA256. Wrap it in one tested helper and never build the signature inline.
Route every outbound call through a queue with a per-shop token-bucket limiter. Shopee limits calls per partner over time windows, while TikTok Shop allocates quota dynamically per shop and returns HTTP 429 when exceeded. Use exponential backoff on 429 and pull only changed records with cursor pagination.

Photo by Zuko.io Images via Wikimedia Commons (CC BY 2.0)
Key Takeaway
Selling on both Shopee and Tokopedia means integrating two seller APIs with different auth: Shopee Open Platform uses an HMAC-SHA256 signature with 4-hour access tokens, while Tokopedia now runs on the TikTok Shop Open API with 24-hour tokens. Build one middleware that owns inventory as the single source of truth and pushes stock to each channel.
Almost every Indonesian SME seller I work with lists the same products on both Shopee and Tokopedia, plus maybe their own web store. The moment two orders for the last unit land within seconds of each other on different marketplaces, you get an oversell, a cancellation, and a hit to your seller rating. The only real fix is a backend that treats your own database as the truth and continuously reconciles stock outward to each channel.
On a recent project I built exactly this kind of omnichannel middleware. Below is the practical shape of both APIs, the parts that trip people up, and how I structure the sync so stock never drifts.
The single most important thing to internalize before writing code: Shopee and Tokopedia authenticate completely differently, and Tokopedia is no longer its own API. After the merger, Tokopedia sellers are managed through the TikTok Shop Open API and the Tokopedia Partner Center. You register one app, but the two integrations barely share code beyond your business logic.
If you integrated Tokopedia before the TikTok Shop migration, the old Tokopedia-only endpoints are being retired. New shops and newly generated data are only reachable through the TikTok Shop Open API, so verify your enabler or your own code targets the current version before onboarding sellers.
Both platforms use the same broad OAuth shape: you redirect the seller to an authorization page, they approve access to their shop, and the marketplace redirects back with a short-lived code. Your server exchanges that code for an access token plus a refresh token. The details differ in how requests are signed.
For Shopee, the signature for a shop-level call is an HMAC-SHA256 of the concatenated partner_id, API path, timestamp, access_token, and shop_id, keyed with your partner_key. Get the field order wrong and every call returns a signature error, so I wrap it in a single helper and never build it inline.
// Shopee Open Platform v2 — shop-level request signature
import { createHmac } from "crypto";
function shopeeSign(params: {
partnerId: number;
partnerKey: string;
apiPath: string; // e.g. "/api/v2/product/update_stock"
accessToken: string;
shopId: number;
}): { sign: string; timestamp: number } {
const timestamp = Math.floor(Date.now() / 1000);
const base =
`${params.partnerId}${params.apiPath}${timestamp}` +
`${params.accessToken}${params.shopId}`;
const sign = createHmac("sha256", params.partnerKey)
.update(base)
.digest("hex");
return { sign, timestamp };
}
// Token refresh runs on a schedule, NOT lazily on 401.
// Shopee access_token ~4h, refresh_token ~30d — refresh early,
// persist BOTH returned values, they can rotate.Refresh tokens on a timer, not on the first failed request. With a 4-hour Shopee token, a cron that refreshes every ~3 hours keeps you far from the edge. Always persist both the new access token and the returned refresh token, because the refresh token can rotate on each call.
There are two directions to sync. Inbound: new orders flow from each marketplace into your system, ideally via push notifications or webhooks so you react in seconds, with a scheduled poll as a safety net for missed events. Outbound: whenever stock changes for any reason, you push the new quantity to every channel that lists that SKU.
The mapping table between your internal SKU and each marketplace's item and model identifiers is the heart of the system. Shopee separates an item_id from a model_id (its variation), and TikTok Shop separates a product_id from a sku_id. If that mapping is wrong, you update the wrong variant and quietly oversell the popular size while the unpopular one shows phantom stock.
Product creation is the most schema-heavy part of either API. Each marketplace enforces its own category tree, required attributes per category, image rules, and logistics options. I do not try to build a lowest-common-denominator product model. Instead I keep a rich internal product and write a per-channel adapter that maps it onto each marketplace's exact category and attribute requirements.
Rate limits are where naive integrations fall over. Shopee limits calls per partner over time windows; TikTok Shop allocates quota dynamically per shop per app and returns HTTP 429 when you exceed it. Both are far more forgiving if you batch, back off, and paginate correctly rather than hammering. Route every outbound call through a queue with a token-bucket limiter per shop, and honor 429 with exponential backoff.
When listing large catalogs, prefer cursor-based pagination and pull only what changed since your last sync using an update-time filter. Full catalog re-pulls are the fastest way to burn your rate budget and the slowest to finish.
The architecture that has never let me down is boring on purpose: one inventory service owns the master quantity, and marketplaces are downstream consumers, never authors, of that number. Every change to stock, whether from a sale, a return, a manual edit, or a warehouse count, updates the master first and then fans out to channels through a queue.
| Concern | Shopee Open Platform | Tokopedia (TikTok Shop API) |
|---|---|---|
| Credentials | partner_id + partner_key | app_key + app_secret |
| Request signing | HMAC-SHA256 sign + timestamp | sign parameter per docs |
| Access token life | About 4 hours | About 24 hours |
| Refresh token life | About 30 days | Up to 365 days |
| Rate limiting | Per-partner time windows | Dynamic QPS per shop, 429 on excess |
| Variant identity | item_id + model_id | product_id + sku_id |
With that in place, adding a third channel later — Lazada, TikTok Shop standalone, your own store — is just another adapter and another row in the SKU mapping table. The core inventory logic never changes, which is exactly what you want when the marketplaces themselves keep changing under you.