Shopee Open Platform API Integration: Auth, Signing & Tokens
How Shopee Open Platform API v2 integration works: app categories, OAuth flow, HMAC-SHA256 signing, token refresh, sandbox, IP whitelist and key rotation.
Long Nguyen
Fullstack Developer · AI Engineer · Researcher
How the Shopee Open Platform API works (v2)
A Shopee Open Platform API integration is a signed REST integration. Your server registers an app, each seller authorizes that app through an OAuth-style redirect, and every call afterwards carries a partner ID, a timestamp and an HMAC-SHA256 signature, plus a short-lived access token for shop-level calls. Listing sync, order import and inventory updates all sit on those pieces.
Shopee scheduled the older v1 API for full deprecation after , so anything new targets v2. The vocabulary is where first-time integrators lose time, so here is the map:
| Term | What it is | Lifetime or rule |
|---|---|---|
partner_id |
Identifies your app | Fixed per app and per environment |
partner_key |
Secret used to sign requests (never sent over the wire) | Valid 180 days, then must be reset |
shop_id |
The seller shop you act for | Returned in the redirect after authorization |
merchant_id |
Used in place of shop_id on merchant-level APIs |
Tokens are stored per merchant, separately from shops |
code |
One-time authorization code | 10 minutes, single use |
access_token |
Credential for shop-level calls | 4 hours |
refresh_token |
Exchanged for a new access token | 30 days |
sign |
HMAC-SHA256 of the request, computed per call | Tied to a timestamp accepted for about 5 minutes |
Registering an app: the category decides everything
Shopee admits two kinds of developer: Third-Party Partners (companies building software for many sellers) and Registered Business Sellers (a seller integrating its own shop). Once the profile is approved you create an app in the console, and this is the step that costs teams weeks: the category chosen at creation fixes every API and permission the app can ever use.
Shopee's own developer FAQ announcement is blunt about it. The Open Platform team does not switch on individual APIs, permissions cannot be added to an existing app, and needing different capabilities can mean creating a new app under another category.
- Choose the category from your endpoint list, not from the label. Write down the product, order, logistics and payment endpoints you need and confirm they all belong to one category before you create the app.
- Do not promise buyer messaging by default. Applications for Customer Service (Chat API) apps from individual third parties and Third-party Partner platforms have been closed since . Confirm access before an inbox or chatbot feature goes into a client scope.
- Permission denied usually means wrong category, a retired endpoint, or a documented change, not a bug in your signature.
How to sign a Shopee API request (HMAC-SHA256)
The signature is the hex digest of an HMAC-SHA256 over a base string, keyed with your partner key. What goes into the base string depends on the API type:
| API type | Base string, concatenated in this order |
|---|---|
| Public (auth link, token exchange, refresh) | partner_id + api_path + timestamp |
| Shop | partner_id + api_path + timestamp + access_token + shop_id |
| Merchant | partner_id + api_path + timestamp + access_token + merchant_id |
The result is sent as the sign query parameter next to partner_id and timestamp; shop calls also carry access_token and shop_id. api_path is only the path, for example /api/v2/shop/get_shop_info, with no host and no query string. This Python module covers the whole first-connection flow:
import hashlib
import hmac
import time
from urllib.parse import urlencode
import requests
HOST = 'https://partner.test-stable.shopeemobile.com' # sandbox; production is https://partner.shopeemobile.com
PARTNER_ID = 1000000 # from the console, must match the environment
PARTNER_KEY = 'your-partner-key'
def sign(path, ts, access_token='', entity_id=''):
# public API: partner_id + path + timestamp
# shop API: ... + access_token + shop_id (merchant API: merchant_id)
base = f'{PARTNER_ID}{path}{ts}{access_token}{entity_id}'
return hmac.new(PARTNER_KEY.encode(), base.encode(), hashlib.sha256).hexdigest()
def auth_url(redirect):
path = '/api/v2/shop/auth_partner'
ts = int(time.time())
query = urlencode({'partner_id': PARTNER_ID, 'timestamp': ts,
'sign': sign(path, ts), 'redirect': redirect})
return f'{HOST}{path}?{query}'
def public_post(path, body):
ts = int(time.time())
params = {'partner_id': PARTNER_ID, 'timestamp': ts, 'sign': sign(path, ts)}
body = dict(body, partner_id=PARTNER_ID)
return requests.post(HOST + path, params=params, json=body, timeout=15).json()
def get_token(code, shop_id):
return public_post('/api/v2/auth/token/get', {'code': code, 'shop_id': shop_id})
def refresh_token(refresh_tok, shop_id):
return public_post('/api/v2/auth/access_token/get',
{'refresh_token': refresh_tok, 'shop_id': shop_id})
def shop_get(path, access_token, shop_id, **extra):
ts = int(time.time())
params = {'partner_id': PARTNER_ID, 'timestamp': ts, 'access_token': access_token,
'shop_id': shop_id, 'sign': sign(path, ts, access_token, shop_id)}
params.update(extra)
return requests.get(HOST + path, params=params, timeout=15).json()
# info = shop_get('/api/v2/shop/get_shop_info', access_token, shop_id)
Run it against the sandbox host first and swap in the production host, partner ID and key only after that works. Three causes account for most signature failures:
- Environment mismatch. Key,
partner_idand host must all belong to the same environment (sandbox or live). A production key against the sandbox host fails every time. - Different timestamps. The timestamp inside the base string must be the exact value sent in the URL. Compute it once per call.
- A drifting server clock. Shopee only honours a signed timestamp for a few minutes, so a server whose clock has slipped looks like an intermittent signature problem. Keep NTP running.
Storing and refreshing tokens without breaking sync
Store one token record per shop_id and per merchant_id, never per app. A record needs the access token, the refresh token, both expiry times, and the seller's authorization expiry. Refresh proactively, well before the four-hour mark, rather than waiting for a call to fail mid-sync.
The subtle failure is concurrency. If two workers refresh the same shop at once, the loser can overwrite a good token pair with a stale one. Treat the refresh token as rotating: take a per-shop lock, call refresh, and write back both tokens returned in the same transaction, overwriting what was there. Every other worker reads from the database and never refreshes on its own.
This token layer is the unglamorous part that every marketplace connector needs, and it multiplies with each channel you add. Teams syncing Shopee alongside Amazon, eBay or TikTok Shop often decide it is cheaper to have marketplace integration built and maintained for them than to own one auth model per platform.
Sandbox vs production: what test-stable will and will not tell you
The sandbox host is partner.test-stable.shopeemobile.com; production is partner.shopeemobile.com. Shopee describes the sandbox as functional but not a full replica of production, so divergent behavior, limits and instability can be expected there.
- Use it to validate signing, the authorization flow, token refresh and payload shapes.
- Do not use it to judge throughput, edge-case order states or logistics behavior.
- Creating test orders can fail with errors such as
error_sandbox_order_checkout_cart_item,invalid productorFailed to get info. Shopee's advice is to create new products, try another category and repeat the flow before assuming your code is wrong. - If you open a support ticket, run isolation tests first and attach a full screen recording, a HAR file and the complete request and response.
Why buyer data comes back masked (IP whitelist)
If order responses arrive with masked buyer details, the fix is in your app settings, not in a support request. Access to sensitive data is tied to the app's security configuration, including the IP whitelist, and Shopee states there is no manual release of masked data. The whitelist is configured by you in the console; Shopee staff cannot do it for you.
Check two things in order. First, that the whitelist is set up and enabled for the exact egress IPs your servers use, which matters if you deploy behind a NAT gateway or on autoscaling infrastructure with changing addresses. Second, the order's status: some order states do not expose buyer data through the API at all, so masked fields on a fresh unpaid order are not necessarily a whitelist problem.
Partner Key expiry and safe rotation
The Partner Key is valid for 180 days from generation, and it does not renew itself. Once it expires the platform marks it invalid and blocks API calls that use it, which for a live integration means every seller goes dark at once. Set a calendar reminder well before day 180 and treat the rotation as a planned release.
- A reset takes effect immediately for the new key.
- You can keep the old key alive for up to 72 hours after the reset, which gives you a window to roll the new secret across servers.
- Resetting does not invalidate seller authorizations, so no seller has to reconnect.
- The key is managed inside the app, under App List in the console. Shopee cannot generate a replacement for you.
Read the key from a secret store at runtime rather than baking it into images, so rotation is a config change and not a redeploy.
Push Mechanism vs polling for orders and inventory
Shopee documents a Push Mechanism, a webhook that notifies a callback URL registered for your app when events such as order status changes occur. It is the right tool for low latency, and it should not be your only tool.
Design it as push plus reconciliation. Verify the push signature as the Push Mechanism documentation describes, acknowledge fast, and put the event on a queue rather than doing the work inline. Then run a scheduled job that queries orders by update-time window and compares them with your database. Pushes can arrive late, twice or not at all, and a reconciliation pass is what turns a best-effort feed into a system you can trust. Make every handler idempotent on the order number so a repeated push is harmless.
Common Shopee API errors and what to check first
| Symptom | Check first |
|---|---|
| Invalid sign | Environment match (key, partner ID, host), the base string order, the same timestamp in sign and URL, a path with no host or query string, and the right token for shop calls |
| Invalid timestamp or a dead auth link | The signed timestamp lives about 5 minutes. Generate a new link and check the server clock |
| Code rejected | Codes are single use and expire after 10 minutes. Restart authorization |
| Access token expired | Tokens last 4 hours. Refresh with the stored refresh token |
| Permission denied or endpoint missing | Is the API inside your app category, does the endpoint still exist in the API reference, and did an announcement change it |
| Masked buyer data | IP whitelist configured and enabled, then the order status |
| Sandbox order creation fails | Create new test products, try another category, repeat the flow |
| All calls fail at once, all shops | Partner Key expired or was reset without the 72-hour overlap |
Build it yourself or hand it over
The Shopee side is manageable for one shop and one developer: sign correctly, store tokens per shop, refresh under a lock, reconcile pushes, and rotate the key on schedule. The cost shows up in maintenance, when a category limitation blocks a feature, a key expires over a weekend, or you add a second marketplace with its own rules.
If you would rather scope that work than carry it, request a free quote with the marketplaces and features you need. Work is priced to scope, and you get an answer without committing to anything.
FAQ
Frequently asked questions
How do I get access to the Shopee Open Platform API?
Register on Shopee Open Platform as either a Third-Party Partner or a Registered Business Seller. After your profile is approved, create an app in the console. The category you choose at creation determines which APIs and permissions the app has, and Shopee does not enable individual APIs on request.
How long do Shopee API access tokens last?
An access_token is valid for 4 hours and a refresh_token for 30 days. Call the refresh endpoint to get a new access token; the seller does not need to authorize again. The seller's own authorization is a separate clock, chosen by the seller at up to 365 days.
How is the Shopee API v2 signature generated?
Concatenate partner_id, the API path and the timestamp, then append access_token and shop_id for shop-level calls (merchant_id for merchant-level calls). Compute an HMAC-SHA256 of that string using your partner key and send the hex digest as the sign parameter.
Can I add permissions to an existing Shopee app?
No. Permissions and available APIs are set by the app category chosen at creation, and the Open Platform team does not enable individual APIs. If you need different capabilities you may have to create a new app under another category.
Is there a Shopee API sandbox?
Yes. The sandbox host is partner.test-stable.shopeemobile.com. It is meant for testing signing, authorization and payloads, but Shopee notes it does not fully replicate production, so some errors and inconsistencies are expected there.
Does the Shopee Partner Key expire?
Yes. A Partner Key is valid for 180 days and is not renewed automatically. After expiry it is marked invalid and calls using it are blocked. You reset it yourself in the console, the new key works immediately, and the old key can stay valid for up to 72 hours. Seller authorizations are not affected.
Why is buyer data masked in Shopee order responses?
Access to sensitive data depends on your app's security setup, including the IP whitelist configured in the console. There is no manual release of masked data, and some order statuses do not expose buyer data at all.