Amazon SP-API 8 min read

The SP-API Reports Pattern Everyone Gets Wrong (Async, Step by Step)

The SP-API Reports API is asynchronous — request, poll, download, decompress — and most broken integrations either treat it like a synchronous call or poll it straight into a throttle. Here's the correct four-step flow, the status states that trip people up, and the gzip gotcha at the end.

Updated Jul 2026
The SP-API Reports Pattern Everyone Gets Wrong (Async, Step by Step)

It's Asynchronous — That's the Whole Trick #

The number-one mistake: expecting report data back from the create call. createReport returns a reportId, not rows. Amazon generates the report in the background; you have to poll for it, then fetch the finished document separately. Four steps, in order:

  1. Create the report → get a reportId.
  2. Poll the report until its processingStatus is DONE.
  3. Fetch the report document → get a download URL.
  4. Download and decompress the file, then parse it.

Step 1 — createReport #

Specify the report type, the marketplaces, and an optional date window:

POST /reports/2021-06-30/reports
{
  "reportType": "GET_FLAT_FILE_OPEN_LISTINGS_DATA",
  "marketplaceIds": ["ATVPDKIKX0DER"],
  "dataStartTime": "2026-07-01T00:00:00Z",
  "dataEndTime":   "2026-07-07T00:00:00Z"
}

You get back a reportId. That's your handle for everything downstream.

Step 2 — Poll getReport (and Handle the Status States) #

Call GET /reports/2021-06-30/reports/{reportId} and watch processingStatus:

Status What it means / what to do
IN_QUEUENot started yet — keep polling.
IN_PROGRESSGenerating — keep polling with backoff.
DONEReady — grab the reportDocumentId and move to step 3.
CANCELLEDNo data for the range (often legitimate) — stop, don't treat as an error.
FATALGeneration failed — log it and retry the create, don't keep polling.

Poll with backoff. A tight polling loop is the fastest way to a 429. Start around 10–30 seconds between polls; big reports can take minutes.

Step 3 — getReportDocument #

Once status is DONE, the report carries a reportDocumentId. Exchange it for a download:

GET /reports/2021-06-30/documents/{reportDocumentId}
→ { "reportDocumentId": "...",
    "url": "https://tortuga-prod-...s3...",   // presigned, short-lived
    "compressionAlgorithm": "GZIP" }

The url is a presigned S3 link that expires quickly — download it promptly, don't cache it for later.

Step 4 — Download, Decompress, Parse #

The gotcha that produces "garbage bytes": when compressionAlgorithm is GZIP, the downloaded file is gzip-compressed. You must decompress before parsing.

import gzip, requests
raw = requests.get(doc["url"]).content
data = gzip.decompress(raw) if doc.get("compressionAlgorithm") == "GZIP" else raw
# most flat-file reports are tab-delimited; some newer ones are JSON
rows = data.decode("utf-8").splitlines()

Flat-file reports are usually tab-delimited text; newer report types may be JSON. Check the report type's documented format.

Don't Recreate Reports You Already Have #

Creating the same report over and over wastes quota and can throttle you. Before creating, call getReports to see if a recent one for that type and range already exists, and reuse it. For recurring needs, use report schedules so Amazon generates them on a cadence and you just fetch the latest.

Prefer not to babysit report pipelines? Reliable SP-API data pipelines — with retries, backoff, and reconciliation — are exactly what I build for sellers and agencies.

FAQ #

Why does createReport return no data?

Because it's asynchronous. createReport only returns a reportId. You poll getReport until it's DONE, then fetch the document separately.

What does CANCELLED mean?

Usually that there's no data for the requested range — not a failure. Stop polling and move on. FATAL, by contrast, means generation failed and you should log it and retry the create.

My downloaded report is unreadable bytes. Why?

It's gzip-compressed. When compressionAlgorithm is GZIP, decompress the file before parsing.

How often should I poll?

With backoff — start around 10–30 seconds between polls. Tight polling loops are the most common cause of 429 throttling on the Reports API.

Why did my download URL stop working?

The document url is a presigned S3 link that expires within minutes. Fetch it right after you get it; don't store it to use later.

Don't want to build this yourself?

I set this up for sellers and agencies every week. Book a free 30-minute audit of your Amazon data & PPC setup — you'll leave with a plan either way, whether we work together or not.

Found this helpful? Share it:

WhatsApp