Shopee Open Platform API: A Developer's Integration Guide
Everything you need to know about integrating with Shopee's Open Platform API for product management, order processing, and inventory sync.
Getting Started with Shopee API
Shopee's Open Platform API enables sellers and developers to automate product management, order processing, and logistics. However, the documentation can be confusing and the authentication flow has several gotchas. This guide covers what you need to know.
Authentication: The Signature Flow
Shopee uses a HMAC-SHA256 signature for every API request. This is more complex than a simple Bearer token:
const crypto = require('crypto');
function generateSignature(path, timestamp, accessToken = '', shopId = '') {
const baseString = `${PARTNER_ID}${path}${timestamp}${accessToken}${shopId}`;
return crypto
.createHmac('sha256', PARTNER_KEY)
.update(baseString)
.digest('hex');
}
function buildShopeeUrl(path, params = {}) {
const timestamp = Math.floor(Date.now() / 1000);
const signature = generateSignature(
path, timestamp, ACCESS_TOKEN, SHOP_ID
);
const url = new URL(`https://partner.shopeemobile.com${path}`);
url.searchParams.set('partner_id', PARTNER_ID);
url.searchParams.set('timestamp', timestamp);
url.searchParams.set('sign', signature);
url.searchParams.set('access_token', ACCESS_TOKEN);
url.searchParams.set('shop_id', SHOP_ID);
Object.entries(params).forEach(([key, value]) => {
url.searchParams.set(key, value);
});
return url.toString();
}
OAuth Flow
To get an access token for a shop:
- Generate auth URL and redirect the seller
- Receive callback with a temporary code
- Exchange code for tokens (access + refresh)
- Refresh tokens before expiry (access tokens last 4 hours)
async function getAccessToken(code, shopId) {
const path = '/api/v2/auth/token/get';
const timestamp = Math.floor(Date.now() / 1000);
const signature = generateSignature(path, timestamp);
const response = await fetch(
`https://partner.shopeemobile.com${path}?partner_id=${PARTNER_ID}×tamp=${timestamp}&sign=${signature}`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
code,
shop_id: parseInt(shopId),
partner_id: parseInt(PARTNER_ID)
})
}
);
const data = await response.json();
return {
accessToken: data.access_token,
refreshToken: data.refresh_token,
expiresIn: data.expire_in
};
}
Product Management
Listing Products
async function getProductList(offset = 0, limit = 50) {
const url = buildShopeeUrl('/api/v2/product/get_item_list', {
offset,
page_size: limit,
item_status: 'NORMAL'
});
const response = await fetch(url);
const data = await response.json();
return data.response.item;
}
Creating a Product
Shopee product creation requires multiple fields, and the category system is strict:
async function createProduct(product) {
const url = buildShopeeUrl('/api/v2/product/add_item');
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
item_name: product.name,
description: product.description,
category_id: product.categoryId,
price: product.price,
stock: product.stock,
weight: product.weight,
image: {
image_id_list: product.imageIds
},
logistics_channel_id: product.logisticsChannels
})
});
return response.json();
}
Image Upload
Images must be uploaded to Shopee's CDN before creating products:
const FormData = require('form-data');
async function uploadImage(imageBuffer, filename) {
const form = new FormData();
form.append('image', imageBuffer, { filename });
const url = buildShopeeUrl('/api/v2/media_space/upload_image');
const response = await fetch(url, {
method: 'POST',
body: form,
headers: form.getHeaders()
});
const data = await response.json();
return data.response.image_info.image_id;
}
Order Processing
Fetching Orders
async function getOrders(status = 'READY_TO_SHIP') {
const url = buildShopeeUrl('/api/v2/order/get_order_list', {
time_range_field: 'create_time',
time_from: Math.floor(Date.now() / 1000) - 86400, // Last 24 hours
time_to: Math.floor(Date.now() / 1000),
page_size: 100,
order_status: status
});
const response = await fetch(url);
const data = await response.json();
return data.response.order_list;
}
Shipping an Order
async function shipOrder(orderSn, trackingNumber) {
const url = buildShopeeUrl('/api/v2/logistics/ship_order');
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
order_sn: orderSn,
package_number: '',
tracking_number: trackingNumber
})
});
return response.json();
}
Common Pitfalls
- Timestamp must be in seconds, not milliseconds. Use
Math.floor(Date.now() / 1000). - Access tokens expire in 4 hours. Implement automatic refresh.
- Rate limits are per-shop — 10 requests per second for most endpoints.
- Sandbox is limited — many features only work in production. Test carefully.
- Image URLs expire — don't store Shopee CDN URLs long-term; store image IDs.
- Category attributes are required — each category has mandatory attributes that must be filled.
Token Refresh Strategy
async function ensureFreshToken(shop) {
const now = Math.floor(Date.now() / 1000);
if (shop.tokenExpiresAt - now > 300) {
return shop.accessToken; // Still valid for 5+ minutes
}
const newTokens = await refreshToken(shop.refreshToken, shop.shopId);
await updateShopTokens(shop.id, newTokens);
return newTokens.accessToken;
}
Conclusion
Shopee's API is powerful but requires attention to authentication details and platform-specific conventions. Once you've handled the signature flow and token management, the rest follows standard REST patterns. The key is building a robust foundation for authentication and error handling before tackling business logic.
Related Project
Shopify-Shopee Multi-Channel SyncAutomated product, inventory, and order sync between Shopify and Shopee Singapore, enabling multi-channel selling with real-time stock updates.