I already had Google Cloud billing.
I did not want another API key, another quota surface, and another place to explain to my team why the coding tool bill was showing up somewhere else.
The annoying part was that Claude Code speaks Anthropic's Messages API, while the budget I wanted to use was sitting in Vertex AI.
So I wired the two together through a local gateway.
The mismatch
If you try to connect Claude Code to Vertex AI directly, the shapes do not line up cleanly.
Claude Code expects this kind of world:
POST /v1/messages- Anthropic headers
- Anthropic content blocks
- Anthropic streaming semantics
Vertex AI is a different world.
For Claude models on Vertex, the request goes to a publisher endpoint like:
.../publishers/anthropic/models/claude-sonnet-4-6:rawPredict
For Gemini models on Vertex, it goes to:
.../publishers/google/models/gemini-2.5-pro:generateContent
That meant I needed something in the middle that could accept Claude Code exactly as-is, then decide how to talk to Vertex on the other side.
What I used
I used CliGate, a local gateway that already sits between Claude Code, Codex CLI, Gemini CLI, and multiple upstream providers.
The useful part for this setup is that Vertex AI is just another API-key-backed upstream inside the same routing layer.
In this project, a Vertex key stores three things that matter:
type: vertex-aiprojectIdlocation
And the apiKey field can hold the full Google service account JSON.
The 5-minute setup
1. Start the gateway
npx cligate@latest start
Default dashboard:
http://localhost:8081
2. Add Vertex AI as an API key provider
You can do it in the dashboard, or post it directly:
curl -X POST http://localhost:8081/api/keys \
-H "Content-Type: application/json" \
-d '{
"type": "vertex-ai",
"name": "vertex-work",
"apiKey": "{\"type\":\"service_account\",\"project_id\":\"my-project\", ... }",
"projectId": "my-project",
"location": "us-central1"
}'
That apiKey value looks strange because it is not a normal API key string. For Vertex AI in CliGate, it can be the full service account JSON blob.
3. Point Claude Code at localhost
export ANTHROPIC_BASE_URL=http://localhost:8081
export ANTHROPIC_API_KEY=any-key
claude
Now Claude Code still thinks it is talking to an Anthropic-compatible server.
It is.
That server just happens to decide that the real upstream should be Vertex AI.
The part that made this worth doing
The nice thing about this setup is that I did not need to patch Claude Code itself.
Claude Code keeps sending Anthropic-style requests to:
POST /v1/messages
CliGate inspects the model and the selected provider, then takes one of two paths:
Claude Code
-> /v1/messages
-> CliGate
-> Vertex Claude rawPredict
or:
Claude Code
-> /v1/messages
-> CliGate
-> Anthropic-to-Gemini bridge on Vertex
That second path is the interesting one. The provider code treats Vertex differently depending on the model family:
-
claude-*models keep the Anthropic-style path on Vertex -
gemini-*models are translated into GeminigenerateContent
So one local entry point can still switch between Claude-on-Vertex and Gemini-on-Vertex without Claude Code learning a new protocol.
The subtle detail I was glad the project handled
There is one implementation detail here that usually gets hand-waved away in blog posts: location.
Vertex AI does not treat every model family the same way.
In CliGate's provider implementation, Gemini can use the Google publisher endpoints, while Claude models on Vertex still need the Anthropic publisher path, and regional handling matters. That is why the stored config keeps both:
projectIdlocation
For me, this was the difference between "works in a diagram" and "works at 11 PM when I just want the CLI to answer."
Why I preferred this over another direct integration
I could have built a one-off wrapper just for Claude Code + Vertex AI.
That would have solved today's problem and created tomorrow's mess.
The local gateway approach was better because the same control plane already handles:
- Claude Code
- Codex CLI
- Gemini CLI
- API key routing
- account pools
- usage and pricing views
So moving Claude Code onto GCP credits did not create another isolated config path. It became one routing rule inside the system I was already using.
What this setup buys me in practice
After the proxy is in place, the workflow gets boring in the best way:
- Claude Code still points to
http://localhost:8081 - Vertex AI holds the real billing surface
- the dashboard keeps request logs, usage, and provider visibility
- I can swap models or providers later without rewiring the CLI again
That last part matters more than it sounds. I do not want my tools to know where the money comes from. I want them to know where the gateway is.
If you already have GCP credits, this is the easiest way I found
If your team already runs on Google Cloud, making Claude Code consume Vertex AI instead of another standalone Anthropic key is mostly a protocol-translation problem.
Once that translation sits on localhost, the rest of the setup becomes very small.
Repo:
I'm curious how other people are handling this split right now: are you connecting Claude Code straight to Vertex, or hiding the provider switch behind one local gateway too?
Top comments (0)