Automate Amazon order tracking using the event-driven architecture: subscribe to ORDER_CHANGE notifications via SQS instead of polling getOrders (which is severely throttled at 0.0167 req/sec = 1/min). When an ORDER_CHANGE event arrives, call getOrder + getOrderItems for details. Accessing buyer PII (name, address) requires a separate Restricted Data Token. FBA orders are fulfilled by Amazon — monitor FBA_OUTBOUND_SHIPMENT_STATUS notifications for shipment updates.
API Quick Reference
LWA OAuth 2.0 (Bearer token)
getOrders: 1 req/min; getOrder: 0.5 req/sec
JSON
Available
Understanding the Amazon SP-API
Amazon SP-API replaced MWS on March 31, 2024. The Orders API provides order management for both FBA (Fulfilled by Amazon) and FBM (Fulfilled by Merchant) sellers. The critical architectural decision for order tracking is event-driven vs polling: getOrders has an extremely low rate limit of 0.0167 req/sec (1 request per minute sustained with burst of 20), making polling architectures impractical for anything beyond a few orders per hour.
The correct architecture uses ORDER_CHANGE notifications delivered to an AWS SQS queue. Amazon publishes a message whenever an order status changes — you process those messages and call getOrder or getOrderItems only when you have an event to handle. This inverts the flow from polling to reactive and eliminates rate limit constraints for the monitoring loop.
Two important access restrictions: (1) Buyer PII (name, email, shipping address) requires a Restricted Data Token (RDT) — calling getOrderBuyerInfo or getOrderAddress without an RDT returns a 403. (2) FBA orders are fulfilled by Amazon — you can only monitor their status, not submit tracking. FBM orders require you to submit tracking via the Listings API or Feeds API. Official documentation: https://developer-docs.amazon.com/sp-api/docs/orders-api-v0-reference
https://sellingpartnerapi-na.amazon.comSetting Up Amazon SP-API Authentication
SP-API uses Login with Amazon (LWA) OAuth 2.0. Exchange your refresh token for a 1-hour access token before each session. For order operations accessing buyer PII, you need an additional Restricted Data Token obtained per operation. AWS IAM signing is completely deprecated — SP-API has ignored SigV4 signatures since October 2023.
- 1Register as a developer in Seller Central > Developer Console
- 2Submit Developer Profile with Inventory & Order Tracking role and explain your use case
- 3If you need buyer PII (name/address), also select 'Buyer Communication' restricted role
- 4Wait for Amazon approval and obtain your LWA credentials (client ID, client secret)
- 5Authorize your app through Seller Central to get the refresh token
- 6Set environment variables: AMAZON_LWA_CLIENT_ID, AMAZON_LWA_CLIENT_SECRET, AMAZON_LWA_REFRESH_TOKEN, AMAZON_SELLER_ID, AMAZON_MARKETPLACE_ID
- 7For notifications: set up an SQS queue in AWS and note its ARN (arn:aws:sqs:region:account:queue-name)
- 8Rotate LWA credentials every 180 days
1import os2import time3import requests45class SPAPIAuth:6 def __init__(self):7 self._token = None8 self._expires_at = 0910 def get_token(self):11 if time.time() >= self._expires_at - 300:12 resp = requests.post(13 'https://api.amazon.com/auth/o2/token',14 data={15 'grant_type': 'refresh_token',16 'refresh_token': os.environ['AMAZON_LWA_REFRESH_TOKEN'],17 'client_id': os.environ['AMAZON_LWA_CLIENT_ID'],18 'client_secret': os.environ['AMAZON_LWA_CLIENT_SECRET']19 }20 )21 resp.raise_for_status()22 data = resp.json()23 self._token = data['access_token']24 self._expires_at = time.time() + data['expires_in']25 return self._token2627 def headers(self):28 return {29 'x-amz-access-token': self.get_token(),30 'Content-Type': 'application/json'31 }3233auth = SPAPIAuth()34print(f'SP-API auth ready')Security notes
- •Store LWA client ID, secret, and refresh token in environment variables — never hardcode
- •LWA access tokens expire in 1 hour — cache them and refresh proactively
- •Rotate LWA credentials every 180 days as required by Amazon
- •Restricted Data Tokens (RDTs) contain buyer PII — follow Amazon's Data Protection Policy for storage and handling
- •Use the correct regional SP-API endpoint for each seller's marketplace region
- •SQS queue policy must be restricted to your AWS account — do not make it publicly accessible
Key endpoints
/orders/v0/orders/{orderId}Retrieves a single order by order ID. Use this in response to ORDER_CHANGE notification events — much more efficient than polling getOrders. Rate limit: 0.5 req/sec with burst 30.
| Parameter | Type | Required | Description |
|---|---|---|---|
orderId | string | required | Amazon order ID in format 111-1234567-1234567 |
Response
1{"payload": {"AmazonOrderId": "111-1234567-1234567", "OrderStatus": "Shipped", "FulfillmentChannel": "MFN", "PurchaseDate": "2024-01-15T10:30:00Z", "LastUpdateDate": "2024-01-15T14:22:00Z", "OrderTotal": {"Amount": "45.99", "CurrencyCode": "USD"}, "NumberOfItemsShipped": 2, "ShipmentServiceLevelCategory": "Standard"}}/orders/v0/orders/{orderId}/orderItemsReturns line items for an order — ASIN, SKU, title, quantity, and price. Use after getOrder to get the full order details. Rate limit: 0.5 req/sec with burst 30.
| Parameter | Type | Required | Description |
|---|---|---|---|
orderId | string | required | Amazon order ID |
Response
1{"payload": {"AmazonOrderId": "111-1234567-1234567", "OrderItems": [{"ASIN": "B01EXAMPLE", "OrderItemId": "123456789", "SellerSKU": "MY-SKU-001", "Title": "Example Product Name", "QuantityOrdered": 2, "QuantityShipped": 0, "ItemPrice": {"Amount": "22.99", "CurrencyCode": "USD"}}]}}/orders/v0/ordersLists orders filtered by last updated time. Rate limit is extremely low at 0.0167 req/sec (1/min). Only use for backfill or initial sync — use ORDER_CHANGE notifications for real-time tracking.
| Parameter | Type | Required | Description |
|---|---|---|---|
MarketplaceIds | array | required | Array of marketplace IDs, e.g. ['ATVPDKIKX0DER'] for US |
LastUpdatedAfter | string | optional | ISO 8601 timestamp — return orders updated after this time. Use for backfill. |
OrderStatuses | array | optional | Filter by status: Pending, Unshipped, PartiallyShipped, Shipped, Canceled, Unfulfillable |
Response
1{"payload": {"Orders": [{"AmazonOrderId": "111-1234567-1234567", "OrderStatus": "Unshipped", "PurchaseDate": "2024-01-15T10:30:00Z"}], "NextToken": "next-page-token"}}/tokens/2021-03-01/restrictedDataTokenCreates a Restricted Data Token (RDT) required for accessing buyer PII in getOrderBuyerInfo and getOrderAddress. The RDT replaces the regular access token for restricted calls.
| Parameter | Type | Required | Description |
|---|---|---|---|
restrictedResources | array | required | Array of restricted operations you need access to. Each has method, path, and dataElements. |
Request
1{"restrictedResources": [{"method": "GET", "path": "/orders/v0/orders/{orderId}/buyerInfo", "dataElements": ["buyerInfo"]}, {"method": "GET", "path": "/orders/v0/orders/{orderId}/address", "dataElements": ["shippingAddress"]}]}Response
1{"restrictedDataToken": "Atz.sprdt|IwEB...", "expiresIn": 3600}Step-by-step automation
Set Up ORDER_CHANGE Notification Subscription
Why: ORDER_CHANGE notifications replace polling getOrders — instead of hitting the 1-req/min limit, Amazon proactively sends you a message when any order changes.
Create an SQS destination using a grantless token, then subscribe to ORDER_CHANGE notifications with your seller token. The notification fires when orders are placed, confirmed, shipped, delivered, or canceled. You can filter by orderChangeTypes to receive only the changes you care about.
1# 1. Get grantless token2curl -X POST https://api.amazon.com/auth/o2/token \3 -d 'grant_type=client_credentials' \4 -d "client_id=$AMAZON_LWA_CLIENT_ID" \5 -d "client_secret=$AMAZON_LWA_CLIENT_SECRET" \6 -d 'scope=sellingpartnerapi::notifications'78# 2. Create SQS destination (with grantless token)9curl -X POST https://sellingpartnerapi-na.amazon.com/notifications/v1/destinations \10 -H "x-amz-access-token: $GRANTLESS_TOKEN" \11 -H 'Content-Type: application/json' \12 -d '{"resourceSpecification": {"sqs": {"arn": "arn:aws:sqs:us-east-1:123456789:order-tracking"}}, "name": "order-tracking-queue"}'1314# 3. Subscribe to ORDER_CHANGE (with seller token)15curl -X POST https://sellingpartnerapi-na.amazon.com/notifications/v1/subscriptions/ORDER_CHANGE \16 -H "x-amz-access-token: $SELLER_TOKEN" \17 -H 'Content-Type: application/json' \18 -d '{"payloadVersion": "1.0", "destinationId": "YOUR_DEST_ID"}'Pro tip: Use the eventFilter.orderChangeTypes to subscribe only to OrderStatusChange and BuyerRequestedChange — this reduces noise from events you don't need to act on.
Expected result: An active ORDER_CHANGE subscription. Amazon will now push order status change messages to your SQS queue instead of requiring you to poll.
Process ORDER_CHANGE Messages from SQS
Why: SQS messages contain the order ID and change type — from there you call getOrder for full order details with a much better rate limit (0.5 req/sec vs 0.0167 for getOrders).
Poll your SQS queue for messages. Each message contains a NotificationMetadata and a Payload with the order ID and change type. Extract the Amazon order ID, call getOrder and getOrderItems, then update your order management system. Delete the SQS message after successful processing to prevent reprocessing.
1# After receiving the ORDER_CHANGE notification with order ID 111-1234567-1234567:2# Fetch full order details3curl "https://sellingpartnerapi-na.amazon.com/orders/v0/orders/111-1234567-1234567" \4 -H "x-amz-access-token: $TOKEN"56# Fetch order items7curl "https://sellingpartnerapi-na.amazon.com/orders/v0/orders/111-1234567-1234567/orderItems" \8 -H "x-amz-access-token: $TOKEN"Pro tip: Use SQS long polling (WaitTimeSeconds=20) to reduce empty receive calls and minimize SQS costs. Only delete messages after successful processing — undeleted messages return to the queue after the visibility timeout for automatic retry.
Expected result: Order details retrieved and processed for each ORDER_CHANGE notification, with processed messages deleted from the SQS queue.
Get Buyer PII with a Restricted Data Token
Why: getOrderBuyerInfo and getOrderAddress return 403 with a regular access token — buyer name, email, and shipping address require a Restricted Data Token (RDT) for privacy protection.
When you need buyer contact information (for FBM shipping labels, customer communication, or fulfillment), create an RDT that grants temporary access to specific restricted operations. The RDT replaces the regular access token in the x-amz-access-token header for those specific calls. RDTs are short-lived and operation-specific.
1# Create a Restricted Data Token for buyer info and address2curl -X POST https://sellingpartnerapi-na.amazon.com/tokens/2021-03-01/restrictedDataToken \3 -H "x-amz-access-token: $REGULAR_TOKEN" \4 -H 'Content-Type: application/json' \5 -d '{6 "restrictedResources": [7 {"method": "GET", "path": "/orders/v0/orders/{orderId}/buyerInfo", "dataElements": ["buyerInfo"]},8 {"method": "GET", "path": "/orders/v0/orders/{orderId}/address", "dataElements": ["shippingAddress"]}9 ]10 }'1112# Use the RDT (not the regular token) for PII calls13curl https://sellingpartnerapi-na.amazon.com/orders/v0/orders/111-1234567-1234567/buyerInfo \14 -H "x-amz-access-token: $RESTRICTED_DATA_TOKEN"Pro tip: RDTs are operation-specific path templates — you don't specify the actual order ID in the token request, just the path pattern /orders/v0/orders/{orderId}/buyerInfo. The same RDT works for any order ID with that operation.
Expected result: Buyer name, email, and shipping address accessible for FBM fulfillment workflows. The RDT is valid for the operations specified during creation.
Backfill Orders with getOrders (Use Sparingly)
Why: For initial sync or recovering missed events, getOrders is the only way to pull historical order data — but its 1/min rate limit means large backlogs take hours.
For initial system setup or after a notification outage, use GET /orders/v0/orders with LastUpdatedAfter to pull recent orders. The burst of 20 lets you fetch 20 pages quickly, but the sustained 0.0167 req/sec means you can only sustain 1 request per minute after the burst is consumed. Paginate via NextToken. For months of history, this can take many hours.
1# Fetch orders updated in the last 24 hours (use sparingly!)2curl "https://sellingpartnerapi-na.amazon.com/orders/v0/orders?MarketplaceIds=ATVPDKIKX0DER&LastUpdatedAfter=2024-01-14T00:00:00Z&OrderStatuses=Unshipped,PartiallyShipped" \3 -H "x-amz-access-token: $TOKEN"Pro tip: Your actual getOrders rate limit may be higher than 0.0167 req/sec if Amazon's dynamic usage plan has raised it based on your order volume. Always check the x-amzn-RateLimit-Limit response header — it shows your actual applied rate.
Expected result: All orders updated in the specified time window retrieved, with appropriate rate limit pacing. For ongoing tracking, switch to ORDER_CHANGE notifications.
Complete working code
A complete order tracking system: SQS notification processor that retrieves order details when ORDER_CHANGE events arrive, handles FBA vs FBM fulfillment differently, and sends status updates to Slack. Run as a long-running process or Lambda function.
1import boto32import json3import requests4import os5import time6import logging78logging.basicConfig(level=logging.INFO)9log = logging.getLogger(__name__)1011BASE_URL = 'https://sellingpartnerapi-na.amazon.com'12SLACK_WEBHOOK = os.environ.get('SLACK_WEBHOOK_URL')1314class SPAPIAuth:15 def __init__(self):16 self._token = None17 self._expires_at = 01819 def get(self):20 if time.time() >= self._expires_at - 300:21 resp = requests.post('https://api.amazon.com/auth/o2/token', data={22 'grant_type': 'refresh_token',23 'refresh_token': os.environ['AMAZON_LWA_REFRESH_TOKEN'],24 'client_id': os.environ['AMAZON_LWA_CLIENT_ID'],25 'client_secret': os.environ['AMAZON_LWA_CLIENT_SECRET']26 })27 resp.raise_for_status()28 d = resp.json()29 self._token = d['access_token']30 self._expires_at = time.time() + d['expires_in']31 return self._token3233auth = SPAPIAuth()3435def get(path, use_token=None):36 token = use_token or auth.get()37 resp = requests.get(38 f'{BASE_URL}{path}',39 headers={'x-amz-access-token': token}40 )41 resp.raise_for_status()42 return resp.json().get('payload', resp.json())4344def send_slack(msg):45 if SLACK_WEBHOOK:46 requests.post(SLACK_WEBHOOK, json={'text': msg}, timeout=5)4748def process_message(body):49 try:50 notification = json.loads(body)51 payload = json.loads(notification.get('Message', '{}'))52 data = payload.get('payload', payload)53 order_id = data.get('OrderId') or data.get('AmazonOrderId')54 change_type = data.get('OrderChangeType', 'unknown')55 except Exception as e:56 log.error(f'Failed to parse notification: {e}')57 return False5859 if not order_id:60 log.warning('No order ID in notification')61 return True # Acknowledge and discard6263 log.info(f'Processing ORDER_CHANGE: {order_id} ({change_type})')6465 order = get(f'/orders/v0/orders/{order_id}')66 items = get(f'/orders/v0/orders/{order_id}/orderItems')['OrderItems']67 status = order['OrderStatus']68 channel = order.get('FulfillmentChannel', 'MFN')69 total = order.get('OrderTotal', {})7071 msg = f'Order {order_id}: {status} ({channel})\n'72 msg += f'Total: {total.get("CurrencyCode","USD")} {total.get("Amount","N/A")}\n'73 msg += f'Items: {len(items)} SKUs'7475 if channel == 'AFN': # FBA76 msg += '\nFulfilled by Amazon — monitoring shipment status via FBA_OUTBOUND_SHIPMENT_STATUS'77 else: # FBM78 if status == 'Unshipped':79 msg += '\nAction required: submit tracking number via Listings API'8081 log.info(msg)82 send_slack(msg)83 return True8485def main():86 sqs = boto3.client('sqs', region_name='us-east-1')87 queue_url = os.environ['AMAZON_SQS_QUEUE_URL']8889 log.info('Starting ORDER_CHANGE notification processor')90 while True:91 result = sqs.receive_message(92 QueueUrl=queue_url,93 MaxNumberOfMessages=10,94 WaitTimeSeconds=2095 )96 for msg in result.get('Messages', []):97 try:98 success = process_message(msg['Body'])99 if success:100 sqs.delete_message(101 QueueUrl=queue_url,102 ReceiptHandle=msg['ReceiptHandle']103 )104 log.info(f'Message processed and deleted')105 except Exception as e:106 log.error(f'Error: {e} — message will retry')107108if __name__ == '__main__':109 main()Error handling
QuotaExceeded — getOrders rate limit exceededThe getOrders rate limit is 0.0167 req/sec (1/min sustained, 20 burst). This is the most common SP-API error for order management code — caused by polling in a loop.
Switch from polling getOrders to ORDER_CHANGE notifications via SQS. If you must use getOrders for backfill, wait 60 seconds between requests. Check x-amzn-RateLimit-Limit header for your actual applied rate.
Wait at least 60 seconds after a 429 on getOrders. For getOrder (0.5 req/sec), wait 2 seconds.
Access denied — restricted data token requiredCalling getOrderBuyerInfo or getOrderAddress with a regular access token. These operations require a Restricted Data Token (RDT) because they return PII.
Request an RDT via POST /tokens/2021-03-01/restrictedDataToken with the specific operations you need. Use the RDT in place of the regular access token for those calls only.
No retry — get an RDT first
Access denied — missing required roleYour SP-API app doesn't have the Inventory & Order Tracking role, or for buyer PII, the Buyer Communication restricted role.
Update your Developer Profile in SP-API Developer Console to add the required roles. Submit for review and wait for Amazon's approval.
No retry — complete role approval
Order 111-1234567-1234567 not foundThe order ID doesn't exist for this seller account or marketplace, or it's from a different marketplace region.
Verify the order ID is correct and from the correct marketplace. Ensure your seller token is for the right region (NA, EU, FE). Cross-region orders are not visible from the wrong regional endpoint.
No retry — check order ID and region
Unauthorized — access token expiredLWA access token has expired (1-hour lifetime).
Implement token caching with refresh when within 5 minutes of expiry. The SPAPIAuth class in the complete automation example handles this automatically.
Refresh token and retry immediately
Rate Limits for Amazon SP-API Orders
| Scope | Limit | Window |
|---|---|---|
| getOrders | 0.0167 requests (1/min) sustained | burst 20 |
| getOrder | 0.5 requests | per second, burst 30 |
| getOrderItems | 0.5 requests | per second, burst 30 |
| createRestrictedDataToken | 1 request | per second, burst 10 |
1import time2import requests34def sp_get_with_retry(url, headers, max_retries=5):5 for attempt in range(max_retries):6 resp = requests.get(url, headers=headers)7 if resp.status_code == 429:8 # Trust x-amzn-RateLimit-Limit header if present9 limit_header = resp.headers.get('x-amzn-RateLimit-Limit')10 wait = max(60, 1/float(limit_header)) if limit_header else 6011 print(f'429 — waiting {wait:.0f}s')12 time.sleep(wait)13 elif resp.status_code in (500, 503):14 time.sleep(2 ** attempt)15 else:16 resp.raise_for_status()17 return resp.json()18 raise Exception('Max retries exceeded')- Use ORDER_CHANGE notifications (SQS) for real-time order tracking — never poll getOrders in a loop
- Use getOrder (0.5 req/sec) not getOrders (0.0167 req/sec) when you have a specific order ID from a notification
- Check the x-amzn-RateLimit-Limit response header on every call — your dynamic usage plan rate may be higher than documented
- For initial backfill, spread getOrders requests over hours — at 1/min sustained, 100 pages takes 100 minutes minimum
- Cache RDTs for their lifetime (1 hour) rather than requesting a new one for each PII operation
Security checklist
- Store LWA credentials and SQS queue URL in environment variables — never hardcode
- Rotate LWA credentials every 180 days as required by Amazon
- Handle buyer PII (name, email, address) from RDT operations per Amazon's Data Protection Policy — encrypt at rest
- Use SQS message deduplication to safely handle duplicate notification deliveries
- Restrict SQS queue access to your application's IAM role — do not use broad IAM permissions
- Log order IDs for audit trails but never log buyer PII (email, address) to application logs
- Validate that order IDs in API requests match the seller account's marketplace before processing
- Use the correct regional SP-API endpoint (NA, EU, FE) matching the seller's marketplace
Automation use cases
FBM Shipment Workflow
advancedDetect new Unshipped FBM orders via ORDER_CHANGE notifications, retrieve buyer address using RDT, generate shipping labels via your carrier API, and submit tracking back to Amazon.
Order Status Dashboard
intermediateProcess ORDER_CHANGE events in real-time to maintain a live dashboard showing order counts by status (Pending, Unshipped, Shipped, Delivered, Canceled) updated as Amazon fires notifications.
FBA Shipment Monitoring
intermediateSubscribe to FBA_OUTBOUND_SHIPMENT_STATUS notifications to track Amazon-fulfilled orders through their delivery lifecycle and proactively alert customers of delays.
Multi-Channel Order Sync
advancedSync Amazon orders into your central order management system (Shopify, WooCommerce, or custom ERP) by processing ORDER_CHANGE notifications and calling getOrder + getOrderItems for each event.
No-code alternatives
Don't want to write code? These platforms can automate the same workflows visually.
Zapier
Free tier (100 tasks/month), paid from $19.99/monthZapier has no native Amazon SP-API integration. Amazon order tracking requires SP-API which uses LWA OAuth and SQS — too complex for Zapier's built-in automation without custom code.
- + Can receive webhook notifications if you set up a relay endpoint
- + Connects to Slack, Sheets, CRM easily
- - No native SP-API module
- - SQS notification processing not supported natively
- - LWA token management is manual
Make
Free tier (1,000 ops/month), paid from $9/monthMake can poll SP-API endpoints on a schedule using HTTP Request modules, but handling SQS-based ORDER_CHANGE notifications requires a custom webhook relay.
- + Scheduled polling possible with HTTP module
- + Better error handling than Zapier
- + Can connect to order management tools
- - getOrders 1/min rate limit makes polling architectures very slow
- - No native SQS integration
- - Complex auth setup
n8n
Free (self-hosted), Cloud from €20/monthn8n can implement the full SP-API order tracking flow: SQS trigger node reads notifications, Code node handles LWA token refresh, HTTP Request node calls getOrder, then Slack/database nodes for output.
- + SQS trigger node for event-driven architecture
- + Code node for LWA token management
- + Full API access via HTTP Request
- - Complex setup requiring SQS, SP-API auth, and n8n configuration
- - Self-hosted infrastructure needed
- - Requires developer knowledge
Best practices
- Use ORDER_CHANGE notifications via SQS as your primary order tracking mechanism — getOrders polling at 1/min is impractical for real-time tracking
- Call getOrder (0.5 req/sec) not getOrders when you have a specific order ID from a notification — 30x better rate limit
- Handle FBA and FBM orders differently: FBA orders are fulfilled by Amazon (monitor only), FBM orders require action (submit tracking)
- Request Restricted Data Tokens for PII only when needed and cache them for their 1-hour lifetime — don't create a new RDT per order
- Implement SQS message idempotency: store processed notification IDs to handle duplicate deliveries safely
- Subscribe to FBA_OUTBOUND_SHIPMENT_STATUS notifications for FBA order delivery tracking alongside ORDER_CHANGE
- Check the x-amzn-RateLimit-Limit header on every getOrders call — your dynamic usage plan may give you a higher rate than the documented 0.0167 req/sec
Ask AI to help
Copy one of these prompts to get a personalized, working implementation.
I'm building Amazon SP-API order tracking in Python using the event-driven architecture. I subscribe to ORDER_CHANGE notifications via SQS, then call getOrder and getOrderItems when events arrive. Help me: (1) implement LWA token refresh with 5-minute early refresh and caching, (2) parse the ORDER_CHANGE SQS message structure to extract the Amazon order ID, (3) get a Restricted Data Token for calling getOrderBuyerInfo to retrieve buyer name and address for FBM fulfillment, and (4) implement exponential backoff for 429 errors that respects the per-endpoint x-amzn-RateLimit-Limit response header.
Build an Amazon order tracking dashboard in React with: (1) a real-time order status board with columns for Pending, Unshipped, Shipped, and Delivered orders updated every 30 seconds via a backend polling endpoint, (2) each order card showing order ID, ASIN, quantity, status, and purchase date, (3) color coding: orange for Unshipped FBM orders needing attention, blue for FBA orders, green for Shipped, (4) a filter to show only FBM orders requiring tracking submission, (5) a detail panel showing order items when an order card is clicked. Backend endpoint: GET /api/amazon/orders?status=Unshipped returns array of order objects.
Frequently asked questions
Why is getOrders limited to 1 request per minute?
Amazon's SP-API rate limits are operation-specific and designed to encourage event-driven architectures. getOrders was set to 0.0167 req/sec (1/min sustained) to push developers toward using ORDER_CHANGE notifications via SQS instead. This design makes sense: a notification arrives within seconds of any order change, while polling at best has 1-minute lag. Your actual applied rate may be higher than the documented default if Amazon's dynamic usage plan has raised it based on your order volume — check the x-amzn-RateLimit-Limit response header.
What's the difference between FBA and FBM order tracking?
FBA (Fulfilled by Amazon) orders are handled entirely by Amazon's warehouses and carriers — you cannot submit tracking because Amazon does the fulfillment. You can only monitor these via FBA_OUTBOUND_SHIPMENT_STATUS notifications. FBM (Fulfilled by Merchant) orders require you to ship the product, generate a tracking number with your carrier, and submit it back to Amazon via the Listings API or Feeds API. ORDER_CHANGE notifications fire for both types.
Why do I get a 403 when calling getOrderBuyerInfo?
Buyer name, email, and contact information are classified as PII (Personally Identifiable Information) and require a Restricted Data Token (RDT). You cannot access these fields with a regular LWA access token. Request an RDT via POST /tokens/2021-03-01/restrictedDataToken, specifying the exact operations you need (getOrderBuyerInfo, getOrderAddress). Use the RDT in place of the regular access token for those specific calls.
How do I handle ORDER_CHANGE notification duplicates?
Amazon's Notifications API delivers messages at least once — the same event can be delivered multiple times. Use the NotificationMetadata.NotificationId field as a deduplication key. Store processed notification IDs in Redis or a database (with a TTL) and skip messages that have already been processed. Always delete SQS messages only after successful processing to enable automatic retry on failure.
What happens when I hit the rate limit?
Amazon returns a 429 QuotaExceeded error. The x-amzn-RateLimit-Limit response header shows your actual applied rate — trust this over the documented defaults, as dynamic usage plans may give you a higher rate. For getOrders (1/min), implement a 60-second wait after any 429. For getOrder (0.5 req/sec), a 2-second wait is sufficient. Exponential backoff is appropriate for 500 and 503 errors.
Is Amazon SP-API free to use?
As of May 2026, SP-API usage is free — Amazon announced an SP-API fee structure in November 2025 but reversed it on May 14, 2026. There are no usage fees or annual fees as of this writing. Check the SP-API Developer portal for any future fee announcements.
Can RapidDev help build a custom Amazon order management integration?
Yes. RapidDev has built 600+ apps including multi-channel order management systems, Amazon FBM fulfillment workflows, and real-time order tracking dashboards. If you need a custom SP-API integration, get a free consultation at rapidevelopers.com.
Need this automated?
Our team has built 600+ apps with API automations. We can build this for you.
Book a free consultation