GEMINI LABJP
NANOLITE — Nano Banana 2 Lite is here: Google's fastest and most cost-efficient Gemini Image model, made for running lightweight image generation cheaplyOMNIFLASH — Gemini Omni Flash is in public preview, a natively multimodal model that lets enterprises and developers build custom, dynamic video workflowsAGENTS — Managed Agents expand with background: true for async server-side runs and polling, remote MCP server integration, and refreshing credentials across interactionsMEMORY — The Memory Bank IngestEvents API is generally available, decoupling event ingestion from memory generation so you can stream content continuouslyTHROUGHPUT — Provisioned Throughput now lets you submit up to seven pending orders for the same model and regionDEPRECATE — Image generation models shut down on August 17, and the Grok 4.1 family on the Gemini Enterprise Agent Platform on August 20NANOLITE — Nano Banana 2 Lite is here: Google's fastest and most cost-efficient Gemini Image model, made for running lightweight image generation cheaplyOMNIFLASH — Gemini Omni Flash is in public preview, a natively multimodal model that lets enterprises and developers build custom, dynamic video workflowsAGENTS — Managed Agents expand with background: true for async server-side runs and polling, remote MCP server integration, and refreshing credentials across interactionsMEMORY — The Memory Bank IngestEvents API is generally available, decoupling event ingestion from memory generation so you can stream content continuouslyTHROUGHPUT — Provisioned Throughput now lets you submit up to seven pending orders for the same model and regionDEPRECATE — Image generation models shut down on August 17, and the Grok 4.1 family on the Gemini Enterprise Agent Platform on August 20
Articles/API / SDK
API / SDK/2026-05-01Intermediate

Migrating Working Code from AI Studio to Vertex AI: A Solo Developer's Hands-On Walkthrough

What actually changes when you move existing Gemini API code from AI Studio to Vertex AI. Includes side-by-side code diffs for SDK init, auth, and response parsing.

gemini-api277vertex-ai8migration6google-cloud6authentication4

"My AI Studio free tier ran out faster than I expected" or "I want all my Gemini billing to live inside the same GCP project as the rest of my stack" — those were the very practical reasons that finally pushed me to migrate. I had read plenty of comparison articles beforehand, but once I started rewriting the actual code, I was surprised by how many small but real differences I ran into. This article is the working log I kept during that migration, with concrete code diffs. I hope it saves you a few hours.

If you are still deciding which side to start on, the Gemini API vs Vertex AI comparison guide walks through the higher-level trade-offs.

The trigger was billing clarity, not the free tier

Honestly, the free tier ending was not what made me move. The real driver was that when you switch to Vertex AI, your Gemini usage rolls up into the regular billing of your existing GCP project. Anyone running a small business on the side will tell you that having one consolidated invoice instead of two separate billing accounts is a quiet but meaningful win. My accountant noticed the difference within the first month — Gemini line items now appear in the same monthly export as Cloud Run, Cloud Build and Cloud Storage, which means I no longer have to manually merge two billing CSVs at tax time.

The second motivator was IAM. AI Studio API keys can be rotated, but they are not great when you want fine-grained separation like "only this service account can call Gemini in production, while developer machines use a different identity." On Vertex AI, you grant or revoke roles/aiplatform.user per service account, which is much more reassuring once you start running real workloads. If a developer laptop is compromised, you revoke that single service account binding rather than rotating a key that might be referenced in a dozen places.

There is also a softer benefit: VPC Service Controls, audit logs in Cloud Audit, and Cloud KMS-backed customer-managed encryption keys all start to make sense once you are in Vertex AI land. Most solo developers do not need any of those on day one, but knowing they are available "without changing platforms" reduces future migration anxiety.

Same SDK, completely different initialization

The biggest discovery was that the google-genai SDK works on both sides — but the arguments to the client constructor are entirely different. On AI Studio, the code is essentially one line of API key setup.

# Before: AI Studio
from google import genai
 
client = genai.Client(api_key="YOUR_API_KEY")
 
response = client.models.generate_content(
    model="gemini-2.5-pro",
    contents="Say hello in English.",
)
print(response.text)
# Expected output: Hello!

When you move to Vertex AI, you flip the vertexai=True flag, drop the API key entirely, and pass the GCP project and a region instead.

# After: Vertex AI
from google import genai
 
client = genai.Client(
    vertexai=True,
    project="your-gcp-project-id",  # GCP project ID
    location="asia-northeast1",      # Tokyo region in my case
)
 
response = client.models.generate_content(
    model="gemini-2.5-pro",
    contents="Say hello in English.",
)
print(response.text)
# Expected output: Hello!

The key thing here is that you do not pass api_key. Vertex AI authenticates through Application Default Credentials (ADC), which means credentials never live in your source. Locally you run gcloud auth application-default login once, and on servers you point the GOOGLE_APPLICATION_CREDENTIALS environment variable at a service account JSON file.

Picking asia-northeast1 (Tokyo) noticeably improved latency for my Japanese users — roughly 10–20% faster first-token times by feel. AI Studio does not let you pin a region, so this is a Vertex AI-only lever. If most of your traffic is North American, us-central1 is a sensible default; for European users, europe-west4 keeps the round-trip short.

The single environment variable that cost me a full day

The single most painful part of the whole migration was a permission error. Locally everything worked because gcloud auth application-default login was set up; the moment I deployed to Cloud Run, every call returned 403 Permission denied.

The cause turned out to be embarrassingly simple: the Cloud Run service account was missing roles/aiplatform.user. The frustrating part was that the error message did not mention Vertex AI at all — it just said Permission denied on resource project ..., which sent me on a long detour debugging API keys that did not exist.

Here is the recovery I should have gone to immediately:

# Find the service account attached to the Cloud Run service
gcloud run services describe my-app \
  --region asia-northeast1 \
  --format="value(spec.template.spec.serviceAccountName)"
 
# Grant it Vertex AI access
gcloud projects add-iam-policy-binding your-gcp-project-id \
  --member="serviceAccount:<output-from-the-command-above>" \
  --role="roles/aiplatform.user"

There is one more trap: even with the right role, Vertex AI calls will fail if the API itself is not enabled on the project. Run gcloud services enable aiplatform.googleapis.com first. If you are coming from a project that has only been used for static hosting or simple Cloud Functions, this enablement step is easy to miss.

A small piece of advice from this experience: when you migrate, write a one-line health check endpoint that calls Gemini with a hardcoded prompt and returns the result. Hit it in your deployment pipeline. That will surface the missing role or disabled API on the very first deploy, instead of when a real user triggers a feature. The full set of auth-error patterns I had to work through is covered in Vertex AI Gemini auth error fixes.

Structured output: a subtle response-shape difference

Most response shapes are compatible across the two surfaces, but I hit one small gotcha around structured output (response_schema). On AI Studio, my prompt returned a top-level JSON array as expected. After migrating, certain models on Vertex AI started wrapping the same payload as {"items": [...]}, which broke my parser.

I caught it from production logs, not unit tests. The fix was to make the parsing layer accept both shapes:

import json
 
raw = response.text
data = json.loads(raw)
 
def coerce_list(payload):
    """Accept either a top-level list, or a dict with 'items' / 'results' / 'data'."""
    if isinstance(payload, list):
        return payload
    if isinstance(payload, dict):
        for key in ("items", "results", "data"):
            if isinstance(payload.get(key), list):
                return payload[key]
    raise ValueError(f"unsupported response shape: {type(payload)}")
 
records = coerce_list(data)
print(f"records: {len(records)}")

I would recommend running both surfaces in parallel for at least a few days after the cutover, dumping responses on both sides and diffing. The differences are small enough that you will not notice them in unit tests, but they show up immediately in real traffic. If you can spare the cost, route 5–10% of production traffic to the new endpoint with feature flags first; that is usually enough to surface any shape divergence within 24 hours.

Three things I am genuinely glad about a month later

After running on Vertex AI for about a month, here are the three wins that have actually mattered to me.

First, billing visibility is dramatically better. Gemini usage now appears alongside Cloud Run, Cloud Storage, and the rest of my GCP bill in one place, which makes monthly cost reviews trivial. Setting up a Cloud Billing budget alert that fires at 50% of expected monthly spend is a one-click affair, and that single guardrail has paid for the migration effort already.

Second, production and staging now use separate service accounts. The chance of a leaked production key is effectively zero, and accidental "oops, I just ran a dev script against prod" is now blocked by IAM rather than by hope. I also feel more comfortable inviting a contractor to a staging-only role without worrying they could read production data.

Third, first-token latency is a touch faster thanks to the Tokyo region. If you are squeezing latency, the Gemini API latency optimization guide has more levers to pull.


On paper, this migration is just rewriting a constructor. In practice — once you account for IAM, API enablement, regional choice, and response-shape differences — it is a half-day to one-day project for a small codebase. My suggestion: get a vertexai=True "hello world" working locally first, then move production workloads over one endpoint at a time. I hope the diffs above give you a head start.

Share

Thank You for Reading

Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

API / SDK2026-04-09
Vertex AI + Gemini Authentication Error Fix: Service Account & ADC Troubleshooting Guide
Struggling with Vertex AI Gemini authentication errors? This guide covers the most common causes—service account misconfiguration and ADC setup issues—with step-by-step solutions to get you unblocked fast.
API / SDK2026-07-18
The Same gemini-flash-latest Pointed to Different Models in Different Regions
Alias resolution rolls out region by region. Send the same gemini-flash-latest to Tokyo and us-central1 and, for a few days, different models answer. Here is why that quietly invalidates your comparisons, and how a one-shot probe catches it.
API / SDK2026-06-30
Folding Scattered Call Sites Into One Front Door: Migrating to the Interactions API for Automation
With the Interactions API now generally available, Gemini's calls can settle behind a single entry point. Here is a migration design for folding scattered call sites — generateContent, Batch, and homegrown agent loops — into one front door without breaking anything, complete with a working adapter layer.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →