cd /news/ai-tools/gemini-api-cli-official-guide-part-2… · home › topics › ai-tools › article
[ARTICLE · art-169802] src=dev.to ↗ pub= topic=ai-tools verified=true sentiment=· neutral

Gemini API CLI Official Guide (Part 2): Keyless ADC, OAuth Scope Troubleshooting & User Tier Economics

A developer published a second-part guide to the Gemini API CLI (v0.4.6) documenting keyless OAuth via Application Default Credentials, where short-lived bearer tokens are minted from existing identity and paired with an explicit x-goog-user-project header to attribute quota and billing to a designated GCP project. The guide also troubleshoots HTTP 403 OAuth scope errors observed at runtime and clarifies that Google AI Pro/Ultra consumer subscriptions do not make Developer API calls free, since the API bills against a GCP project rather than a Google Account. Source inspection of the release found no native ADC discovery or token auto-refresh, so credentials must be supplied via GEMINI_ACCESS_TOKEN or --access-token and injected through environment variables rather than command-line flags.

by read13 min views3 publishedNov 15, 2026

Verified baseline: 2026-11-14 (Asia/Taipei, UTC+8) on macOS Apple Silicon (ts-m1), gemini-api 0.4.6, plus our 2026-11-15 live execution on gde-jimmyliao-redeem summarized in Section 10. Runtime HTTP 403 scope diagnostics and Paid Tier live execution are observed tool outputs; pricing, quota, and consumer migration boundaries come directly from official Google documentation.

📚 Series Navigation

Part 1 introduced the standalone multimodal CLI. Once AI agents operate across laptops, containers, and CI runners, two architectural questions immediately arise:

AIza* API keys on disk while attributing every request cleanly to a designated GCP project? Keyless OAuth answers the first question by minting short-lived bearer access tokens from your existing identity and attaching an explicit quota project header. Importantly, keyless does not mean credential-less: local ADC still stores an underlying refresh credential, short-lived bearer tokens remain secrets, and OAuth never bypasses IAM roles, API enablement, or Cloud Billing.

A second widespread misconception is "I pay for Google AI Pro / Ultra, so logging in with that Google account makes Gemini API calls free." Your Google Account is an identity; Google One / Antigravity is a consumer subscription pool; the Developer API (generativelanguage.googleapis.com) belongs to a GCP project / Cloud Billing pool. Even promotional or GDE credits must be checked per billing account, eligible SKU, and expiration date.

Primary evidence lives in experiments/03-part2-keyless-adc/results.md, experiments/04-tier-limits-and-errors/tier-research.md, and gde-feedback/upstream-issues-draft.md.

v0.4.6 Implements Keyless OAuth To verify exact release behavior rather than relying on an earlier snapshot, we inspected four source files directly in tag v0.4.6:

Source File ( v0.4.6 ) Observed Responsibility & Limitation
internal/cli/auth.go runAuthLoginCmd persists an existing API key or access-token string viaconfig.StoreSecret /config.SaveConfig ; it never launches a browser OAuth flow or acquires refresh credentials.
internal/config/config.go SecurityConfig.AccessToken maps to YAMLaccess_token across flags, env vars, OS Keychain, and~/.config/gemini-api/config.yaml (notconfig.json ).
internal/cli/configure.go runConfigureCmd writes credentials and global parameters includinguser-project locally.
internal/client/client.go BuildGlobalSecurity selects API key or access token;buildGlobalOptions mapsuser-project tosdk.WithUserProject . No native ADC discovery or token auto-refresh source exists here yet.

GEMINI_ACCESS_TOKEN / --access-token supplies the bearer credential (who is calling), whereas --user-project sets the x-goog-user-project header (which GCP project owns quota & billing). Never pass tokens on the command line where process inspection (ps) can read them—inject via subprocess environment variables instead.

General credential precedence follows flag -> environment -> Keychain -> config, and the Go client picks a complete explicit credential over fallback paths. Therefore our wrapper unsets inherited GEMINI_API_KEY variables before injecting a fresh ADC access token. Merely setting GOOGLE_APPLICATION_CREDENTIALS does not make v0.4.6 discover or refresh ADC natively. See OAuth quickstart and API CLI docs.

ts-m1: HTTP 403 ACCESS_TOKEN_SCOPE_INSUFFICIENT On ts-m1, the active gcloud/ADC identity sjliao@gmail.com (authorized_user, quota_project_id: leapcore-dev) held cloud-platform, sqlservice.login, userinfo.email, email, and openid—but lacked https://www.googleapis.com/auth/generative-language:

Live Call Tested Process Exit HTTP / Error Reason Engineering Conclusion
models list (no project header) 3 403 ACCESS_TOKEN_SCOPE_INSUFFICIENT Default Cloud ADC token lacks generative-language scope
models list --user-project gdg-kc-465418 3 Same Switching --user-project cannot fix a token scope gap
generate withgdg-kc-465418 3 403 PERMISSION_DENIED / same reason Zero inference response or usage incurred
Wrapper generate withleapcore-dev 3 Same billingEnabled=true cannot compensate for missing OAuth scope
gcloud auth application-default login --scopes="https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/generative-language,https://www.googleapis.com/auth/generative-language.retriever,https://www.googleapis.com/auth/userinfo.email,openid"

Note why all of those scopes appear: the official /docs/oauth guide demonstrates cloud-platform + generative-language.retriever, whereas live generate calls explicitly requested https://www.googleapis.com/auth/generative-language. If Google's default gcloud client blocks sensitive scopes for your workspace or account, create a Desktop app OAuth Client in Google Cloud Console (configuring your consent screen / test users per OAuth quickstart), store client_secret.json outside the repo, and pass --client-id-file <path>:

./bin/gapi-adc --print-reauth
./bin/gapi-adc --reauth --client-id-file /secure/path/outside/repo/client_secret.json

Scope is not IAM: the target quota project must also enable generativelanguage.googleapis.com and grant the caller serviceusage.services.use.

bin/gapi-adc bin/gapi-adc calls gcloud auth application-default print-access-token right before live invocations, captures the short-lived token in memory, exports GEMINI_ACCESS_TOKEN, and exec s gemini-api without ever persisting tokens to disk or CLI config. (Long-running polling loops past token TTL still require a refreshing client or periodic re-invocation.)

Because gemini-api v0.4.6 rejects -p (exit 2: unknown shorthand flag), the wrapper maps -p to --user-project. Project resolution order is: explicit --user-project/-p -> GAPI_USER_PROJECT -> scalar user_project/ user-project in ~/.config/gemini-api/config.yaml -> gcloud core/project.

./bin/gapi-adc --help
./bin/gapi-adc --print-auth-status
./bin/gapi-adc --print-reauth
./bin/gapi-adc generate "hi" --dry-run
./bin/gapi-adc generate "hi" --user-project gdg-kc-465418 --dry-run
./bin/gapi-adc generate "hi" -p gdg-kc-465418 --dry-run

--print-auth-status safely inspects ADC type and quota_project_id (redacting refresh tokens/secrets), queries Google's tokeninfo endpoint via stdin so the token never appears in curl argv:

curl -sS -m 15 -X POST -d @- https://oauth2.googleapis.com/tokeninfo

It reports PRESENT/ MISSING for both generative-language scopes, expires_in, and a masked email, returning exit 0 even when scopes are missing (diagnostic mode—never treat exit 0 of --print-auth-status as CI auth proof). Normal execution exits 3 on token failure, 2 if no project is resolved, 127 on missing binaries, or passes through gemini-api's exit code. --dry-run skips gcloud network calls completely.

sjliao@gmail.com) Inspecting the actual GCP projects under sjliao@gmail.com illustrates why billingEnabled, OPEN billing accounts, and API enablement must be verified separately:

Project ID(s) billingEnabled / Billing Account generativelanguage.googleapis.com Status
jimmyliao-dev ,gcp-trial-423705 ,gen-lang-client-0954497962 ,bwai2026-jimmyliao ,bwai2026-jimmyliao-ws ,io26-496910 false (bwai2026-jimmyliao retains historical linkage) Listed enabled -> Free Tier candidates
gdg-kc-465418 true (0154CA-AF3D86-D7DE34 ) Enabled -> generate blocked earlier by ADC scope 403
gde-jimmyliao-redeem ,ai-course-2026 ,helloworld2026-jimmyliao true (GDE_Redeem /01FE1D-589853-A0397F ) Enabled -> Paid / Redeem candidates ( gde-jimmyliao-redeem verified live on2026-11-15 )
leapcore-dev true (sameGDE_Redeem account) Raw gcloud services list --enabled returnedListed 0 items. (contradicts earlier assumption)
jimmyliao-ai-proj ,bwai2026 ,gdg-ws-0523 ,gdgyep2026 false Not listed in enabled services

Billing account checks also confirmed both gde-aipro-jimmy (013712-FCD5CC-34996C) and GDE_Redeem (01FE1D-589853-A0397F) are OPEN=True, and showed why cloudbilling.googleapis.com on a consumer project sometimes needs --billing-project=jimmyliao-dev separately from --user-project.

GEMINI_ACCESS_TOKEN + --user-project <unbilled-project> with generativelanguage.googleapis.com enabled and serviceusage.services.use IAM). See UTC-7) and UTC-8, active in November). Current general numeric quotas live behind the authenticated 200 RPM / 50,000 TPM / 10,000 RPD, while gemini-omni-1.2-flash has | Capability | Free vs. Paid Boundary (per Official Docs) | |---|---| | Text / Multimodal Input Analysis | Selected models offer Free Tier; free multimodal input does not imply free image/videooutput | | Image Generation | gemini-3.1-flash-image (Nano Banana 2 ) &gemini-nano-banana-2.1 list API Free Tier asNot available (AI Studio UI previews do not grant programmatic Free quota) | | Text-to-Speech (TTS) | gemini-3.8-flash-tts ,3.1 Flash TTS Preview , and2.5 Flash Preview TTS allow FreeStandard ;Batch has no Free Tier | | Video Generation | Current gemini-omni-1.2-flash ispaid-only ; legacyVeo 3.1 Preview shut down on**2026-10-22** (Veo GA lives in Enterprise Agent Platform, a separate surface) | | Search Grounding | Not strictly paid-only: 2.5 Flash Standard offers500 RPD free (shared with Flash-Lite); Gemini 3+ models include5,000 monthly free Search requests then$14 / 1,000 , capped at1,500 RPD per project by default | | Batch / Context Caching | No Free Tier for explicit context caching or Batch on checked models ( 2.5 Flash ); paid Batch offers independent enqueue quotas | | Managed / Deep Research Agents | Billed at underlying token rates + tool fees—not bundled in Google One subscriptions |

store=false disables state persistence (incompatible with background execution) but does not override Unpaid terms. See | Tier | Official Qualification Criteria | Monthly Spend Cap per Billing Account | |---|---|---| | Tier 1 | Linked active Cloud Billing account; Prepay requires minimum $5 USD top-up (eligible accounts may use Postpay) | $250 USD | | Tier 2 | $100 USD paid spend +3 days since first successful payment | $2,000 USD | | Tier 3 | $1,000 USD paid spend +30 days since first successful payment | $20,000 – $100,000+ USD (varies by account) |

Cumulative spend aggregates Google Cloud services spend across the linked billing account (upgrades trigger automatically after review: near-instant Free -> Tier 1, ~10 min afterward). Paid Tier 1 also enforces a rolling 10-minute spend rate limit of $10 USD (Tier 2/3: $200 USD), which protects against runaway retries (429).

Regarding GDE / Cloud Credits & Prepay: promotional credits only offset eligible Gemini API SKUs on the matching billing account before expiration. New $300 GCP Welcome Free Trial credits (since March 2026) exclude Gemini API except grandfathered balances within 90 days, and promotional credits alone may not satisfy paid-spend tier upgrade criteria (billing). Prompts sent through active-billing Paid projects are not used to improve Google products (Paid Services terms).

Modality Pricing Notes from Docs ( 2026-11-14/15 )
Text / Multimodal Understanding 2.5 Flash :$0.30 / 1M text/image/video input,$1 / 1M audio input,$2.50 / 1M output (deprecated; auto-routes to3.5 Flash-Lite on2026-12-01 ). Currentgemini-3.8-flash :$0.75 / 1M in,$3.75 / 1M out+thinking (through2026-12-31 ).
image (3.1 Flash /Nano Banana 2.1 ) Legacy 3.1 Flash Image :$0.067 (1K); current defaultgemini-nano-banana-2.1 :$1.50 / 1M in,$7.50 / 1M text+thinking out,$30 / 1M image out (1,120 tokens = $0.0336 per 1K image).
video (gemini-omni-1.2-flash ) $1.50 / 1M in,$9.00 / 1M text out,$12.00 / 1M video out (5,792 tokens/s @ 720p ≈$0.07/s ).
tts (gemini-3.8-flash-tts ) Through 2026-12-31 :$0.50 / 1M text input,$9.00 / 1M audio output (25 tokens/s ≈$0.00225 / 10s ); doubles starting2027-01-01 .
Deep Research / Managed Agents Underlying model input/output + reasoning-loop intermediate tokens + tool fees; managed sandbox compute currently $0 in preview.
Batch Tier Typically 50% off standard token rates on supported models (not applied to every tool/storage fee).

Effective 2026-06-18, individual Pro/Ultra subscriptions moved from gemini-cli to agy, featuring 5-hour rolling quotas bounded by weekly caps (and optional AI Credits overage inside Antigravity). Because those credits sit strictly inside the consumer/Antigravity pool, logging into gcloud with a Pro/Ultra account does not transfer free API quota to gemini-api.

The Golden Hybrid Pattern resolves this cleanly:

agy's subscription pool. image, video, music, tts) through gapi-adc to explicitly governed Free or Paid GCP projects. In planning our 15-second Reels pipeline:

jimmyliao-dev) generate storyboard, Standard single analyze QA), while omni-1.2-flash video remain gdg-kc-465418 / gde-jimmyliao-redeem)

Item Estimated Pre-Run Initial Planning Estimate & Constraint
3.1 Flash Image 1K (prior baseline) ~$0.067 / image × 3 ≈ $0.201 (v0.4.6 actually defaults togemini-nano-banana-2.1 )
Omni 1.2 Flash 720p video ~$0.07 / s × 15s = $1.05 + input tokens
3.8 Flash TTS $0.50 / 1M text in,$9.00 / 1M audio out
Strategy Safeguard Keyframe-First : inspect 9:16 image keyframes (~$0.05 ) manually before paying for video motion (~$0.35 / 5s ) to avoid tripping Tier 1's$10 rolling 10-minute cap

sample-part2-adc-tier-router.sh) Offline project-attribution router dry-run (add --check-live-token locally for live ADC scope diagnostics; source: examples/part2-keyless-adc-tiers/sample-part2-adc-tier-router.sh):

bash <<'PART_SAMPLE'
(
set +x
set -euo pipefail
cd examples/part2-keyless-adc-tiers
unset GEMINI_API_KEY GEMINI_ACCESS_TOKEN GEMINI_ENVIRONMENT_KEY GEMINI_USER_PROJECT
export GEMINI_NO_KEYRING=true
R=../reels-storyboard-pipeline
W=../../bin/gapi-adc
FREE_PROJECT_ID=${FREE_PROJECT_ID:-jimmyliao-dev}
PAID_PROJECT_ID=${PAID_PROJECT_ID:-gdg-kc-465418}
case "${1:-}" in
 '') echo 'Offline dry-run: skipping live token and network calls.' >&2 ;;
 --check-live-token) "$W" --print-auth-status </dev/null ;;
 *) echo 'Usage: sample-part2-adc-tier-router.sh [--check-live-token]' >&2; exit 2 ;;
esac
"$W" --print-reauth </dev/null
verify_project() {
 local project=$1
 shift
 "$W" --agent-mode "$@" -p "$project" --dry-run </dev/null |
 python3 -c 'import json,sys; d=json.load(sys.stdin); h=d["request"]["headers"]["X-Goog-User-Project"]; assert h == [sys.argv[1]], h; print(json.dumps(d,ensure_ascii=False)); print("Verified project header: "+sys.argv[1],file=sys.stderr)' "$project"
}
echo 'Stage A: Free candidate pre-production (requires online entitlement check separately)' >&2
verify_project "$FREE_PROJECT_ID" generate 'Plan a 3-scene 9:16 Reels storyboard' --stream=false
verify_project "$FREE_PROJECT_ID" tts 'Welcome to the Multimodal CLI' --out /tmp/part2-voice.wav
verify_project "$FREE_PROJECT_ID" analyze -i "$R/fixtures/scene1-keyframe.png" 'Inspect layout safe-zone'
echo 'Stage B: Paid render dry-run, Keyframe before Video' >&2
verify_project "$PAID_PROJECT_ID" image '9:16 tech Reels keyframe' --aspect-ratio 9:16 --image-size 1K --output-mime-type image/jpeg --out /tmp/part2-keyframe.jpg
verify_project "$PAID_PROJECT_ID" video 'Slow camera push on keyframe' --aspect-ratio 9:16 --resolution 720p -i "$R/fixtures/scene1-keyframe.png" --out /tmp/part2-video.mp4
echo 'Guardrail: Approve keyframe before video render to protect Tier 1 rolling 10-min $10 cap.' >&2
)
PART_SAMPLE
Persona Authentication Billing Pool Quota Reset Data Privacy Available Features Recommended CLI Combo
Free AI Studio key, or ADC token + unbilled --user-project Project Free quota (no Google One transfer) RPM/TPM; RPD 15:00 PDT / 16:00 PST (Taipei) Taiwan Unpaid terms allow product training & review; no confidential data Selected text/analyze/TTS/Grounding; image/video/Batch/cache restricted Any coding agent + gemini-api Free (select Free models)
Paid Tier 1–3 Key / auth key, or ADC token + billed --user-project Cloud Billing / Prepay / eligible GDE & Cloud credits Same RPD + rolling 10-min cap ($10/$200) & billing cap Paid prompts not used to train Google models; compliance logging applies Image, video, Managed Agents, Batch, caching per model unlock Coding agent + gemini-api ADC; pin CLI & project in CI
Pro / Ultra agy personal OAuth; separate key/ADC for API agy 5H + weekly pool (+ optional AI Credits); separate API pool agy 5H bounded by weekly cap; API per-project agy consumer terms vs. Free/Paid project API terms agy coding/Argon; offload specialized multimodal tasks to API Golden Hybrid :agy +gemini-api

Documented CLI exit codes: 0 success, 1 runtime/API error, 2 usage/validation, 3 auth/authorization. Always inspect both HTTP status and reason:

HTTP Status / Reason Next Action Evidence Type
403 ACCESS_TOKEN_SCOPE_INSUFFICIENT Run --print-auth-status , then re-authorize scopes (--reauth ); do not swap billing projects to fix scope Live local test
403 SERVICE_DISABLED Check which service & consumer project lacked enablement ( --billing-project vs--user-project ) Live Cloud Billing check
401 / other403 Inspect token expiration, IAM ( serviceusage.services.use ), API enablement, and project attribution Docs diagnostic
429 RESOURCE_EXHAUSTED Distinguish RPM/TPM/RPD, tool caps, and 10-min rolling spend limits; respect server retry backoff Docs research
402 Payment Required Check Prepay balance / billing account status across the entire linked billing account Docs research

Both issue drafts in gde-feedback/upstream-issues-draft.md await submission:

403 scope/IAM error messages in gemini-api-cli (applied upstream in Speakeasy spec/generator rather than patched in generated files).gemini-cli consumer migration documentation banners with internal quota tables. Official references verified during this research: OAuth quickstart, ADC troubleshooting, API CLI, API keys, Billing, Rate limits, Pricing, Terms, Interactions, Agents, Deep Research, Video, Antigravity plans, Antigravity install, Transition announcement.

2026-11-15): ADC Scope Guardrail & §6.5 Cost Proof Our 2026-11-15 00:20– 00:24 live run confirmed both Part 2 theses in practice:

$0.00)./bin/gapi-adc --agent-mode generate -p gde-jimmyliao-redeem with GEMINI_API_KEY unset deterministically exited 3 (--reauth—proving the wrapper fails loudly instead of silently billing the personal pool.$0.3527 / $0.5485 (~64%)9:16 keyframes (gemini-nano-banana-2.1), dual-speaker TTS ($0.0021), and QA ($0.0109) combined cost only ~$0.107 USD``$10 rolling 10-minute cap. See experiments/06-live-execution-and-cost-log/cost-and-call-ledger.md.

── more in #ai-tools 4 stories · sorted by recency
── more on @google 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/gemini-api-cli-offic…] indexed:0 read:13min 2026-11-15 · —