Skip to main content
RapidDev - Software Development Agency
v0-integrationsNext.js API Route

How to Integrate Google Fit with V0

To integrate Google Fit with V0 by Vercel, use the Google Fitness REST API through a Next.js API route with OAuth 2.0 authentication. V0 generates the health dashboard UI; your server-side route authenticates with Google OAuth and fetches activity, step count, sleep, and nutrition data. Display health metrics in charts and summaries without exposing user OAuth tokens to the browser.

What you'll learn

  • How to set up Google OAuth 2.0 for the Google Fitness API in a Next.js app
  • How to fetch step count, activity, and sleep data via a Next.js API route
  • How to display health metrics in a V0-generated dashboard with charts
  • How to handle Google OAuth token refresh for uninterrupted health data access
  • How to request only the Google Fit scopes your app actually needs
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate16 min read30 minutesOtherApril 2026RapidDev Engineering Team
TL;DR

To integrate Google Fit with V0 by Vercel, use the Google Fitness REST API through a Next.js API route with OAuth 2.0 authentication. V0 generates the health dashboard UI; your server-side route authenticates with Google OAuth and fetches activity, step count, sleep, and nutrition data. Display health metrics in charts and summaries without exposing user OAuth tokens to the browser.

Build a Google Fit Health Dashboard with V0 and the Google Fitness REST API

Google Fit aggregates health and activity data from Android phones, Wear OS watches, and connected third-party fitness apps — step counts, active minutes, sleep sessions, heart rate, and nutritional data. The Google Fitness REST API gives your V0-generated Next.js app access to this aggregated health data through a standard OAuth 2.0 flow. Users authorize your app once, and you can then read their historical and current fitness data.

The integration pattern for V0 developers follows the OAuth + API route approach: V0 generates the dashboard UI with activity charts, step count cards, sleep duration displays, and trend graphs. A Next.js API route handles Google OAuth authentication and data fetching server-side. The user's Google OAuth token is stored securely (in a server-side session or database) and never exposed to the browser — your frontend components only receive the processed health data returned by your API routes.

An important context: Google deprecated the Google Fit Android app in 2024 and the website in early 2025, recommending users migrate to Google Health Connect on Android. However, the Google Fitness REST API remains operational and continues to serve existing data. New user data ingestion has shifted to Health Connect on Android, but web-based health dashboards reading historical Google Fit data via the REST API continue to work. For new applications, consider whether your users are primarily Android users with Google Fit history or whether a broader health data approach (integrating multiple sources) better serves your use case.

Integration method

Next.js API Route

Google Fit connects to V0-generated Next.js apps through server-side API routes using the Google Fitness REST API with OAuth 2.0 authentication. User health data is fetched server-side using the user's authorized access token, which is obtained via Google's standard OAuth flow. Health metrics are returned to the V0-generated frontend components through your API routes.

Prerequisites

  • A V0 account at v0.dev with a Next.js project created
  • A Google account with Google Fit data (steps, activities recorded on Android or Wear OS)
  • A Google Cloud project with the Fitness API enabled at console.cloud.google.com
  • OAuth 2.0 credentials (client ID and client secret) from Google Cloud Console
  • A Vercel account connected to your V0 project for deployment

Step-by-step guide

1

Enable the Google Fitness API and Create OAuth Credentials

Set up the Google Cloud project and OAuth credentials needed to call the Google Fitness REST API. This is a prerequisite step done in Google Cloud Console before any code is written. Go to console.cloud.google.com and create a new project or select an existing one. In the left sidebar, go to APIs & Services → Library. Search for 'Fitness API' and click on Google Fitness API, then click Enable. This activates the API for your project. Next, configure the OAuth consent screen: go to APIs & Services → OAuth consent screen. Select 'External' for apps available to all Google users. Fill in the app name, user support email, and developer contact information. For scopes, you will add them in the next step. Add yourself as a test user while the app is in testing mode — external apps require verification before being publicly available. Click Save and Continue through the remaining steps. Now create OAuth credentials: go to APIs & Services → Credentials → Create Credentials → OAuth 2.0 Client IDs. Select 'Web application' as the application type. Add your Vercel deployment URL as an authorized JavaScript origin: https://your-app.vercel.app. Add your OAuth callback URL as an authorized redirect URI: https://your-app.vercel.app/api/auth/callback/google. For development, also add http://localhost:3000 and http://localhost:3000/api/auth/callback/google. After creating the OAuth client, download the JSON or copy the client ID and client secret. These become GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET in Vercel environment variables. Google Fitness API scopes you typically need: fitness.activity.read (steps, active minutes, workouts), fitness.sleep.read (sleep sessions), fitness.heart_rate.read (heart rate data), fitness.nutrition.read (nutrition logging). Request only the scopes your app actually uses — additional scopes require user consent and increase OAuth verification scrutiny.

V0 Prompt

Create a Google Fit connection page with a 'Connect with Google' button that redirects to /api/auth/google. Show a list of data that will be accessed: Step Count & Activity, Sleep Data, Heart Rate. Below the button, show privacy information explaining that data is read-only and stored only in the current session. Add a 'Disconnect' button if already connected (check /api/auth/status).

Paste this in V0 chat

app/api/auth/google/route.ts
1// app/api/auth/google/route.ts — OAuth initiation for Google Fit
2import { NextResponse } from 'next/server';
3
4export async function GET() {
5 const clientId = process.env.GOOGLE_CLIENT_ID;
6 const redirectUri = process.env.GOOGLE_REDIRECT_URI;
7
8 if (!clientId || !redirectUri) {
9 return NextResponse.json(
10 { error: 'Google OAuth not configured' },
11 { status: 500 }
12 );
13 }
14
15 const scopes = [
16 'https://www.googleapis.com/auth/fitness.activity.read',
17 'https://www.googleapis.com/auth/fitness.sleep.read',
18 'https://www.googleapis.com/auth/fitness.heart_rate.read',
19 ].join(' ');
20
21 const authUrl = new URL('https://accounts.google.com/o/oauth2/v2/auth');
22 authUrl.searchParams.set('client_id', clientId);
23 authUrl.searchParams.set('redirect_uri', redirectUri);
24 authUrl.searchParams.set('response_type', 'code');
25 authUrl.searchParams.set('scope', scopes);
26 authUrl.searchParams.set('access_type', 'offline'); // Required for refresh token
27 authUrl.searchParams.set('prompt', 'consent'); // Force consent to get refresh token
28
29 return NextResponse.redirect(authUrl.toString());
30}

Pro tip: Add access_type=offline and prompt=consent to the OAuth authorization URL. Without these, Google will not return a refresh token, and the user will need to re-authorize every hour when the access token expires.

Expected result: The Google Fitness API is enabled in your Cloud project. OAuth credentials are created with the correct redirect URIs. Clicking 'Connect with Google' redirects to Google's OAuth consent screen.

2

Create the OAuth Callback and Token Storage Route

Build the OAuth callback route that exchanges the authorization code for Google access and refresh tokens, and store them securely for use in subsequent API calls. Create app/api/auth/callback/google/route.ts. When Google redirects after user authorization, it includes a code parameter in the query string. Exchange this code for tokens by calling Google's token endpoint with the code, client credentials, and redirect URI. Google returns an access_token (expires in 1 hour), an expires_in value, and — critically — a refresh_token (permanent until revoked). For a single-user personal health dashboard, store the tokens in an encrypted cookie or Vercel KV (Upstash Redis) keyed by a session identifier. For multi-user applications where each user connects their own Google account, store tokens in your database associated with the user's profile. The refresh token is the most valuable piece: it allows your server to get new access tokens indefinitely without requiring the user to re-authorize. Store it in a database or Vercel KV rather than a short-lived cookie. The access token can be cached in memory or a short-lived cookie since it only needs to last 1 hour. After storing tokens, redirect the user to the dashboard page. The callback URL must exactly match what you registered in Google Cloud Console — even a trailing slash difference causes a redirect_uri_mismatch error.

app/api/auth/callback/google/route.ts
1// app/api/auth/callback/google/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3import { cookies } from 'next/headers';
4
5export async function GET(request: NextRequest) {
6 const { searchParams } = new URL(request.url);
7 const code = searchParams.get('code');
8 const error = searchParams.get('error');
9
10 if (error || !code) {
11 return NextResponse.redirect(new URL('/dashboard?error=oauth_failed', request.url));
12 }
13
14 const tokenResponse = await fetch('https://oauth2.googleapis.com/token', {
15 method: 'POST',
16 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
17 body: new URLSearchParams({
18 code,
19 client_id: process.env.GOOGLE_CLIENT_ID!,
20 client_secret: process.env.GOOGLE_CLIENT_SECRET!,
21 redirect_uri: process.env.GOOGLE_REDIRECT_URI!,
22 grant_type: 'authorization_code',
23 }),
24 });
25
26 if (!tokenResponse.ok) {
27 return NextResponse.redirect(new URL('/dashboard?error=token_exchange_failed', request.url));
28 }
29
30 const tokens = await tokenResponse.json();
31
32 // Store tokens in httpOnly cookies (for single-user apps)
33 // For multi-user apps, store in database associated with user session
34 const response = NextResponse.redirect(new URL('/dashboard', request.url));
35 const cookieStore = cookies();
36
37 // Access token — short-lived, store in session cookie
38 response.cookies.set('google_access_token', tokens.access_token, {
39 httpOnly: true,
40 secure: process.env.NODE_ENV === 'production',
41 maxAge: tokens.expires_in,
42 path: '/',
43 });
44
45 // Refresh token — long-lived, store securely
46 if (tokens.refresh_token) {
47 response.cookies.set('google_refresh_token', tokens.refresh_token, {
48 httpOnly: true,
49 secure: process.env.NODE_ENV === 'production',
50 maxAge: 60 * 60 * 24 * 365, // 1 year
51 path: '/',
52 });
53 }
54
55 return response;
56}

Pro tip: Google only returns the refresh_token on the first authorization or when prompt=consent is set. If you do not receive a refresh_token, the user needs to revoke and re-authorize the app (or you need to add prompt=consent to the auth URL).

Expected result: After Google authorization, the callback route exchanges the code for tokens, stores them in secure httpOnly cookies, and redirects to the dashboard. The user is now authenticated and health data can be fetched.

3

Create the Google Fit Data Fetching Routes

Build the API routes that query the Google Fitness REST API for step counts, activity sessions, and sleep data using the stored access token. These are the routes your V0-generated dashboard components call. Create app/api/google-fit/steps/route.ts. The Google Fitness REST API uses a data aggregation model: you POST a request body specifying the data type, time range, and aggregation bucket (daily, weekly, monthly). The steps endpoint uses the com.google.step_count.delta data type aggregated into daily buckets. The API base URL is https://www.googleapis.com/fitness/v1/users/me/. The dataset endpoint returns raw data points, while the aggregateBy endpoint returns pre-aggregated summaries which is what most dashboards need. Always use the aggregateBy endpoint for dashboard data rather than the raw dataset endpoint — it returns fewer data points and is significantly faster. For dates, Google Fit uses milliseconds since epoch (Unix timestamp * 1000) rather than ISO date strings. Convert JavaScript Date objects to milliseconds for the startTimeMillis and endTimeMillis fields in your request. Handle the case where the access token has expired: read the token from the httpOnly cookie, attempt the Fitness API call, and if you get a 401 response, use the refresh token to get a new access token and retry the request. This token refresh logic should be a shared utility used by all Fitness API routes.

V0 Prompt

Build a step count dashboard widget that fetches from /api/google-fit/steps?days=7. Display a bar chart showing step counts for each of the last 7 days. Add a daily goal line at 10,000 steps. Show today's total prominently at the top with goal achievement percentage. Color bars green if goal met, yellow if 75-100% of goal, red if below 75%. Include a weekly total and daily average below the chart.

Paste this in V0 chat

app/api/google-fit/steps/route.ts
1import { NextRequest, NextResponse } from 'next/server';
2import { cookies } from 'next/headers';
3
4const FITNESS_API = 'https://www.googleapis.com/fitness/v1/users/me';
5
6async function refreshAccessToken(refreshToken: string): Promise<string | null> {
7 const response = await fetch('https://oauth2.googleapis.com/token', {
8 method: 'POST',
9 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
10 body: new URLSearchParams({
11 refresh_token: refreshToken,
12 client_id: process.env.GOOGLE_CLIENT_ID!,
13 client_secret: process.env.GOOGLE_CLIENT_SECRET!,
14 grant_type: 'refresh_token',
15 }),
16 });
17 if (!response.ok) return null;
18 const data = await response.json();
19 return data.access_token;
20}
21
22export async function GET(request: NextRequest) {
23 const cookieStore = cookies();
24 let accessToken = cookieStore.get('google_access_token')?.value;
25 const refreshToken = cookieStore.get('google_refresh_token')?.value;
26
27 if (!accessToken && refreshToken) {
28 accessToken = await refreshAccessToken(refreshToken) || undefined;
29 }
30
31 if (!accessToken) {
32 return NextResponse.json({ error: 'Not authenticated with Google Fit' }, { status: 401 });
33 }
34
35 const { searchParams } = new URL(request.url);
36 const days = parseInt(searchParams.get('days') || '7');
37 const endTime = Date.now();
38 const startTime = endTime - days * 24 * 60 * 60 * 1000;
39
40 const body = {
41 aggregateBy: [{ dataTypeName: 'com.google.step_count.delta' }],
42 bucketByTime: { durationMillis: 86400000 }, // 1 day buckets
43 startTimeMillis: startTime.toString(),
44 endTimeMillis: endTime.toString(),
45 };
46
47 const response = await fetch(`${FITNESS_API}/dataset:aggregate`, {
48 method: 'POST',
49 headers: {
50 Authorization: `Bearer ${accessToken}`,
51 'Content-Type': 'application/json',
52 },
53 body: JSON.stringify(body),
54 });
55
56 if (response.status === 401) {
57 return NextResponse.json({ error: 'Token expired — please reconnect Google Fit' }, { status: 401 });
58 }
59
60 if (!response.ok) {
61 return NextResponse.json({ error: 'Failed to fetch step data' }, { status: response.status });
62 }
63
64 const data = await response.json();
65 const steps = (data.bucket || []).map((bucket: {
66 startTimeMillis: string;
67 dataset: Array<{ point: Array<{ value: Array<{ intVal?: number }> }> }>;
68 }) => ({
69 date: new Date(parseInt(bucket.startTimeMillis)).toISOString().split('T')[0],
70 steps: bucket.dataset[0]?.point[0]?.value[0]?.intVal || 0,
71 }));
72
73 return NextResponse.json({ steps, period: `${days}d` });
74}

Pro tip: The Google Fitness API returns data in nested buckets with complex structure. Always check that bucket.dataset[0]?.point[0] exists before accessing values — days with no recorded activity return empty point arrays, not zero-value points.

Expected result: The steps API route returns daily step counts for the requested period. The V0-generated bar chart displays real step data from Google Fit. Days with no data show zero-height bars.

4

Add Environment Variables and Deploy

Configure Google OAuth credentials in Vercel environment variables and test the end-to-end flow from Google OAuth authorization to health data display in your dashboard. In Vercel Dashboard → Settings → Environment Variables, add: GOOGLE_CLIENT_ID (from Google Cloud Console OAuth credentials), GOOGLE_CLIENT_SECRET (from Google Cloud Console), and GOOGLE_REDIRECT_URI (your production callback URL, e.g., https://your-app.vercel.app/api/auth/callback/google). Critically, update the Google Cloud Console OAuth credentials to add your Vercel deployment URL: go to Google Cloud Console → APIs & Services → Credentials → click your OAuth client → add https://your-app.vercel.app as an Authorized JavaScript Origin and https://your-app.vercel.app/api/auth/callback/google as an Authorized Redirect URI. Without this, Google will reject OAuth callbacks from your production deployment with a redirect_uri_mismatch error. After deploying, test the OAuth flow: click the 'Connect with Google' button, authorize through Google's consent screen, verify the redirect back to your dashboard works, and confirm step data appears in the charts. If the OAuth callback fails, check that the redirect URI in Google Cloud Console exactly matches what is in GOOGLE_REDIRECT_URI — case-sensitive, including or excluding trailing slashes. A key consideration for V0 developers: Google Fitness API access for apps in testing mode (OAuth verification not completed) is limited to 100 authorized test users. For personal dashboards, this limit is sufficient. For public apps, you need to complete Google's OAuth verification process, which requires a privacy policy URL and review by Google's trust and safety team.

Pro tip: For personal health dashboards, complete the minimal OAuth verification (privacy policy, app description) to move out of testing mode. Testing-mode apps show a warning to users on the consent screen and have a 100-user limit.

Expected result: Google OAuth credentials are in Vercel environment variables. Redirect URIs are registered in Google Cloud Console. The OAuth flow completes successfully and health data displays in the dashboard after authorization.

Common use cases

Personal Activity Dashboard

Build a personal fitness dashboard that displays daily step counts, active minutes, calories burned, and weekly trends pulled from Google Fit. Show a 7-day step count chart, today's activity summary, and monthly goal progress. This gives users a custom view of their Google Fit data with visualizations tailored to their fitness goals.

V0 Prompt

Create a personal fitness dashboard with these sections: Today's Stats row showing Steps (with goal progress bar), Active Minutes, Calories Burned, and Distance. Below, a 7-day step count bar chart using Recharts BarChart. A weekly trends section showing Mon-Sun with color-coded days (green if goal met, yellow if close, red if far off). Fetch data from /api/google-fit/summary. Show a 'Connect Google Fit' button if not authenticated.

Copy this prompt to try it in V0

Sleep Quality Tracker

Display sleep session data from Google Fit including total sleep duration, sleep quality phases (light, deep, REM sleep), and weekly sleep consistency metrics. Users can see sleep trends over time and identify patterns affecting their sleep quality.

V0 Prompt

Build a sleep tracker page that calls /api/google-fit/sleep?days=30. Show: average sleep duration for the period, a 30-day timeline chart with each night shown as a bar (total hours), today's sleep breakdown as a stacked bar (awake/light/deep/REM), and a streak counter showing consecutive nights with 7+ hours. Add a weekly average card comparing this week to last week. Use muted colors: deep sleep in navy, REM in purple, light in blue, awake in red.

Copy this prompt to try it in V0

Fitness Goal Progress Report

Create a monthly fitness report that summarizes Google Fit activity data against user-set goals. Show goal achievement rates for steps, active minutes, and active days, with week-by-week breakdown and encouraging messaging when goals are met.

V0 Prompt

Create a monthly fitness report page that fetches 30 days of data from /api/google-fit/summary?period=month. Display: Goal Achievement section with step goal (default 10,000/day), active minutes goal (30/day), and active days goal (5/week) as progress rings. A weekly breakdown table showing Week 1-4 with Steps Avg, Active Minutes Avg, Days Active. A 'Best Day' callout showing the single highest step day. A motivational summary section with a personalized message based on overall goal achievement percentage.

Copy this prompt to try it in V0

Troubleshooting

OAuth error — 'redirect_uri_mismatch' after Google authorization

Cause: The redirect URI in your GOOGLE_REDIRECT_URI environment variable does not exactly match an Authorized Redirect URI registered in Google Cloud Console. This mismatch causes Google to reject the callback.

Solution: Go to Google Cloud Console → APIs & Services → Credentials → click your OAuth client. Verify the Authorized Redirect URIs include the exact URL from your GOOGLE_REDIRECT_URI environment variable — including https, the exact domain, and the exact path /api/auth/callback/google. Even a trailing slash difference causes this error. Add both with and without trailing slashes to be safe.

Google Fitness API returns empty data for step count even though Google Fit shows data in the app

Cause: The time range in the request does not align with when data was recorded, the data type name is incorrect, or the Android device has not synced recent data to Google Fit's servers.

Solution: Verify the startTimeMillis and endTimeMillis values are in milliseconds (not seconds) and cover the period you expect. Ensure the user's Android device has synced Google Fit data recently (open Google Fit app, pull down to refresh). Check that you are using com.google.step_count.delta as the data type name — this is case-sensitive.

typescript
1// Debug: log the time range being requested
2const startDate = new Date(startTime);
3const endDate = new Date(endTime);
4console.log('Fetching steps from:', startDate.toISOString(), 'to:', endDate.toISOString());
5console.log('startTimeMillis:', startTime.toString()); // Must be a string in the request body

Google OAuth does not return a refresh_token — only an access_token

Cause: Google only returns a refresh_token on the first authorization or when prompt=consent is included. If the user previously authorized your app, subsequent authorizations skip consent and do not return a new refresh token.

Solution: Add prompt=consent and access_type=offline to your OAuth authorization URL. If users have already authorized, they need to revoke access (Google Account settings → Security → Third-party apps) and re-authorize. Alternatively, store the refresh token permanently on first authorization and reuse it.

typescript
1// Ensure these params are in your auth URL
2authUrl.searchParams.set('access_type', 'offline');
3authUrl.searchParams.set('prompt', 'consent'); // Forces consent screen even for re-authorization

Best practices

  • Store Google OAuth refresh tokens in a persistent database or Vercel KV (Upstash Redis) rather than cookies — cookies are cleared when users clear browser data, losing access until they re-authorize.
  • Request only the Google Fitness API scopes your app actually uses — each additional scope increases OAuth verification scrutiny and user concern about data access.
  • Use the dataset:aggregate endpoint with daily buckets rather than the raw dataset endpoint — it returns pre-aggregated daily values, reducing response size and simplifying data processing.
  • Implement token refresh transparently: if a Fitness API call returns 401, automatically get a new access token using the refresh token and retry the request before returning an error to the user.
  • Cache aggregated health data in Vercel KV or your database with appropriate TTLs — step count data does not change after midnight, so a 6-hour cache for historical days is safe.
  • Complete Google's OAuth app verification before launching publicly — unverified apps show a security warning on the consent screen that discourages users from authorizing.
  • Handle the case where users revoke access in their Google Account settings — Fitness API calls will return 401 errors that must be handled with a re-authorization prompt rather than an error message.

Alternatives

Frequently asked questions

Does Google Fit's deprecation affect the REST API?

Google deprecated the Google Fit Android app and website in 2024-2025, but the Google Fitness REST API remains operational. Existing data stored in Google Fit is still readable via the API. For new data collection on Android, Google now recommends Health Connect. If you are building a new health app targeting Android users, consider whether to use the Fitness REST API (for existing data) or the Android Health Connect SDK (for new data).

Can I access other users' Google Fit data without their authorization?

No — the Google Fitness API requires each user to individually authorize your app via OAuth to access their health data. You can only read data for users who have explicitly granted your app permission. There is no way to access Google Fit data without the user's explicit OAuth consent.

What health data types are available via the Google Fitness API?

The Google Fitness API provides: step count (com.google.step_count.delta), calories burned, active minutes, distance traveled, heart rate, blood glucose, blood pressure, sleep sessions, activity segments (walking, running, cycling), weight, height, and body fat percentage. Data availability depends on what devices and apps the user has connected to Google Fit.

How do I handle users who have never used Google Fit?

The Fitness API returns empty datasets for users with no recorded data — empty bucket arrays rather than errors. Implement empty state handling in your V0-generated components: show a message like 'No fitness data recorded yet. Connect your Android device to Google Fit to start tracking.' for users with zero data points.

Can I read Google Fit data in real time or only historical data?

The Google Fitness REST API provides near-real-time data that syncs from the user's Android device to Google's servers. Data updates are not instant — there is typically a 15-30 minute delay from when activity is recorded on device to when it appears in the API. For real-time step counting, the user's device itself tracks the data; the API is better suited for historical and daily aggregated views.

Do I need to submit my app for Google OAuth verification to use the Fitness API?

For personal or internal use (100 test users), verification is not required but the consent screen shows a warning. For public-facing apps, Google requires OAuth verification — you must provide a privacy policy URL, describe how fitness data is used, and pass Google's review. The review process takes 1-4 weeks. Fitness data is considered sensitive, so Google scrutinizes these apps carefully.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.