June's plan changes added a new option: AI Ultra at $100 per month, with roughly five times the usage limits of Pro.
As an indie developer who increasingly runs multiple Antigravity agents in parallel, I had a vague feeling of hitting the Pro ceiling toward the end of each month. But a vague feeling is a poor basis for an extra $80 a month. That money matters when you fund your own tools.
The exact quota numbers are not published anywhere. So the only way to know was to measure my own environment. I recorded fourteen days of consumption data before deciding anything.
This article documents the measurement harness, the numbers it produced, and the arithmetic that turns wait time into a break-even judgment.
The June 2026 Plan Lineup — What Is Public and What Is Not
First, the groundwork. As of June 2026, three options matter to individual developers.
- AI Pro ($20/month): the standard plan, including Antigravity agent usage
- AI Ultra ($100/month): the mid-tier added in June, with roughly 5x the Pro limits
- Top-tier Ultra ($200/month): reduced from $250; limits are effectively a non-issue
Here is the catch: the "5x" multiplier is public, but the Pro baseline itself is not. Is it request count? Token volume? Are models weighted differently? Nobody outside Google knows. This opacity is precisely what forces plan decisions to run on gut feeling.
What is not published can still be observed from the outside. Agent execution history lives in local logs. That became my starting point.
Building a Small Measurement Harness
The first thing I built was a script that aggregates agent session logs by day.
One sentence on what problem this code solves: it records, in a consistent daily format, when I ran agents, at what parallelism, and at what moment I hit a rate limit.
Antigravity stores per-session execution history as JSONL files on the local machine. The location varies by setup, so the path is passed as an argument.
#!/usr/bin/env python3
"""agent_quota_tracker.py — daily aggregation of agent execution logs"""
import json
import sys
import re
from pathlib import Path
from collections import defaultdict
from datetime import datetime
# Patterns that indicate a rate limit (extend as you observe new ones)
RATE_LIMIT_PATTERNS = [
re.compile(r"rate.?limit", re.IGNORECASE),
re.compile(r"quota.+exceeded", re.IGNORECASE),
re.compile(r"resource.?exhausted", re.IGNORECASE),
]
def is_rate_limited(text: str) -> bool:
return any(p.search(text) for p in RATE_LIMIT_PATTERNS)
def collect(log_dir: Path) -> dict:
daily = defaultdict(lambda: {
"sessions": 0,
"agent_runs": 0,
"max_parallel": 0,
"rate_limit_hits": [],
})
for jsonl in sorted(log_dir.rglob("*.jsonl")):
active = [] # (start, end) pairs for estimating parallelism
for line in jsonl.read_text(encoding="utf-8").splitlines():
try:
ev = json.loads(line)
except json.JSONDecodeError:
continue
ts = ev.get("timestamp", "")
day = ts[:10] if ts else "unknown"
kind = ev.get("type", "")
if kind == "session_start":
daily[day]["sessions"] += 1
elif kind == "agent_run":
daily[day]["agent_runs"] += 1
start = ev.get("started_at", ts)
end = ev.get("ended_at", ts)
active.append((start, end))
elif kind == "error" and is_rate_limited(str(ev.get("message", ""))):
daily[day]["rate_limit_hits"].append(ts[11:16])
# Treat the max number of overlapping runs as the day's parallelism
for day, stats in daily.items():
overlaps = [
sum(1 for s2, e2 in active if s2 <= s1 < e2)
for s1, _ in active
]
if overlaps:
stats["max_parallel"] = max(stats["max_parallel"], max(overlaps))
return daily
def main():
if len(sys.argv) < 2:
print("usage: agent_quota_tracker.py <log_dir>")
sys.exit(1)
daily = collect(Path(sys.argv[1]))
for day in sorted(daily):
s = daily[day]
hits = ",".join(s["rate_limit_hits"]) or "-"
print(f"{day} runs={s['agent_runs']:3d} "
f"parallel_max={s['max_parallel']} limited_at={hits}")
if __name__ == "__main__":
main()Two design decisions are worth explaining.
First, rate-limit detection works by pattern-matching error messages. Since the internal quota mechanics are invisible, the moment you hit the wall is the only fact observable from the outside. I built around that fact.
Second, parallelism is estimated from overlapping execution windows rather than counted directly. As you will see below, consumption pace correlated more strongly with parallelism than with raw run count.
Register the script with cron or Antigravity 2.0's scheduled execution, run it nightly, and the record accumulates on its own.