# Serp API 429 quota exceeded on competitor news calls

## TL;DR
A 429 from a SERP API means the competitor-news polling burned the monthly search quota, which is the metered product working as designed. The fix is query economy: fewer, broader queries with site filters instead of many narrow ones, aggressive caching of result pages, and scheduling polls rather than running them on demand. When the intel need genuinely exceeds the quota, upgrade the plan; SERP data is metered everywhere.

## The error
```text
HTTP 429 Too Many Requests
{"error": "Quota exceeded for this billing period."}
```

## When this helps
- SERP API calls return 429 quota exceeded
- competitor news polling exhausts the search budget
- designing query economy for metered search APIs
- deciding whether to upgrade a SERP plan

## When it doesn't
- the error is 401; that is the API key
- you need real-time SERP data; caching and scheduling trade freshness for quota
- the quota reset is near; waiting may be enough

## Works with
SerpApi and similar metered SERP providers as of 2026. Quota mechanics are provider-side.

## Steps
### 1. Check quota usage on the provider dashboard or API
```bash
K="api_key"
curl -s "https://serpapi.com/account?${K}=${SERPAPI_KEY}" -o acct.json -w "HTTP %{http_code}\n"
python3 -c "import json; d=json.load(open("acct.json")); print(str(d)[:200])"
```
Expected: The quota state. Knowing searches used versus plan total tells you whether to optimize or upgrade.

### 2. Consolidate narrow queries into broad ones
```python
import requests, os
r = requests.get("https://serpapi.com/search", params={"q": "[competitor] news", "tbm": "nws", "api_key": os.environ["SERPAPI_KEY"]}, timeout=30)
print(r.status_code, len(r.json().get("news_results", [])))
```
Expected: One call with many results. A single broad query with the news vertical beats ten narrow ones.

### 3. Cache result pages by query hash
```python
import hashlib, json
q = "[competitor] news"
fp = hashlib.md5(q.encode()).hexdigest()
print("cache:", "cache/serp_" + fp + ".json, ttl 24h")
print("competitor news barely changes intraday; cache aggressively")
```
Expected: A cache key with a daily TTL. Repeat briefings reuse yesterday's results instead of spending new searches.

### 4. Schedule polls instead of on-demand searches
```python
import json
plan = {"competitor_news_poll": "daily 06:00 UTC", "max_queries_per_poll": 10}
open("serp_plan.json", "w").write(json.dumps(plan, indent=2))
print("bounded daily polling replaces ad-hoc searches")
```
Expected: A poll plan file. Bounded scheduled polling keeps usage predictable and inside quota.

## Other ways people phrase this
### serpapi 429 quota exceeded
Monthly quota spent. Consolidate queries, cache, schedule.

### serp api quota billing period
Quotas reset per billing period. The dashboard shows the reset date.

### competitor news search quota failed
On-demand competitor searches are the burn source. Schedule them.

## Why it happens
SERP APIs meter every search because each one costs the provider a real search engine query. Briefing agents that search per competitor per hour exhaust monthly quotas fast. The 429 is the meter; the fix is fewer searches through consolidation, caching, and scheduling, or a bigger plan.

## Edge cases
- News vertical searches cost the same as web searches; use tbm=nws to get news without extra calls.
- Location parameters multiply quota burn; default to no location unless needed.
- Some providers offer cheaper async batch search; use it for bulk competitor sweeps.
- Track quota per agent when several share one key.

## Provenance

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