Skip to main content
RapidDev - Software Development Agency
API AutomationsAmazonBearer Token

How to Automate Amazon Order Tracking using the API

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.

Need help automating? Talk to an expert
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate8 min read30-60 minutesAmazonMay 2026RapidDev Engineering Team
TL;DR

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

Auth

LWA OAuth 2.0 (Bearer token)

Rate limit

getOrders: 1 req/min; getOrder: 0.5 req/sec

Format

JSON

SDK

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

Base URLhttps://sellingpartnerapi-na.amazon.com

Setting 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.

  1. 1Register as a developer in Seller Central > Developer Console
  2. 2Submit Developer Profile with Inventory & Order Tracking role and explain your use case
  3. 3If you need buyer PII (name/address), also select 'Buyer Communication' restricted role
  4. 4Wait for Amazon approval and obtain your LWA credentials (client ID, client secret)
  5. 5Authorize your app through Seller Central to get the refresh token
  6. 6Set environment variables: AMAZON_LWA_CLIENT_ID, AMAZON_LWA_CLIENT_SECRET, AMAZON_LWA_REFRESH_TOKEN, AMAZON_SELLER_ID, AMAZON_MARKETPLACE_ID
  7. 7For notifications: set up an SQS queue in AWS and note its ARN (arn:aws:sqs:region:account:queue-name)
  8. 8Rotate LWA credentials every 180 days
auth.py
1import os
2import time
3import requests
4
5class SPAPIAuth:
6 def __init__(self):
7 self._token = None
8 self._expires_at = 0
9
10 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._token
26
27 def headers(self):
28 return {
29 'x-amz-access-token': self.get_token(),
30 'Content-Type': 'application/json'
31 }
32
33auth = 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

GET/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.

ParameterTypeRequiredDescription
orderIdstringrequiredAmazon order ID in format 111-1234567-1234567

Response

json
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"}}
GET/orders/v0/orders/{orderId}/orderItems

Returns 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.

ParameterTypeRequiredDescription
orderIdstringrequiredAmazon order ID

Response

json
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"}}]}}
GET/orders/v0/orders

Lists 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.

ParameterTypeRequiredDescription
MarketplaceIdsarrayrequiredArray of marketplace IDs, e.g. ['ATVPDKIKX0DER'] for US
LastUpdatedAfterstringoptionalISO 8601 timestamp — return orders updated after this time. Use for backfill.
OrderStatusesarrayoptionalFilter by status: Pending, Unshipped, PartiallyShipped, Shipped, Canceled, Unfulfillable

Response

json
1{"payload": {"Orders": [{"AmazonOrderId": "111-1234567-1234567", "OrderStatus": "Unshipped", "PurchaseDate": "2024-01-15T10:30:00Z"}], "NextToken": "next-page-token"}}
POST/tokens/2021-03-01/restrictedDataToken

Creates a Restricted Data Token (RDT) required for accessing buyer PII in getOrderBuyerInfo and getOrderAddress. The RDT replaces the regular access token for restricted calls.

ParameterTypeRequiredDescription
restrictedResourcesarrayrequiredArray of restricted operations you need access to. Each has method, path, and dataElements.

Request

json
1{"restrictedResources": [{"method": "GET", "path": "/orders/v0/orders/{orderId}/buyerInfo", "dataElements": ["buyerInfo"]}, {"method": "GET", "path": "/orders/v0/orders/{orderId}/address", "dataElements": ["shippingAddress"]}]}

Response

json
1{"restrictedDataToken": "Atz.sprdt|IwEB...", "expiresIn": 3600}

Step-by-step automation

1

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.

request.sh
1# 1. Get grantless token
2curl -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'
7
8# 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"}'
13
14# 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.

2

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.

request.sh
1# After receiving the ORDER_CHANGE notification with order ID 111-1234567-1234567:
2# Fetch full order details
3curl "https://sellingpartnerapi-na.amazon.com/orders/v0/orders/111-1234567-1234567" \
4 -H "x-amz-access-token: $TOKEN"
5
6# Fetch order items
7curl "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.

3

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.

request.sh
1# Create a Restricted Data Token for buyer info and address
2curl -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 }'
11
12# Use the RDT (not the regular token) for PII calls
13curl 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.

4

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.

request.sh
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.

automate_amazon_order_tracking.py
1import boto3
2import json
3import requests
4import os
5import time
6import logging
7
8logging.basicConfig(level=logging.INFO)
9log = logging.getLogger(__name__)
10
11BASE_URL = 'https://sellingpartnerapi-na.amazon.com'
12SLACK_WEBHOOK = os.environ.get('SLACK_WEBHOOK_URL')
13
14class SPAPIAuth:
15 def __init__(self):
16 self._token = None
17 self._expires_at = 0
18
19 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._token
32
33auth = SPAPIAuth()
34
35def 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())
43
44def send_slack(msg):
45 if SLACK_WEBHOOK:
46 requests.post(SLACK_WEBHOOK, json={'text': msg}, timeout=5)
47
48def 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 False
58
59 if not order_id:
60 log.warning('No order ID in notification')
61 return True # Acknowledge and discard
62
63 log.info(f'Processing ORDER_CHANGE: {order_id} ({change_type})')
64
65 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', {})
70
71 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'
74
75 if channel == 'AFN': # FBA
76 msg += '\nFulfilled by Amazon — monitoring shipment status via FBA_OUTBOUND_SHIPMENT_STATUS'
77 else: # FBM
78 if status == 'Unshipped':
79 msg += '\nAction required: submit tracking number via Listings API'
80
81 log.info(msg)
82 send_slack(msg)
83 return True
84
85def main():
86 sqs = boto3.client('sqs', region_name='us-east-1')
87 queue_url = os.environ['AMAZON_SQS_QUEUE_URL']
88
89 log.info('Starting ORDER_CHANGE notification processor')
90 while True:
91 result = sqs.receive_message(
92 QueueUrl=queue_url,
93 MaxNumberOfMessages=10,
94 WaitTimeSeconds=20
95 )
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')
107
108if __name__ == '__main__':
109 main()

Error handling

429QuotaExceeded — getOrders rate limit exceeded
Cause

The 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.

Fix

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.

Retry strategy

Wait at least 60 seconds after a 429 on getOrders. For getOrder (0.5 req/sec), wait 2 seconds.

403Access denied — restricted data token required
Cause

Calling getOrderBuyerInfo or getOrderAddress with a regular access token. These operations require a Restricted Data Token (RDT) because they return PII.

Fix

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.

Retry strategy

No retry — get an RDT first

403Access denied — missing required role
Cause

Your SP-API app doesn't have the Inventory & Order Tracking role, or for buyer PII, the Buyer Communication restricted role.

Fix

Update your Developer Profile in SP-API Developer Console to add the required roles. Submit for review and wait for Amazon's approval.

Retry strategy

No retry — complete role approval

404Order 111-1234567-1234567 not found
Cause

The order ID doesn't exist for this seller account or marketplace, or it's from a different marketplace region.

Fix

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.

Retry strategy

No retry — check order ID and region

401Unauthorized — access token expired
Cause

LWA access token has expired (1-hour lifetime).

Fix

Implement token caching with refresh when within 5 minutes of expiry. The SPAPIAuth class in the complete automation example handles this automatically.

Retry strategy

Refresh token and retry immediately

Rate Limits for Amazon SP-API Orders

ScopeLimitWindow
getOrders0.0167 requests (1/min) sustainedburst 20
getOrder0.5 requestsper second, burst 30
getOrderItems0.5 requestsper second, burst 30
createRestrictedDataToken1 requestper second, burst 10
retry-handler.ts
1import time
2import requests
3
4def 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 present
9 limit_header = resp.headers.get('x-amzn-RateLimit-Limit')
10 wait = max(60, 1/float(limit_header)) if limit_header else 60
11 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

advanced

Detect 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

intermediate

Process 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

intermediate

Subscribe 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

advanced

Sync 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/month

Zapier 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.

Pros
  • + Can receive webhook notifications if you set up a relay endpoint
  • + Connects to Slack, Sheets, CRM easily
Cons
  • - 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/month

Make can poll SP-API endpoints on a schedule using HTTP Request modules, but handling SQS-based ORDER_CHANGE notifications requires a custom webhook relay.

Pros
  • + Scheduled polling possible with HTTP module
  • + Better error handling than Zapier
  • + Can connect to order management tools
Cons
  • - getOrders 1/min rate limit makes polling architectures very slow
  • - No native SQS integration
  • - Complex auth setup

n8n

Free (self-hosted), Cloud from €20/month

n8n 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.

Pros
  • + SQS trigger node for event-driven architecture
  • + Code node for LWA token management
  • + Full API access via HTTP Request
Cons
  • - 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.

ChatGPT / Claude Prompt

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.

Lovable / V0 Prompt

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.

RapidDev

Need this automated?

Our team has built 600+ apps with API automations. We can build this for you.

Book a free consultation

Skip the coding — we'll build it for you

Our experts have built 600+ API automations. From prototype to production in days, not weeks.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.