Moving a Project Off a Free Tier Without an Outage
10 min read · updated August 11, 2026
A project that outgrew a free tier does not fail by hitting a wall. It fails by paying, ramping to the traffic it always had, and discovering that the paid limits at the tier you just entered are closer to the free ones than to the ones in the table you planned against.
Four things change, not one
Teams plan for the rate limits and get surprised by the rest.
- The limits. Higher, but not immediately as high as the published ceiling — see the next section.
- The data handling terms. Several providers document different treatment of free-tier traffic than of paid traffic, including whether prompts and outputs may be used to improve products. This is a clause type to read in your own agreement before you route customer data, not a fact any page can assert on your behalf, because the terms are set per provider and per contract and change. If your free-tier usage has already been production traffic, that is a question to resolve before the migration rather than after.
- The model list. Free tiers often expose a subset. A model available in evaluation may not be the model you can deploy, and the reverse also happens.
- The failure mode. On a free tier, running out is a 429. On a paid tier, running out is a bill. A retry loop that was merely noisy becomes expensive the moment it succeeds, which makes a spend ceiling part of the cutover rather than a follow-up.
You land on the bottom paid tier
Where limits derive from a spend tier, paying does not grant the limits you sized against. OpenAI documents six levels: Free, then Tier 1 qualifying at $5 paid, Tier 2 at $50, Tier 3 at $100, Tier 4 at $250 and Tier 5 at $1,000, each with its own monthly usage cap — $100 on Free and on Tier 1, rising to $200,000 at Tier 5 — and states that organisations graduate automatically as spend rises. See OpenAI’s rate limits guide.
Two consequences for planning. Your ramp must fit inside the bottom paid tier’s limits, because that is where you begin, and the way to the next tier is spend that accrues over calendar time you cannot compress. And the monthly usage cap is itself a limit: a workload that fits the per-minute limits can still stop at month-end against the cap, which is a failure mode with no per-request warning at all.
If you also need an elevated limit beyond the tier system, that is a separate track with its own lead time, covered in what a migration means for an existing rate-limit increase. Start it in parallel with step 1 below.
Billing and keys are separate switches
Adding a payment method does not rotate your API key, and the same key can carry different limits before and after. So “is this project on paid?” is not answerable by looking at the key string, and it is not reliably answerable from the console either if several projects share an organisation. It is answerable from a live response: the rate-limit headers reflect what the account actually has right now.
Which is the argument for creating a new project and a new key for paid traffic rather than upgrading the existing one in place. Two keys give you attribution — you can tell free usage from paid usage in your own logs — and, more importantly, they give you a rollback that is a config change instead of a billing change.
The cutover
- Measure current demand. From your request log, bucket by minute over fourteen days and take the p99 of requests per minute and of total tokens per minute. The p99 minute is what a limit is enforced against; a daily total tells you nothing about whether you will be throttled.
- Create a separate project and key for paid traffic. Do not reuse the free key. Record which services hold which.
- Add billing and set a hard monthly budget below the tier’s usage cap, with alerts at 50% and 80% of the budget rather than at 100%, where the alert and the outage arrive together.
- Probe the limits you actually have. One real request, headers dumped:
curl -sS -D - -o /dev/null https://api.openai.com/v1/chat/completions \ -H "Authorization: Bearer $PAID_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"<model-you-will-ship>","messages":[{"role":"user","content":"ping"}],"max_tokens":1}' \ | grep -i '^x-ratelimit'Readx-ratelimit-limit-requestsandx-ratelimit-limit-tokensfrom the output. Do this per model you intend to ship, because limits are per model. - Compute the fraction you can move. Divide the probed token limit by your p99 tokens per minute, subtract headroom for retries, and treat the result as the maximum share of traffic that may go to the paid key today. If it is above 1, you can cut over fully; if it is 0.3, you are doing this in stages and now you know it before customers do.
- Route that share by proportion of requests, not by service. Keep the free key live and serving the remainder.
- Watch three numbers for 48 hours: the 429 rate on the paid path, p95 latency on both paths, and spend per hour extrapolated to a month. The third is the one nobody instruments and the one that ends badly.
- Re-probe before each ramp. When spend should have graduated the tier, confirm it from the headers rather than from the tier table, then raise the share and repeat step 7.
- Retire the free key last, after a full traffic cycle — including whatever your weekly and month-end peaks are — has run entirely on paid.
The rollback trigger, defined first
Write this down before step 6, because a trigger invented during an incident is a negotiation. A usable trigger is a threshold, a window and an action, all three of which are numbers or names rather than judgements: for example, a 429 rate above 1% of requests on the paid path sustained for ten minutes, or an hourly spend that extrapolates above the monthly budget, sets the paid share back to its previous value automatically.
Automatic is the operative word. A rollback that requires a human to agree that things are bad enough is a rollback that happens forty minutes late, and the ten-minute window exists precisely so that a brief spike does not trip it. Pair it with a standing spend alert as described in migrating a cost ceiling alert so the budget question survives past the cutover, and with per-request costing as in working out cost per request so the extrapolation is grounded in something real.