Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

sp-api-node-starter

A production-ready Amazon SP-API client in TypeScript — proper auth caching, per-endpoint rate limiting, retry logic, and typed methods for Orders and FBA Inventory.
No bloated SDK. No magic. Just the patterns that actually work at scale.


Why this repo exists

I spent a year building an Amazon marketplace platform that syncs 50 k+ SKUs across 5 marketplaces, survived Black Friday at ~12,000 concurrent users, and cut a client's sync latency from 24 hours to under 15 minutes using the SP-API Notifications API + SQS + Lambda.

Every time I onboarded a new engineer to the codebase, the same two things tripped them up:

  1. Auth — the LWA token docs look simple but the naive implementation causes silent 401 storms under load.
  2. Rate limits — developers treat SP-API like a single rate-limited API. It isn't. Every operation has its own bucket.

This starter repo encodes both lessons.

Full write-up: amazon-sp-api-marketplace-automation
Built by Shubham Kansal — Full Stack & DevOps, 14 yrs, founder of DietGhar and NyayX.


Features

  • LWA token manager — caches the access token in memory, refreshes proactively 5 minutes before the 1-hour expiry, deduplicates concurrent refresh calls.
  • Per-endpoint token bucket — matches Amazon's published (burst, restoreRate) pairs exactly; conservative fallback for unknown operations.
  • Retry on 429/503 — exponential back-off, configurable max attempts.
  • Typed methodsgetOrders, getOrdersPages (async generator), getInventorySummaries, getInventoryPages.
  • Zero runtime dependencies beyond undici — use Node's built-in fetch if you're on Node 18+; swap in undici for older versions or more control.
  • MIT licensed.

Quickstart

1. Prerequisites

  • Node.js 20.6+ (uses --env-file flag; works with 18+ if you load .env manually)
  • An Amazon Seller Central account with SP-API access approved
  • An LWA app (OAuth client) created in Seller Central → Apps → Manage

2. Install

git clone https://github.com/shubhamkansal/sp-api-node-starter.git
cd sp-api-node-starter
npm install

3. Configure

cp .env.example .env

Open .env and fill in:

Variable Where to find it
LWA_CLIENT_ID Seller Central → Apps → your app → App ID
LWA_CLIENT_SECRET Same page, "Client Secret"
SP_API_REFRESH_TOKEN Output of the OAuth grant flow (see below)
SP_API_REGION us-east-1 (NA), eu-west-1 (EU), us-west-2 (FE)
SP_API_MARKETPLACE_ID See table in .env.example

4. Build and run

npm run build
node --env-file=.env dist/examples/getOrders.js

The auth story nobody explains

Amazon's LWA flow is OAuth 2.0 with a non-obvious quirk: the access token expires after exactly 3600 seconds and you need to refresh it using your long-lived refresh token. The documentation makes this look straightforward.

The trap: if you fetch a new token on every API call (the "stateless" approach), you're making N token requests for N API calls. At 50 k SKU syncs that's thousands of redundant round-trips to api.amazon.com. Worse, if two threads race to refresh simultaneously they can both succeed — wasting a refresh and briefly using a soon-to-be-stale token.

The fix (in src/auth/lwaTokenManager.ts):

┌─────────────────────────────────────────────────────────┐
│  Token valid?  ──── yes ──── return cached token        │
│       │                                                 │
│      no                                                 │
│       │                                                 │
│  Refresh in flight? ── yes ── await existing promise    │
│       │                                                 │
│      no                                                 │
│       │                                                 │
│  POST /auth/o2/token → cache result, set expiresAt      │
└─────────────────────────────────────────────────────────┘

Proactive refresh fires 5 minutes before actual expiry. No request ever finds itself mid-execution with a token that expires on the next line.


SP-API rate limit model

SP-API does not have a single global rate limit. Each operation has its own token bucket defined in the docs:

Operation Burst Restore rate
getOrders 1 req 0.0167 req/s (~1/min)
getOrderItems 1 req 0.5 req/s
getInventorySummaries 2 req 2 req/s
searchCatalogItems 5 req 5 req/s
createReport 15 req 0.0167 req/s

src/rateLimit/tokenBucket.ts implements a token bucket that mirrors this model exactly. The RateLimitRegistry keeps one bucket per operation name and lazily creates them with the correct config.

Pre-emptive limiting (waiting before the request) is better than reactive limiting (catching 429s) because:

  • It avoids a full network round-trip.
  • It avoids burning your retry quota.
  • 429 responses from Amazon can themselves trigger secondary throttling.

Project structure

sp-api-node-starter/
├── src/
│   ├── auth/
│   │   └── lwaTokenManager.ts    # LWA OAuth token cache + proactive refresh
│   ├── rateLimit/
│   │   └── tokenBucket.ts        # Per-endpoint token bucket + registry
│   ├── client/
│   │   └── spApiClient.ts        # Typed SP-API client (auth + rate limit + retry)
│   └── examples/
│       └── getOrders.ts          # Runnable example
├── .env.example
├── .gitignore
├── LICENSE
├── package.json
└── tsconfig.json

Extending the client

Adding a new SP-API operation takes three steps:

1. Add the rate limit to KNOWN_RATE_LIMITS in tokenBucket.ts:

getPricing: { burst: 2, restoreRatePerSec: 0.1 },

2. Add types and a method in spApiClient.ts:

export interface GetPricingResponse { /* ... */ }

async getPricing(asin: string): Promise<GetPricingResponse> {
  const qs = new URLSearchParams({ Asin: asin, ItemType: "Asin",
    MarketplaceId: this.marketplaceId });
  return this.request<GetPricingResponse>("getPricing",
    `/products/pricing/v0/price?${qs}`);
}

3. Call it.

The rate limiter and retry logic are applied automatically by request().


Using the Notifications API (SQS + Lambda)

The example above polls getOrders. For real-time updates — the model that cut our sync latency from 24h to <15min — use the Notifications API:

  1. Create an SQS queue in your AWS account.
  2. Call createDestination (SP-API) to register it.
  3. Call createSubscription for ORDER_CHANGE, ITEM_INVENTORY_EVENT, etc.
  4. Wire a Lambda to the SQS queue and process messages.

This eliminates polling entirely. Detailed implementation:
https://shubhamkansal.com/blog/amazon-sp-api-marketplace-automation


FAQ

Do I need AWS credentials?
For basic SP-API calls: no. The only credentials you need are LWA (OAuth). AWS credentials are only required if you use the deprecated AWS Signature V4 authentication (older versions of the API), or if you're using the Notifications API with SQS/Lambda (for the AWS side, not the SP-API side).

Can I use this with Vendor Central?
Yes. The Vendor APIs (Orders, Invoices, Shipments) use the same LWA auth flow. Change the refresh_token to one obtained via a Vendor Central OAuth grant and point at the Vendor API paths.

Why undici instead of axios?
undici is Node's reference HTTP/1.1 + HTTP/2 client and ships with Node 18+ as the fetch implementation. It's faster than axios and adds zero transitive dependencies. If you need request interceptors similar to Axios, the request() method in spApiClient.ts is the right place to add them.

The getOrders burst is 1. That's tiny.
Yes. This is why pagination with NextToken is important: one getOrders call returns up to 100 orders, and you only spend 1 token per page. For backfill jobs, schedule them off-peak and use CreatedAfter/CreatedBefore windows rather than hitting the API in a tight loop.


Contributing

PRs welcome. If you're adding a new API operation, please include:

  • The BucketConfig entry in KNOWN_RATE_LIMITS with a comment linking to the SP-API docs page.
  • TypeScript types for the response.
  • A short example in src/examples/.

License

MIT — see LICENSE.


Built by Shubham Kansal — Full Stack & DevOps engineer, 14 years experience, serving US / UK / UAE / AU clients.
Full write-up and production lessons: https://shubhamkansal.com/blog/amazon-sp-api-marketplace-automation
Site: https://shubhamkansal.com

About

Production-ready Amazon SP-API client in TypeScript — LWA token caching, per-endpoint rate limiting, 429 retry, and typed Orders/Inventory methods. Full write-up at shubhamkansal.com.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages