Between our 2026-09-09 and 2026-09-10 documentation snapshots, OpenAI's Advertiser API reference gained a control that ChatGPT Ads did not previously have in any form: a spending limit that applies across every campaign in an ad account at once.
Everything ChatGPT Ads has published about budgets until now lived inside a campaign. A daily budget is a campaign's daily budget. A campaign total budget is a campaign's total. There was no documented object that said "this account will not spend more than X, no matter how many campaigns exist inside it." As of today there is, and it is documented only in the developer docs — the help centre says nothing about it at all.
Last verified: 2026-09-10. Everything in the sections headed "what the documentation says" is quoted from developers.openai.com — Ad Account. Everything labelled Soku analysis is our editorial reading and is marked as such. Where the documentation is silent, this article says so rather than filling the gap.
Before the mechanics: most readers cannot use this
The restriction is a > Note: block three paragraphs into the new section, and it is the single most important sentence for a general audience:
"Account spending limits are available only for ad accounts on postpaid invoice billing."
That is the whole capability, gated. If your ChatGPT Ads account is on a card — which is how self-serve advertising accounts on every comparable platform work by default, and how most ChatGPT Ads accounts opened during the beta will be set up — none of what follows is available to you today.
Two further clauses in the same note:
"Existing daily limits can still be viewed and removed if the account's billing changes. All requests in this section, including reads, require permission to manage billing for the account."
Soku analysis. Two things follow. First, the read endpoint is gated on the same billing permission as the writes, which is unusual and operationally annoying: an ops engineer or a monitoring service that only needs to observe the account ceiling must be granted permission to manage billing to do it. There is no read-only scope documented for this data. If your organisation separates "can see the numbers" from "can change the money", this API does not respect that separation.
Second, the "if the account's billing changes" clause implies a real state you can land in — an account that once had invoice billing, has a daily limit, and no longer qualifies. OpenAI tells you what remains possible in that state (view, remove) and by omission what does not (create, edit). It does not say whether an existing limit continues to be enforced in that state. That is a genuine gap and it matters: an account that drops off invoice billing either keeps a live ceiling it can no longer adjust upward, or keeps a decorative object. The documentation does not distinguish these.
How we know the change is real and dated
Two independent signals, neither of which is an announcement.
The page itself. The "Account spending limits" section, and the six endpoints under it, are present in today's capture of ad-account.md and absent from the previous one.
The index changed its own description. OpenAI publishes a machine-readable index of the ads developer documentation at developers.openai.com/ads/llms.txt, where each page carries a one-line summary. On 2026-09-10 the Ad Account line changed:
| Description of the Ad Account page | |
|---|---|
| Before | "Update and retrieve account metadata." |
| Now | "Manage account metadata and account-wide spending limits." |
Soku analysis. That index line is the most reliable canary OpenAI ships for the ads API. Page bodies get edited constantly for wording; a page's one-line index summary changes only when someone decides the page is about something new. Watching llms.txt costs one HTTP request a day and catches capability additions before any human-facing surface mentions them.
And no human-facing surface does. There was no OpenAI advertiser product-update email in this window — the most recent is dated 2026-09-03 — and the help centre does not document account spending limits anywhere as of 2026-09-10. Nothing in this article rests on an email, and we deliberately do not cite a help-centre URL for a capability the help centre has not acknowledged.
What the documentation says: the model
Two limit types, one account.
"Set a shared spending limit across all campaigns in an ad account. Choose a date range limit for a total allowance over a fixed period, or a daily limit for an allowance that renews each day."
| Date range limit | Daily limit | |
|---|---|---|
| Shape | Total allowance over a fixed period | Allowance that renews each day |
| How many | Up to 60 future windows can be scheduled | One at a time (can_create_daily_limit is false while one exists) |
| Earliest start | Today or later | Tomorrow or later — earliest_daily_start_date "is never today" |
| Appears in list response | In the data array | In daily_limit, not in data |
| End date | Required, exclusive | Optional (null for none), exclusive |
| Concurrency control | Not required for date range mutations | expected_revision required on every mutation |
| Carry-over | N/A (single pooled total) | "Daily allowances do not carry over" |
| Status values | upcoming, active, completed | upcoming, active |
And the sentence that defines what these objects are not:
"Campaign budgets still apply; account limits do not allocate spend between campaigns or pace delivery."
Soku analysis: it caps, it does not pace — and that is a different instrument
This is the part worth reading twice, because ChatGPT Ads has spent the last month teaching advertisers that budgets pace.
We covered the campaign total budget becoming a paced budget type on 2026-09-09, with its 365-day maximum and 60-day default pacing period. We covered the daily budget's seven-day average and its 2×/7× bounds. Both of those are distribution mechanisms: they decide how a given amount of money gets spread across time, and both are documented as smoothing delivery.
An account spending limit does none of that. Three consequences follow directly from "do not allocate spend between campaigns or pace delivery":
1. It is a race, not an allocation. If you set a $10,000 date range limit across an account running eight campaigns, OpenAI does not divide $10,000 eight ways, does not respect campaign priority, and does not reserve headroom for the campaign you care about most. Whichever campaigns deliver fastest consume the shared pool first. A high-volume prospecting campaign can exhaust an account allowance before a low-volume, high-value retargeting campaign gets a meaningful share. The account limit is indifferent to which of your campaigns matters.
2. The failure mode is a cliff, not a slowdown. A paced campaign budget degrades gracefully — delivery slows as it runs ahead of schedule. An account limit does not degrade at all until it is reached, and then everything stops. If you were relying on pacing behaviour to keep an account delivering evenly across a month, an account limit will not give you that; you still need the campaign-level budget types for shape.
3. The stop is not instant. OpenAI says so:
"Delivery can take time to stop after a limit is reached."
No number is attached. There is no documented maximum overshoot, no documented latency, and no statement about whether spend beyond the limit is billed or absorbed. Combined with the next section — a lower limit cannot claw back billed spend — the honest reading is that an account spending limit is a soft ceiling with unquantified slack, and you should not set it at the exact number you cannot exceed.
The right mental model: campaign budgets are the steering, account limits are the guardrail. Guardrails are worth having. They are not the same product as steering, and the documentation is refreshingly clear that OpenAI is not claiming otherwise.
What the documentation says: amounts, dates, and the arithmetic
"Amounts are nonnegative integers in micros:
100000000is 100 currency units. Use the currency's smallest unit, such as multiples of10000micros for USD. The maximum is 1 billion currency units (1000000000000000micros)."
One currency unit is 1,000,000 micros. For a USD account the multiple-of-10000 rule is just "amounts land on whole cents".
| You mean | amount_micros |
|---|---|
| $1.00 | 1000000 |
| $100 | 100000000 |
| $1,000 | 1000000000 |
| $1,500 | 1500000000 |
| 1 billion units (the documented maximum) | 1000000000000000 |
On dates:
"Dates use
YYYY-MM-DD. Limits start at midnight on the start date and end at midnight on the end date, in the account timezone. The end date is excluded."
OpenAI's own example makes the convention concrete — a $1,000 allowance described as covering October 1–7 is created as:
{
"start_date": "2026-10-01",
"end_date": "2026-10-08",
"amount_micros": 1000000000,
"name": "October promotion"
}Seven days of spend, an end date of the eighth.
Also documented in the same block:
"Daily and date range limits cannot overlap. Existing scheduled limits are not removed when you create a daily limit."
"Daily allowances do not carry over or change with daylight saving time. The account timezone cannot change while a daily limit exists."
"Limits apply to billable ad spend, not taxes or the total invoice."
The six traps
Soku analysis. Each of these is a documented sentence with an operational consequence that the sentence does not spell out.
1. The exclusive end date is a one-day error waiting to happen
Every advertiser has typed a campaign end date meaning "run through this day". Here, end_date: "2026-10-07" for an October 1–7 flight gives you six days of allowance, and the seventh day of your flight is uncapped by that window. The mistake is silent: no error, no warning, a valid window that is simply one day short of what you meant. If you are wrapping these endpoints, add the day in your own code and write a test for it. If you are hand-rolling JSON against an insertion order, check every end date twice.
2. spent_micros absent is not spent_micros zero
Stated outright for date range windows:
"
spent_microsis included for the active window when available; an omitted value does not mean zero spend."
And for the daily limit, spent_micros is "null if unavailable or not active", remaining_micros likewise, and spend_as_of is the "spend counter update time... or null if unavailable".
This is the field most likely to be mishandled, because the naive dashboard treats a missing number as zero and renders a reassuring empty bar on an account that is spending hard. Two defences: treat null/absent as unknown and render it as unknown, and use spend_as_of as a freshness check rather than assuming the counter is live. OpenAI reinforces the distinction elsewhere in the list response — evaluated_at is documented as "configuration evaluation time, not the freshness of spend data". Configuration freshness and spend freshness are two different clocks and OpenAI is careful to say so.
3. A lower limit cannot undo spend already billed
"Amount edits take effect on save and retain counted spend; a decrease below recorded spend is rejected. Edits are not retroactive: delayed charges use the limit effective when the event occurred, and concurrent billing can add spend while an edit is saved. A lower limit cannot undo spend already billed."
The rejection is a specific, catchable error — budget_window_amount_below_spend, returned with a 422.
This is structurally the same rule as the campaign total budget's "your revised budget must cover the amount already spent", now applied at account level, plus two extra hazards. Delayed charges use the limit that was effective when the event occurred — so lowering a limit today does not retroactively suppress a charge for yesterday's click that has not landed yet. And concurrent billing can add spend while an edit is saved — so a decrease that was valid when you computed it can be rejected by the time it is processed. Any automation that lowers a limit needs to handle budget_window_amount_below_spend as an expected outcome, not an exception, and re-read before retrying.
The blunt version: an account spending limit is a forward-looking brake. It is not a refund mechanism and it is not an emergency stop for money already committed.
4. The timezone lock
"The account timezone cannot change while a daily limit exists."
A one-line sentence that turns a billing control into a configuration dependency. If your account timezone is wrong — set at signup, never revisited, and quietly making your "daily" spend boundary land in the middle of your business day — you must remove the daily limit before you can fix it. That is a window during which the account has no daily ceiling at all, and it is a change most teams would schedule rather than discover mid-incident. Fix the timezone first; add the daily limit second.
Related, and easy to miss: daily allowances "do not... change with daylight saving time". Your daily window is anchored to the account timezone's midnight, and across a DST boundary one real-world day is 23 or 25 hours long. OpenAI is telling you the allowance is not adjusted for that.
5. 503 may mean it worked
The error table is short and its last row is the interesting one:
| Status | Documented action |
|---|---|
400 | "Correct missing or malformed request fields, including invalid calendar dates or dates outside the supported range of 2000-01-01 through 2100-01-01." |
409 | "Refresh the list and reconcile a stale revision or configuration conflict before resubmitting the change." |
422 | "Correct the amount, date ordering, overlapping limits, or a change to a completed date range limit. A decrease below recorded spend returns budget_window_amount_below_spend." |
503 | "Refresh before retrying a daily mutation: the change may have succeeded even if its response could not be loaded." |
That 503 note is an explicit admission that daily-limit mutations are not idempotent from the client's point of view. The naive retry — catch 503, resend the same body — is wrong twice over: the change may already be applied, and the expected_revision you are resending is now stale, so the retry will fail with a 409 anyway. The documented pattern is refresh-then-reconcile: GET /ad_account/spend_limit_windows, compare daily_limit to your intent, and only resubmit if the state does not already match, with the new revision.
6. A daily limit cannot start today
"New daily limits start tomorrow or later; use
earliest_daily_start_datefrom the list response."
And in the field table: earliest_daily_start_date is the "earliest allowed start date for a new daily limit. It is never today. The full period must also avoid existing limits."
This rules out the most obvious use case. "Something has gone wrong, cap the account today" is exactly the moment an operator would reach for a daily limit, and it is exactly what this endpoint will not do. A date range limit can start today, which is the only same-day account-level lever documented — but it is a total allowance over a period, not a per-day cap, and it must not overlap anything already scheduled. Account spending limits are a planning instrument. Treat the incident-response question as unsolved by this API.
The endpoints, in the order you would actually call them
Six endpoints, all under the ad account. Reads are GET; every mutation is a POST, including deletes.
| Purpose | Endpoint |
|---|---|
| Read current configuration | GET /ad_account/spend_limit_windows |
| Create a date range limit | POST /ad_account/spend_limit_windows |
| Update a date range limit | POST /ad_account/spend_limit_windows/{window_id} |
| Delete a date range limit | POST /ad_account/spend_limit_windows/{window_id}/delete |
| Create or update the daily limit | POST /ad_account/daily_spend_limit |
| Remove the daily limit | POST /ad_account/daily_spend_limit/delete |
The list endpoint is the one everything else depends on. OpenAI's instruction is "read the configuration before making changes", and the response carries the state you need to make a legal mutation:
| Field | What it is for |
|---|---|
data | Date range limits, ordered by start date. Daily limits are not in this array. |
daily_limit | The active or upcoming daily limit, or null when none exists. |
revision | Send as expected_revision in daily mutations. "Use 0 only if the field is absent." |
earliest_daily_start_date | Earliest legal start for a new daily limit. Never today. |
earliest_dated_start_date | Additional start-date floor for date range limits after a daily limit, or null. |
can_create_daily_limit | false while one exists — which "does not prevent editing it". |
evaluated_at | Configuration evaluation time, not spend-data freshness. |
Each date range window carries window_id, start_date, end_date, amount_micros, name, io_id, status, and the three capability booleans can_edit, can_edit_start_date, can_delete.
Soku analysis. Use the booleans; do not re-derive them. The edit rules are stated separately — "only upcoming limits can change their start date", "an active limit's end date must remain in the future", "completed limits cannot be edited", and for deletion "you can delete active or upcoming limits, but not completed limits" — and a client that reimplements that logic from status and today's date will eventually disagree with the server about a boundary case. The server ships you its answer on every read.
The daily limit object adds four live-state fields on top of amount_micros, start_date, end_date, timezone and status: spent_micros, remaining_micros, spend_as_of, and next_reset — the last being "next midnight with a fresh allowance... null before activation or on the final day". That null-on-the-final-day case is worth handling; a countdown widget that assumes next_reset is always present will break on exactly the day it matters most.
On daily mutations specifically: amount_micros and expected_revision are both required, and amount_micros is "required even when only changing the end date". start_date must be omitted when editing, because "an existing start date cannot change". The response returns spend_limits with the same shape as the list response, so you read spend_limits.revision for the next revision rather than issuing another list call.
Who this is actually for
Soku analysis. Two audiences, and the documentation quietly names one of them.
Agencies running client budgets against an insertion order
The io_id field exists for precisely this:
"
io_id| string or null | No | Optional insertion order reference, up to 256 characters."
Alongside name (also 256 characters), that gives you a date range window that is directly reconcilable to a signed contract: a start date, an exclusive end date, a total allowance, a human label, and the IO reference. Sixty of them can be scheduled ahead. For an agency that has ever had to explain to a client why spend under a Q4 IO leaked into the following period, an account-level window keyed to io_id is a materially better artefact than a spreadsheet of campaign budgets.
The caveat is the same one from earlier: the window enforces a total, not an allocation. It stops the account from overspending the IO. It does not stop one campaign from consuming the IO.
Finance teams who want a ceiling independent of the campaign count
The structural weakness of campaign-level budgets is that they scale with the number of campaigns. Ten campaigns at $500/day is $5,000/day, and every new campaign a team launches raises the account's theoretical maximum without anyone re-approving a number. An account daily limit breaks that link: it is one number, set by whoever holds billing permission, that does not move when a media buyer launches campaign eleven.
That is a genuinely useful separation of duties — and the billing-permission gate on the endpoints, annoying as it is for read-only monitoring, is what makes it a real control rather than a suggestion. A media buyer cannot raise it.
Who this is not for
Anyone on card billing. Anyone who needs a cap today. Anyone who wants the account limit to shape delivery rather than truncate it. And anyone who wants a hard guarantee of a number they cannot exceed, given "delivery can take time to stop after a limit is reached" ships without a bound.
Where Soku sits, honestly
Soku does not integrate with these endpoints. We have not built it, and we are not going to imply otherwise while the capability is invoice-only and one day old.
What we do is a different layer of the same problem, and the difference is worth stating precisely because it is the reason an account spending limit does not make it redundant — or vice versa.
An account spending limit is a provider-side ceiling: it sits inside OpenAI's billing system, it is authoritative, and it acts at the moment spend is counted. Its weaknesses are the ones catalogued above — it stops late, it cannot claw back, it cannot start today, and it does not know which of your campaigns deserved the money.
Soku's spend approvals and balance monitoring act before and around spend: an agent that wants to launch a campaign or raise a budget asks first, and a human decides with the cross-platform picture in front of them. That catches the class of problem an account limit structurally cannot — the spend that was authorised, was within the ceiling, and was still the wrong call. It is also, unlike an account limit, not confined to one platform or one billing arrangement.
Neither replaces the other. If you are on invoice billing, set an account limit; it is free and it is the only thing standing between you and an unbounded invoice if something goes badly wrong at 3am. Then do not mistake it for a control system.
The one-paragraph version
ChatGPT Ads now has account-level spending limits, documented as of 2026-09-10 in the Advertiser API reference for the Ad Account resource and nowhere else — not in the help centre, not in an email. Two shapes: a date range limit (total allowance over a fixed period, up to 60 scheduled, can start today) and a daily limit (renews each day, one at a time, cannot start today). They are available only on postpaid invoice billing, and even reading them requires permission to manage billing. They cap, they do not pace — campaign budgets still apply, and OpenAI explicitly does not allocate the shared pool between campaigns. The traps that will cost someone real money: the exclusive end date, spent_micros absent not meaning zero, delivery taking time to stop after the limit is reached, a lower limit not undoing billed spend (budget_window_amount_below_spend, 422), the timezone lock while a daily limit exists, and a 503 that may mean the change succeeded. Useful for agencies reconciling to an insertion order — io_id is there for exactly that — and for finance teams who want one number that does not rise every time someone launches a campaign.
Sources
- Ad Account — Advertiser API reference (developer docs; the "Account spending limits" section, all six endpoints, field tables and error table — the source for every fact stated above, captured 2026-09-10)
- developers.openai.com/ads/llms.txt (developer docs index; the Ad Account one-line description changed on 2026-09-10 from "Update and retrieve account metadata." to "Manage account metadata and account-wide spending limits.")
Not cited, deliberately: the OpenAI help centre, which does not document account spending limits on any page as of 2026-09-10; and the OpenAI advertiser product-update email, which had no new issue in this window — the most recent is dated 2026-09-03. Nothing above is attributed to either. Statements about what the documentation does not say are statements about the documentation, not about the product. The micros conversions are arithmetic from OpenAI's stated rule that 100000000 micros is 100 currency units; they are not platform benchmarks or pricing.
Related reading
- ChatGPT Ads now paces campaign total budgets — the campaign-level control this sits above, and the 365-day / 60-day pacing periods
- ChatGPT Ads daily budgets are a seven-day average — the 2× day and 7× week bounds, unchanged by this update
- Unspent ChatGPT Ads budget and rollover — why an underdelivering campaign is a deferred liability
- ChatGPT Ads daily budget minimums and the bid floor
- Everything you need to know about ChatGPT Ads










