To integrate RescueTime with V0 by Vercel, generate a productivity dashboard UI with V0, create a Next.js API route that fetches your time-tracking and productivity data from the RescueTime Analytics API using your API key, add the key to Vercel environment variables, and deploy. Your dashboard will display time spent by category, daily productivity scores, and top-used applications.
Build a Personal Productivity Dashboard with RescueTime and V0
RescueTime quietly runs in the background on your computer, tracking every app and website you use and assigning productivity scores based on categories you configure. Its Analytics API makes all that data accessible — daily productivity scores, time spent per category (software development, communication, social media), top applications by time, and focus-time totals. V0 can generate the dashboard UI in minutes; the integration challenge is connecting that UI to real data through a secure server-side API route.
The RescueTime Analytics API uses simple key-based authentication — you append your API key to each request as a query parameter. Because this key grants read access to your time-tracking history, it belongs in a server-side environment variable, proxied through a Next.js API route rather than called directly from browser JavaScript. The API returns JSON data you can map to charts, progress bars, and summary stats in your dashboard.
This integration is particularly useful for SaaS founders who want to track their own productivity while building, coaches who want to analyze client productivity data (with appropriate authorization), or teams building internal tools that aggregate productivity metrics. The V0-generated UI provides a polished starting point — bar charts for daily time breakdowns, a productivity score gauge, and category summaries — that you can customize for your specific reporting needs.
Integration method
RescueTime integrates with V0-generated Next.js apps through a server-side API route that proxies requests to the RescueTime Analytics API. Your RescueTime API key is stored as a server-only environment variable in Vercel and never exposed to the browser. The React components V0 generates fetch data from your internal API route and render charts and summary cards showing productivity analytics.
Prerequisites
- A RescueTime account with the desktop app installed and running (rescuetime.com) — the API only returns data after the app has tracked activity
- Your RescueTime API key — found at rescuetime.com/anapi/manage under 'Create a new API Key'
- A V0 account at v0.dev to generate the dashboard UI
- A Vercel account to deploy the Next.js app and store environment variables securely
- At least a few days of RescueTime tracking history so the API returns meaningful data for testing
Step-by-step guide
Generate the Productivity Dashboard UI with V0
Generate the Productivity Dashboard UI with V0
Open V0 at v0.dev and prompt it to generate a productivity analytics dashboard. Be specific about the data visualizations you want — productivity score gauges, category breakdown charts, and activity lists map well to V0's component generation capabilities. V0 will create a Next.js App Router project with React components using shadcn/ui charts (built on Recharts) and Tailwind CSS. The generated components will include placeholder data arrays and likely mock fetch calls to endpoints like /api/rescuetime/summary. Review the preview — you should see a styled dashboard with sample charts and cards. The key components to note are how the charts expect their data formatted (usually arrays of objects with name and value keys) since you'll need to transform RescueTime's API response to match. Use V0's Git panel to push the project to a new GitHub repository. If V0 generates a client component that fetches data directly on mount via useEffect, that's fine for now — the API route you create will proxy the secure RescueTime call so the component's fetch call doesn't need your API key.
Build a productivity analytics dashboard with a productivity score card showing a large percentage number and color indicator (green for 70+, yellow for 40-69, red below 40), a donut chart breaking down time by category (Software Development, Communication, Utilities, Social Media, Other), a ranked list of today's top 5 apps by time spent, and total hours tracked today. Include a simple date picker. Data loads from /api/rescuetime/summary. Use a clean professional design.
Paste this in V0 chat
Pro tip: Ask V0 to add skeleton loading states to your dashboard components: 'Add skeleton loaders while data is being fetched from the API.' RescueTime API calls can take 1-2 seconds, and skeleton loaders prevent layout shifts.
Expected result: A styled productivity dashboard renders in V0's preview with sample chart data, a productivity score display, category breakdown, and app usage list — all ready to be connected to real RescueTime data.
Create the RescueTime API Route
Create the RescueTime API Route
Create an API route that acts as a secure proxy between your React components and the RescueTime Analytics API. The RescueTime API's main data endpoint is https://www.rescuetime.com/anapi/data — it accepts query parameters including key (your API key), perspective (interval or rank), resolution_time (day, week, month), restrict_begin and restrict_end (date range), and format (json). The response includes a 'rows' array where each row is [date, time_spent_seconds, number_of_people, activity_name, category, productivity_score]. Your API route reads the API key from process.env.RESCUETIME_API_KEY, constructs the query URL with appropriate parameters from the request's query string, fetches from RescueTime, and transforms the response into a clean JSON structure for your React components. Keep the transformation logic in the API route rather than the component — this makes it easier to adjust the data shape if RescueTime updates their API response format. The route should support query parameters like date and resolution so your UI's date picker can request different time ranges.
1// app/api/rescuetime/summary/route.ts2import { NextRequest, NextResponse } from 'next/server';34const RESCUETIME_API_BASE = 'https://www.rescuetime.com/anapi/data';56interface RescueTimeRow {7 date: string;8 timeSpentSeconds: number;9 activity: string;10 category: string;11 productivityScore: number;12}1314export async function GET(request: NextRequest) {15 const apiKey = process.env.RESCUETIME_API_KEY;1617 if (!apiKey) {18 return NextResponse.json(19 { error: 'RescueTime API key not configured' },20 { status: 500 }21 );22 }2324 const { searchParams } = new URL(request.url);25 const date = searchParams.get('date') || new Date().toISOString().split('T')[0];2627 const params = new URLSearchParams({28 key: apiKey,29 perspective: 'rank',30 resolution_time: 'day',31 restrict_begin: date,32 restrict_end: date,33 format: 'json',34 });3536 try {37 const response = await fetch(`${RESCUETIME_API_BASE}?${params.toString()}`, {38 next: { revalidate: 300 }, // Cache for 5 minutes39 });4041 if (!response.ok) {42 throw new Error(`RescueTime API error: ${response.status}`);43 }4445 const data = await response.json();4647 // Transform rows: [date, seconds, people, activity, category, productivity]48 const rows: RescueTimeRow[] = (data.rows || []).map((row: (string | number)[]) => ({49 date: row[0] as string,50 timeSpentSeconds: row[1] as number,51 activity: row[3] as string,52 category: row[4] as string,53 productivityScore: row[5] as number,54 }));5556 // Compute summary stats57 const totalSeconds = rows.reduce((sum, r) => sum + r.timeSpentSeconds, 0);58 const productiveSeconds = rows59 .filter((r) => r.productivityScore > 0)60 .reduce((sum, r) => sum + r.timeSpentSeconds, 0);61 const productivityPercent =62 totalSeconds > 0 ? Math.round((productiveSeconds / totalSeconds) * 100) : 0;6364 // Group by category65 const byCategory: Record<string, number> = {};66 for (const row of rows) {67 byCategory[row.category] = (byCategory[row.category] || 0) + row.timeSpentSeconds;68 }69 const categories = Object.entries(byCategory)70 .map(([name, seconds]) => ({ name, hours: Math.round((seconds / 3600) * 10) / 10 }))71 .sort((a, b) => b.hours - a.hours);7273 return NextResponse.json({74 date,75 totalHours: Math.round((totalSeconds / 3600) * 10) / 10,76 productivityPercent,77 topActivities: rows.slice(0, 10),78 categories,79 });80 } catch (error) {81 console.error('RescueTime API error:', error);82 return NextResponse.json(83 { error: 'Failed to fetch RescueTime data' },84 { status: 500 }85 );86 }87}Pro tip: The next: { revalidate: 300 } option caches the RescueTime API response for 5 minutes on Vercel's edge. RescueTime's data is updated every few minutes anyway, so 5-minute caching avoids unnecessary API calls without showing stale data.
Expected result: Calling GET /api/rescuetime/summary returns a JSON object with totalHours, productivityPercent, categories array, and topActivities — ready for your dashboard components to consume.
Connect the Dashboard Components to the API Route
Connect the Dashboard Components to the API Route
Now update your V0-generated React components to fetch from the API route instead of using mock data. If V0 generated a Server Component (no 'use client' directive), you can fetch directly in the component function using the standard fetch API. If it generated a Client Component with useEffect, you'll keep that pattern and just update the fetch URL. The transformation you did in the API route means your components receive clean data objects matching the chart library's expected format. For the donut chart showing category breakdown, map the categories array to the format your chart component expects — typically [{name: 'Software Development', value: 2.4}, ...]. For the productivity score display, use productivityPercent as a 0-100 number. For the activity list, use topActivities and convert timeSpentSeconds to a human-readable format (convert to minutes or hours using simple math). If V0 generated components using Recharts directly (via shadcn/ui's chart primitives), the data format should slot in naturally. Test by visiting your page locally or in V0's preview — you should see real data from your RescueTime account replacing the mock values. Add error handling to show a friendly message if the API route returns an error (for example, if RescueTime hasn't tracked anything today).
Update the productivity dashboard to fetch data from /api/rescuetime/summary?date={today's date in YYYY-MM-DD format} when the component mounts. Display the returned totalHours, productivityPercent as the score, categories array in the donut chart, and topActivities as a ranked list. Show a skeleton loader while fetching and an error message if the request fails. Add a date input that re-fetches data for the selected date.
Paste this in V0 chat
1// app/components/ProductivityDashboard.tsx2'use client';34import { useEffect, useState } from 'react';56interface SummaryData {7 date: string;8 totalHours: number;9 productivityPercent: number;10 categories: { name: string; hours: number }[];11 topActivities: { activity: string; timeSpentSeconds: number; productivityScore: number }[];12}1314export default function ProductivityDashboard() {15 const [data, setData] = useState<SummaryData | null>(null);16 const [loading, setLoading] = useState(true);17 const [error, setError] = useState<string | null>(null);18 const [date, setDate] = useState(new Date().toISOString().split('T')[0]);1920 useEffect(() => {21 setLoading(true);22 fetch(`/api/rescuetime/summary?date=${date}`)23 .then((res) => res.json())24 .then((d) => {25 setData(d);26 setLoading(false);27 })28 .catch(() => {29 setError('Failed to load RescueTime data');30 setLoading(false);31 });32 }, [date]);3334 if (loading) return <div className="animate-pulse h-64 bg-gray-100 rounded-lg" />;35 if (error) return <div className="text-red-500 p-4">{error}</div>;36 if (!data) return null;3738 return (39 <div className="space-y-6 p-6">40 <div className="flex items-center justify-between">41 <h1 className="text-2xl font-bold">Productivity Dashboard</h1>42 <input43 type="date"44 value={date}45 onChange={(e) => setDate(e.target.value)}46 className="border rounded px-3 py-1 text-sm"47 />48 </div>49 <div className="grid grid-cols-2 gap-4">50 <div className="bg-white border rounded-lg p-4">51 <div className="text-sm text-gray-500">Productivity Score</div>52 <div className={`text-4xl font-bold ${53 data.productivityPercent >= 70 ? 'text-green-600' :54 data.productivityPercent >= 40 ? 'text-yellow-500' : 'text-red-500'55 }`}>{data.productivityPercent}%</div>56 </div>57 <div className="bg-white border rounded-lg p-4">58 <div className="text-sm text-gray-500">Total Hours Tracked</div>59 <div className="text-4xl font-bold text-gray-800">{data.totalHours}h</div>60 </div>61 </div>62 <div className="bg-white border rounded-lg p-4">63 <h2 className="font-semibold mb-3">Time by Category</h2>64 {data.categories.map((cat) => (65 <div key={cat.name} className="flex justify-between py-1 text-sm">66 <span>{cat.name}</span>67 <span className="font-medium">{cat.hours}h</span>68 </div>69 ))}70 </div>71 </div>72 );73}Pro tip: Format seconds to human-readable time in a utility function: const formatTime = (s: number) => s >= 3600 ? `${Math.floor(s/3600)}h ${Math.floor((s%3600)/60)}m` : `${Math.floor(s/60)}m`. Use this in your activity list for clarity.
Expected result: The dashboard component fetches real data from the API route and renders your actual RescueTime productivity score, category breakdown, and top activities for the selected date.
Add Environment Variable and Deploy to Vercel
Add Environment Variable and Deploy to Vercel
Push your updated project to GitHub, then configure the RescueTime API key in Vercel. Open your Vercel Dashboard, navigate to your project, click Settings, then Environment Variables. Add a new variable named RESCUETIME_API_KEY and paste your API key (found at rescuetime.com/anapi/manage under the 'API Key' section — if you haven't created one, click 'Create a new API Key' and name it for your V0 app). This variable should NOT have the NEXT_PUBLIC_ prefix since it's a server-side secret used only in your API route. Set it for Production, Preview, and Development environments. Click Save. Trigger a new deployment from the Deployments tab or push a commit to your GitHub repository. Once deployed, open your Vercel URL and navigate to the dashboard page. Because RescueTime's API returns your personal time-tracking data, you should see real stats immediately if the desktop app has been running. If the dashboard shows an error or empty state, check the Vercel Function Logs (Vercel Dashboard → Deployments → click your deployment → Functions tab) to see the raw error from the RescueTime API. Common issues are an invalid API key or no data for the current date (RescueTime only tracks when the desktop app is running).
Pro tip: RescueTime's API rate limit is generous for personal use, but if you're polling frequently, the next: { revalidate: 300 } cache setting in your API route prevents redundant requests. For multi-user apps, each user would need their own API key which requires a different auth approach.
Expected result: Your Vercel deployment runs successfully, the RESCUETIME_API_KEY is injected into the API route at runtime, and your productivity dashboard loads real data from your RescueTime account when accessed via the Vercel URL.
Common use cases
Personal Productivity Dashboard
A personal analytics page that fetches the day's RescueTime data and displays total productive hours, a productivity score (0-100), and a breakdown of time by category. The user can filter by date range to compare productivity across weeks.
Create a productivity dashboard with a large productivity score gauge showing 0-100, a daily summary showing total productive hours vs unproductive hours, a bar chart of time spent by category (Software Development, Communication, Design, etc.), and a date range picker to filter the data fetched from /api/rescuetime/summary. Use a clean minimal design with green for productive time and gray for neutral.
Copy this prompt to try it in V0
Weekly Productivity Report Page
A report page that pulls the past 7 days of RescueTime data and shows trend lines, day-by-day productivity comparisons, and which applications consumed the most time that week. Useful for weekly retrospectives and habit improvement.
Build a weekly productivity report page showing a line chart of daily productivity scores over 7 days, a ranked list of top applications by total time spent, a comparison of this week vs last week productivity percentage, and a 'most focused day' highlight card. Data comes from /api/rescuetime/weekly.
Copy this prompt to try it in V0
Focus Time Tracker Widget
A widget that calls the RescueTime API to show today's total focus time (time spent in 'very productive' activities) versus the user's daily goal. A simple progress bar and current streak counter motivate consistent deep work habits.
Design a focus time widget with a circular progress bar showing today's focus hours out of a 4-hour goal, a 7-day streak indicator, and a small table of today's top focus activities. Fetch current data from /api/rescuetime/focus. Use an indigo and white color scheme.
Copy this prompt to try it in V0
Troubleshooting
API route returns empty rows array even though RescueTime shows data in the dashboard
Cause: The date range in the API request doesn't match when data was recorded, or the restrict_begin and restrict_end parameters are formatted incorrectly. RescueTime expects dates in YYYY-MM-DD format and uses the timezone configured in your account.
Solution: Verify the date format is YYYY-MM-DD (e.g., 2026-03-31). Check that your RescueTime account timezone matches the dates you're querying — data recorded at 11 PM in one timezone may appear under the next day in another. Log the full API URL in your route handler to debug the exact parameters being sent.
1// Log the full request URL for debugging2console.log('Fetching:', `${RESCUETIME_API_BASE}?${params.toString()}`);RescueTime API returns 401 Unauthorized
Cause: The API key is incorrect, expired, or was set in an environment variable with extra whitespace or quotes around it. Vercel environment variable values are used exactly as entered — any surrounding quotes or newlines become part of the value.
Solution: Go to rescuetime.com/anapi/manage to verify your API key is active. In Vercel Dashboard → Environment Variables, delete and re-add the RESCUETIME_API_KEY value, being careful not to include quotes or trailing spaces. Redeploy after making changes.
Dashboard shows 0 hours and 0% productivity for today
Cause: The RescueTime desktop app is not running on your computer, so no activity has been tracked. The API returns an empty rows array when there's no data for the requested period, causing all totals to default to zero.
Solution: Ensure the RescueTime app is installed, running, and not paused. Check the RescueTime web dashboard at rescuetime.com to confirm data is being recorded. Test the API with a past date that you know had activity to verify the API route is working correctly.
RESCUETIME_API_KEY is undefined in Vercel Functions logs
Cause: The environment variable was added to Vercel after the last deployment and the deployment hasn't been rebuilt with the new variable. Vercel injects environment variables at build time for the serverless function bundle.
Solution: After adding or changing environment variables in Vercel Dashboard, trigger a new deployment. Go to Deployments tab → click the three-dot menu on the latest deployment → Redeploy. The new deployment will include the updated environment variables.
Best practices
- Never expose your RescueTime API key in client-side code or NEXT_PUBLIC_ environment variables — always proxy through an API route
- Cache RescueTime API responses for 5-10 minutes using Next.js fetch caching (next: { revalidate: 300 }) since the data updates infrequently
- Handle the case where no data exists for a date (empty rows array) gracefully with a friendly empty state rather than an error
- Use the restrict_kind parameter to filter data by 'activity', 'category', or 'overview' depending on what granularity your dashboard needs
- For team dashboards, require each user to provide their own RescueTime API key via your app's settings — never use a single shared key for multiple users
- Display time in human-readable format (2h 15m) rather than raw seconds — this requires a simple utility function but dramatically improves dashboard readability
- Add a refresh button that clears the Next.js cache and re-fetches fresh data for users who want up-to-the-minute accuracy
Alternatives
Use Doodle instead of RescueTime if you need scheduling and meeting coordination rather than passive time tracking and productivity analytics.
Choose Teamwork if you need active time tracking tied to specific projects and tasks rather than automatic passive tracking of all computer activity.
Use the Fitbit API instead of RescueTime if you want to display physical health metrics like steps, sleep, and heart rate alongside or instead of digital productivity data.
Frequently asked questions
Does the RescueTime API require OAuth or is an API key enough?
For accessing your own account data, an API key is sufficient. You generate the key at rescuetime.com/anapi/manage and include it as a query parameter in API requests. OAuth is only required if you're building an application that accesses other users' RescueTime data — for a personal productivity dashboard, the API key approach is simpler and recommended.
Can I display RescueTime data for multiple team members?
RescueTime's API key grants access only to the account that generated it. To show multiple team members' data, each person would need to provide their own API key through your app's settings. RescueTime does offer team plans with separate analytics per user, but each user's key accesses only their own data — there's no admin-level key that returns all users' data.
How often does RescueTime update its API data?
RescueTime syncs data from the desktop app approximately every 5-15 minutes. The API reflects data as of the last sync. This means very recent activity (last few minutes) may not appear yet. Setting a 5-minute cache in your API route (next: { revalidate: 300 }) aligns well with this update frequency.
What is the RescueTime API rate limit?
RescueTime doesn't publish a specific rate limit, but their API is designed for reasonable personal use — dozens of requests per hour. Aggressive polling (every few seconds) would likely trigger throttling. Using server-side response caching in your API route and only re-fetching on user action or at reasonable intervals (every 5-10 minutes) keeps you well within limits.
Can I access historical RescueTime data going back years?
Yes. The RescueTime API supports any date range through the restrict_begin and restrict_end parameters. You can fetch data going back to when you first installed the desktop app. For long date ranges (months or years), the API returns aggregated data rather than minute-by-minute logs, which is appropriate for trend charts.
Will this integration work on Vercel's free Hobby plan?
Yes. The RescueTime integration uses a simple GET API route with no special infrastructure requirements. Vercel Hobby's 300-second function timeout and 2GB memory are far more than sufficient for fetching and transforming RescueTime's API response. The only limitation is that Hobby functions have a 10-second default timeout for API routes, which is plenty for a single RescueTime API call.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation