Short answer
- The log’s duration runs up to the point where the gateway finishes processing;
- Your client is not done until the last byte of the response body has arrived and the connection signals that it is finished.
What the log’s duration actually covers
A call’s total latency breaks into five segments:duration_for_view (call duration in seconds), is_stream, and request_id (quote this one when reporting an issue).
Step 1: pin the gap to a segment with one curl
This is the entry point for everything below. Run it first, then decide which section applies.Reading the result
Match your numbers against this table — it decides what you do next:Step 2: measuring “the data arrived but the connection never finished”
If the table points at the third row, you need finer observation: read the response chunk by chunk, recording each chunk’s arrival time and the gaps between them. The question to answer is — once the last byte arrived, how long did the connection keep spinning?- Python
- Node.js
tail_99 above 30 seconds, or a max_gap above 30 seconds, counts as one “tail stall”. The signature is a max_gap occurring at the point where the byte count already reached 100% — every byte arrived, and only then did the waiting begin.
Step 3: benchmarking throughput between two servers
Between two machines you own
Useiperf3 to measure real throughput — this is the most accurate option:
From your server to our API
iperf3 cannot be used for this leg — we do not run an iperf server. Measure the effective rate from real calls instead:
Check whether your bandwidth is enough
An image response body is one solid block of base64. Measured magnitudes:Step 4: which fields your instrumentation should record
To describe the symptom precisely — whether for your own analysis or to send to us — record at least these per call:duration_for_view from the console log —
- Close to each other → the problem is downstream transfer; look at bandwidth and concurrency;
- Far apart → the problem is the finish signal or the client side.
Four things that reduce the risk immediately
Switch to URL output — the highest-leverage change
gpt-image-2-vip and gpt-image-2-all accept response_format: "url", returning an image link instead of base64. The response body drops from about 2.6 MB to about 0.3 KB — which removes the download and finish-signal problems at the same time, because a small response carries Content-Length and the client knows on its own when it is done.If your business depends on URL output, switch the token’s group to image2_OSS: deterministic URL output that will not degrade to base64 under resource pressure, at a 1x multiplier with no markup.Shrink the response body
output_format=jpeg with output_compression cuts the size by more than half versus PNG; lower size and quality to what you actually need instead of defaulting to 4K. Compressing input reference images to under 1.5MB helps the upload leg too.Cap concurrency at what your bandwidth supports
Split the timeout into three, and finish proactively once data is complete
Common questions
I never got the result — why was I still billed?
I never got the result — why was I still billed?
Would raising the timeout from 600 to 1200 seconds help?
Would raising the timeout from 600 to 1200 seconds help?
- Slow download (low rate, byte count still climbing): yes, raising it gets you the result.
- Finish signal never came (bytes complete long ago, nothing new at the end): no. We measured 330 seconds of continued waiting in this state without a single new byte; a longer timeout only delays the moment you notice. Here you need to finish proactively on the client.
Is this my problem or your gateway's?
Is this my problem or your gateway's?
- Points at you: a clearly low
speed_download, a rate that falls as concurrency rises, packet loss inmtr, or curl behaving fine while only your application code times out. - Points at us: bytes complete long ago with a long stretch of zero new data at the end. The gateway did have a “delayed finish signal” problem — its root cause was billing accounting on the image path blocking request handling — fixed and verified with an upstream release on 13 August 2026. Even in completely healthy periods, roughly 4% of requests still wait 10–79 extra seconds for the finish signal, with their data already fully delivered.
Is there an async API? I would rather not hold a connection open
Is there an async API? I would rather not hold a connection open
Can I work around it by changing endpoint or machine?
Can I work around it by changing endpoint or machine?
api-cf.apiyi.com runs through Cloudflare and returns 524 at roughly 100 seconds, which is unsuitable for long image requests.Three easily missed client-side causes
If curl measures clean and only your application code times out, look here:- The timeout does not mean what you think. Is your 600 seconds a total timeout or only a read timeout? Node’s
undicihas three independent timeouts —headersTimeout,bodyTimeout, andconnect.timeout— whose defaults are far below whatever you set on the outer layer, and changing only the outer one has no effect. - Connection-pool queueing. When the pool is saturated, the clock starts before the request is actually sent. That wait is completely invisible to us — there is no record of the request in the backend log until it truly goes out. Diagnostic: no matching log record generally means this class.
- There is another layer in between. A self-hosted nginx defaults
proxy_read_timeoutto 60 seconds, and load balancers, API gateways, and serverless platforms each impose their own ceilings. List the timeout at every hop; the smallest one is your real timeout.
What to include when reporting
If you still need our help after the self-check, send these together to save several rounds:- Request IDs (a handful is enough, not the full set)
- Per-segment timings: time to first byte / last-byte time / total time / response body bytes
- When it happened, with the time zone (e.g.
2026-08-13 15:57 (UTC+8)) - Concurrency at the time and your egress bandwidth
- Which model and token group you were using
