A supplier API answered every request with HTTP 200 - but during throttling the body came back empty. Status codes alone told us everything was fine while records quietly went missing.
By Team WebSync · · 4 min read

This is the bug we bring up most often when someone asks why integration work takes longer than it looks. It cost real data, it produced no errors, and nothing in the logs was red.
We were running a sync pipeline against a large cross-border supplier's B2B API - authenticate, crawl categories, pull product records, write them into the database. It worked. Then, on longer runs, the final product counts started coming in lower than expected. Not zero. Just short.
There were no exceptions. No failed requests. No non-200 responses. Every monitoring signal we had said the run completed successfully. The only evidence anything was wrong was a number at the end that didn't match what we knew was in the source catalog.
The supplier's API rate-limited us. That part was expected - most do. What was not expected was how it communicated that.
Instead of returning 429 Too Many Requests, or a 503, or an error object with a retry-after header, it returned HTTP 200 with an empty body. As far as our code was concerned, the request had succeeded and the category simply contained no products. So we recorded zero results, moved on to the next page, and never retried.
That last point is what makes this class of bug expensive. It only appears under load, and it disappears the moment you try to reproduce it in isolation.
The principle we settled on: validate the shape of the response, not just the status of the response.
The retry itself was ordinary exponential backoff. The hard part was never the recovery mechanism - it was noticing there was something to recover from.
HTTP status codes are a convention, not a guarantee. Plenty of third-party APIs - especially older ones, regional ones, and ones not primarily designed for public integration - signal problems in ways that don't map onto the spec. We've seen success codes wrapping error payloads, XML errors returned with a JSON content type, and, as here, throttling expressed as silence.
If you're integrating an API you don't control, assume its error handling is different from what the docs imply, and write your client so that "I got nothing back" is a case you handle explicitly rather than a case you accidentally accept.
A pipeline that fails loudly is a bug. A pipeline that fails silently is a liability - you don't find out until someone downstream asks why the numbers are wrong.
Two checks worth adding this week. First, reconcile counts: whatever your source says it has, compare it to what you actually stored, every run. Second, add a deliberate assertion that a successful response contains the data it's supposed to contain - and make the failure of that assertion noisy.
Neither takes long to build. Both would have caught this on day one.
The supplier's API responded to rate-limiting with HTTP 200 and an empty body instead of an error code, so status-code checks and try/catch both passed cleanly while records silently went missing. The fix was validating the shape of every response, not just its status, before treating it as successful.
Clean, format, parse, and validate JSON inputs with instant syntax lint warnings and code export.
Decode, verify, parse, and edit JWT token components including headers, payloads, and signatures.
Convert Unix epoch timestamps to readable dates and back, in local time or UTC.
Book a free consult - we'll scope it and give you a fixed price.