Skip to content
Effloow
← Back to article
EFFLOOW LAB LAB-RUN

Openai Spend Limit 429 Fail Closed Client Audit 2026

Evidence notes document the bounded local or source-based checks behind an Effloow article. They are not product endorsements, legal advice, or benchmark claims.
  • Slug: openai-spend-limit-429-fail-closed-client-audit-2026
  • Date: 2026-07-29 (UTC)
  • Track: api-backed-poc
  • Evidence level: openai-api-backed-lab (live API calls + local sandbox harness)
  • Environment: macOS, Python 3.12.8, openai Python SDK 2.37.0
  • Safety boundary: no confidential, customer, credential, or private data was sent. Live prompts were the literal string Reply with the single word: ok. No real spend limit was tripped, and no production system was touched.

What we set out to check

OpenAI documents that a hard spend limit makes requests return 429 with the insufficient_quota code, while ordinary throughput throttling also returns 429. The question: does a normal client tell those apart, and what does a default client do when it can't?

Three probes:

  1. Local retry harness — a fake OpenAI endpoint that always answers 429, once with the spend-limit body and once with the rate-limit body. Count how many HTTP requests one SDK call actually produces.
  2. Discriminator probe — inspect the raised exception to see which fields, if any, separate the two cases.
  3. Live header probe — one tiny real call to each of /v1/responses and /v1/chat/completions to see what limit signalling a healthy 200 carries.

Probe 1 — local retry harness (zero cost, no OpenAI traffic)

Script: /tmp/effloow-429-lab/probe_local.py (local http.server on 127.0.0.1:8137, SDK pointed at it via base_url, fake key sk-local-fake-not-a-real-key).

python3 probe_local.py

Output:

{
  "insufficient_quota": {
    "http_requests_sent": 3,
    "wall_clock_seconds": 1.56,
    "request_offsets_seconds": [0.35, 0.78, 1.562],
    "exception": "RateLimitError"
  },
  "rate_limit_exceeded": {
    "http_requests_sent": 3,
    "wall_clock_seconds": 2.01,
    "request_offsets_seconds": [0.002, 1.01, 2.013],
    "exception": "RateLimitError"
  },
  "insufficient_quota_max_retries_0": {
    "http_requests_sent": 1,
    "wall_clock_seconds": 0.0
  },
  "_env": { "openai_sdk": "2.37.0", "python": "3.12.8" }
}

Observations:

  • One application-level call became 3 HTTP requests in both cases: the original plus the SDK's two default retries.
  • The SDK raised the same RateLimitError class for both bodies.
  • With retry-after: 1 present, retries landed ~1.0s apart (0.002 / 1.01 / 2.013). Without it, the SDK used its own backoff (0.35 / 0.78 / 1.562).
  • max_retries=0 produced exactly 1 request.

Probe 2 — what distinguishes the two 429s

Script: /tmp/effloow-429-lab/probe_discriminator.py, max_retries=0.

{
  "insufficient_quota": {
    "exception_class": "RateLimitError",
    "status_code": 429,
    "e_code": "insufficient_quota",
    "e_type": "insufficient_quota",
    "body_error_code": "insufficient_quota",
    "has_retry_after_header": false
  },
  "rate_limit_exceeded": {
    "exception_class": "RateLimitError",
    "status_code": 429,
    "e_code": "rate_limit_exceeded",
    "e_type": "requests",
    "body_error_code": "rate_limit_exceeded",
    "has_retry_after_header": true
  }
}

Observation: status code and exception class are identical. The usable discriminator is error.code on the exception body (e.code). The presence of a retry-after header is a secondary signal in this harness, but it is set by our fake server, so treat it as illustrative rather than as verified OpenAI behaviour.

Probe 3 — live limit headers on a healthy response

Two real calls, model gpt-5.5-2026-04-23, max_retries=0, budget-guarded via scripts/proof_budget.py. Artifact: openai-spend-limit-429-fail-closed-client-audit-2026.openai.json.

/v1/responses — HTTP 200, usage 29 total tokens. Limit-related headers found:

{ "x-request-id": "28bed0ce-b8b6-46eb-9d40-f68dca0e2b2d" }

No x-ratelimit-* header at all.

/v1/chat/completions — HTTP 200, usage 29 total tokens, same account, same model, same session:

[
  "x-ratelimit-limit-requests",
  "x-ratelimit-limit-tokens",
  "x-ratelimit-remaining-requests",
  "x-ratelimit-remaining-tokens",
  "x-ratelimit-reset-requests",
  "x-ratelimit-reset-tokens"
]

Daily token ledger after both live calls: 58 tokens.

What worked / what did not

  • Worked: reproducing the retry multiplication and the exception-field discriminator deterministically, with no OpenAI spend.
  • Worked: observing a real endpoint-to-endpoint difference in limit headers.
  • Not attempted: tripping an actual OpenAI hard spend limit. We did not want to disable the account's API access, so the insufficient_quota response body used in probes 1 and 2 is the documented shape, served locally.

Limitations

  • The 429 bodies in probes 1 and 2 were served by our own local endpoint. They reproduce the documented shape; they are not captured OpenAI responses.
  • Probe 3 is one account, one model, one moment. Header presence may vary by tier, endpoint version, region, or over time. It is not a claim that the Responses API never returns rate-limit headers.
  • Retry counts and backoff timings are specific to openai Python SDK 2.37.0 defaults. Other SDKs, gateways, and HTTP clients differ.
  • No production traffic, customer data, or third-party service was involved.
  • The wall-clock difference between the two cases (1.56s vs 2.01s) is not a measurement of OpenAI pacing. It is an artifact of our own fake server, which set retry-after: 1 on the rate-limit body and no such header on the spend-limit body. Only the ~1.5s "wasted wait under SDK defaults" figure should be cited.
  • insufficient_quota is not unique to a configured hard spend limit. The same code has long accompanied an exhausted prepaid balance ("you exceeded your current quota, please check your plan and billing details"). Both conditions are terminal and non-retryable, so the branch logic is unaffected, but alert copy should say "refused on billing grounds" rather than naming a cap.

Addendum 2026-07-29 (phase 1.5 verification)

Re-verified before publication:

  • Live WebSearch confirmed the rollout announcement is real: OpenAI Developer Community thread "Hard spend limits rolling out to all API Platform accounts" and the OpenAI Devs post stating access is being expanded "to all accounts this week." Article wording set to "began expanding ... in late July 2026" to match the in-progress phrasing rather than asserting completion.
  • Live WebSearch re-confirmed the spend-limits guide language on both the 429 + insufficient_quota contract and the non-instantaneous enforcement ("recorded spend can slightly exceed the configured amount").

Primary sources consulted

Read the article

This note supports the public article and records what was actually checked.

Open article →