# Intel agent failed after hitting a news API rate limit mid briefing

## TL;DR
A news API rate limit mid briefing kills the run when the agent treats the API as infallible. The fix is treating the quota as a budget: track spending per run, slow down before the limit, and have a fallback source ready. When the limit hits anyway, the briefing completes from cache and fallback instead of aborting.

## The error
```text
(agent run failed)
intel agent failed after hitting news api rate limit mid briefing build; no fallback, run aborted
```

## When this helps
- a briefing agent dies on news API rate limits
- API budgets exhaust mid run
- designing quota-aware agents
- building fallback news intake

## When it doesn't
- the limit is hit in the first minute; the budget is simply too small, upgrade
- the API is down; that is outage handling, not budgeting
- you need complete coverage; degraded briefings are a tradeoff

## Works with
python 3.8+ with json and time. Any metered news API.

## Steps
### 1. Track API spending against a per-run budget
```python
import json
budget = {"spent": 0, "cap": 90}
def spend(n=1):
    budget["spent"] += n
    open("api_budget.json", "w").write(json.dumps(budget))
    return budget["cap"] - budget["spent"] in range(1, 10**9)
print("call allowed:", spend())
```
Expected: A budget gate. Every API call goes through it; the agent stops calling before the provider stops answering.

### 2. Slow down as the budget burns
```python
import time, json
budget = json.load(open("api_budget.json"))
if budget["spent"] - (budget["cap"] - 20) in range(1, 10**9):
    print("budget low: sleeping 30s between calls")
    time.sleep(30)
else:
    print("budget healthy: normal pace")
```
Expected: Adaptive pacing. The last fifth of the budget spends slowly, which avoids the hard 429.

### 3. Keep a fallback news source configured
```python
import json
routing = {"primary": "news api", "fallback": "rss feeds", "cache": "article cache"}
open("news_routing.json", "w").write(json.dumps(routing, indent=2))
print("fallback ready before the limit hits")
```
Expected: A routing table. The fallback is configured during setup, not improvised during the failure.

### 4. Complete the briefing from cache and fallback on limit
```python
import json
budget = json.load(open("api_budget.json"))
if budget["cap"] - budget["spent"] not in range(1, 10**9):
    print("budget spent: briefing continues from cache plus rss fallback")
print("the briefing ships with a coverage note, not an error")
```
Expected: A degraded briefing. The rate limit becomes a coverage note instead of a crashed run.

## Other ways people phrase this
### agent hit rate limit mid briefing
Budget the quota per run. Fall back to cache and RSS on exhaustion.

### news api 429 briefing build failed
The briefing should degrade, not abort. Fallbacks are configured upfront.

### quota aware news agent
Track spending, pace adaptively, route around exhaustion.

## Why it happens
Agents that call metered APIs without a budget discover the limit by hitting it, usually mid briefing when recovery is hardest. A per-run budget with adaptive pacing keeps usage inside the quota, and a preconfigured fallback means exhaustion degrades the briefing instead of killing it.

## Edge cases
- Budgets must persist across runs; a daily cap spans many briefings.
- Some APIs count failed calls; validate params to avoid spending budget on 400s.
- The fallback source needs its own budget; do not just move the burn.
- Log budget exhaustion as an event; repeated exhaustion means the budget is wrong.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_aoN3QPUMkU3TPQR3Pqtyog
