Skip to main content
RapidDev - Software Development Agency

YouTube API

Connect FlutterFlow to the YouTube Data API v3 using a Custom Widget for video playback (youtube_player_flutter) and a FlutterFlow API Call group for public data reads with an API key. Public video, channel, and playlist data requires only an API key as a URL parameter — no backend proxy needed. Private data and OAuth require a Firebase or Supabase proxy. The critical gotcha: search.list costs 100 quota units vs 1 for videos.list, so quota-aware design is essential.

What you'll learn

  • How to create a YouTube Data API v3 key in Google Cloud Console and restrict it for app safety
  • How to build the youtube_player_flutter Custom Widget in FlutterFlow for IFrame-compliant video playback
  • How to set up a FlutterFlow API Call group for the YouTube Data API v3 and parse nested JSON into Data Types
  • How quota units work and why replacing search.list with playlistItems/videos calls is essential for app longevity
  • How to cache API responses in Firestore or Supabase to stretch the free 10,000-unit daily quota across many users
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate23 min read45 minutesMedia & ContentLast updated July 2026RapidDev Engineering Team
TL;DR

Connect FlutterFlow to the YouTube Data API v3 using a Custom Widget for video playback (youtube_player_flutter) and a FlutterFlow API Call group for public data reads with an API key. Public video, channel, and playlist data requires only an API key as a URL parameter — no backend proxy needed. Private data and OAuth require a Firebase or Supabase proxy. The critical gotcha: search.list costs 100 quota units vs 1 for videos.list, so quota-aware design is essential.

Quick facts about this guide
FactValue
ToolYouTube API
CategoryMedia & Content
MethodCustom Widget
DifficultyIntermediate
Time required45 minutes
Last updatedJuly 2026

Build a YouTube Video Feed or Channel Dashboard in FlutterFlow

YouTube integration is one of the most-requested features in FlutterFlow video apps — whether that is a community platform that surfaces curated channels, a creator tool that shows channel analytics, or a content hub that lets users browse and watch videos without leaving the app. The YouTube Data API v3 makes all of this possible, but it introduces a concept most developers don't encounter with other APIs: the quota-unit system. Rather than a simple rate limit, every API call to YouTube costs a specific number of quota units drawn from a 10,000-unit daily pool. The critical number to memorize is that search.list costs 100 units per call while videos.list and playlistItems.list cost approximately 1 unit — a naive implementation that runs search on every screen load can exhaust the entire daily budget in around 100 queries.

For playback, YouTube's Terms of Service prohibit extracting raw video stream URLs and playing them directly. The compliant path in FlutterFlow is the youtube_player_flutter Custom Widget, which wraps YouTube's official IFrame player. This means playback runs inside a WebView-based component — it will not render in FlutterFlow's web Test Mode canvas, so all playback testing must happen on a real device or through a build. API metadata calls (fetching video titles, thumbnails, statistics, and playlist contents) do run in Test Mode and can be validated there.

Auth comes in two flavors. Public data — any video's title, thumbnail, view count, description, and playlist contents — needs only an API key passed as a URL query parameter. Because the key is public-facing (like a web API key), it is acceptable to include it in a FlutterFlow API Call, though you should restrict it to your app's bundle ID or referrer in Google Cloud Console. Private data, such as managing your own channel's uploads, accessing YouTube Analytics time-series data, or moderating comments, requires Google OAuth 2.0 with youtube/youtube.readonly scopes. The OAuth client secret must never ship in the Flutter bundle — proxy those calls through Firebase Cloud Functions or a Supabase Edge Function.

Integration method

Custom Widget

YouTube integration in FlutterFlow combines two layers: a Custom Widget using the youtube_player_flutter pub.dev package for in-app video playback (the only approach that complies with YouTube's Terms of Service — raw stream URL extraction is prohibited), plus a FlutterFlow API Call group hitting the YouTube Data API v3 base URL for metadata reads. Public catalog reads use an API key as a query parameter directly in the API Call; private or write operations require routing through a Firebase Cloud Function or Supabase Edge Function proxy that holds the OAuth client secret.

Prerequisites

  • A Google account with access to Google Cloud Console (console.cloud.google.com)
  • A Google Cloud project with the YouTube Data API v3 enabled — go to APIs & Services → Library → search 'YouTube Data API v3' → Enable
  • A YouTube Data API v3 API key created in Cloud Console → APIs & Services → Credentials → Create Credentials → API key
  • A FlutterFlow project (any plan — Custom Code requires the Standard plan or above for the Custom Widget editor)
  • For private channel data or OAuth: a Firebase project (Cloud Functions) or Supabase project (Edge Functions) to host the proxy

Step-by-step guide

1

Create and restrict a YouTube Data API v3 key in Google Cloud Console

Open console.cloud.google.com and sign in. Select or create a project using the dropdown at the top of the page. In the left sidebar, navigate to APIs & Services → Library. Search for 'YouTube Data API v3' and click Enable. Once enabled, go to APIs & Services → Credentials. Click Create Credentials at the top → choose API key. Google generates an API key immediately — copy it to a safe place. Now restrict the key so it can only be used for YouTube calls from your app. Click the pencil (edit) icon next to the new key. Under Application restrictions, choose 'Android apps' or 'iOS apps' and enter your app's package name and SHA-1 certificate fingerprint (for mobile builds). If you're also building a web version, add 'HTTP referrers' with your app's domain. Under API restrictions, select 'Restrict key' and choose 'YouTube Data API v3' from the dropdown. Click Save. Note your YouTube channel ID if you plan to show channel-specific data. Go to studio.youtube.com → Settings → Channel → Advanced Settings and copy the Channel ID (it looks like UCxxxxxxxxxxxxxxxxxxxx). The API key is used as a query parameter: `?key=YOUR_API_KEY` appended to every YouTube API Call URL in FlutterFlow. This key is safe for client-side use in FlutterFlow API Calls because it is scoped to YouTube only and restricted to your app's identifier — it is the equivalent of a publishable key. Your daily free quota is 10,000 units; check current consumption at Cloud Console → APIs & Services → Dashboard → YouTube Data API v3.

Pro tip: Quota resets at midnight Pacific Time. If you exhaust your quota during development, either wait for reset or apply for a quota increase in Cloud Console → APIs & Services → Quotas & System Limits → YouTube Data API v3. Never use the same API key across multiple apps — create a separate key per project so quota is isolated.

Expected result: You have a YouTube Data API v3 key restricted to your app identifier and only the YouTube API. The API is enabled in your Cloud project and quota usage is visible in the Dashboard.

2

Build the youtube_player_flutter Custom Widget for in-app video playback

In FlutterFlow, click Custom Code in the left navigation panel. Click + Add → Widget. Name it YouTubePlayerWidget. In the Dependencies field at the top of the code editor, type youtube_player_flutter and add the latest stable version (check pub.dev for the current version number — it is typically 9.x at time of writing). This widget wraps YouTube's official IFrame player, which is the only ToS-compliant way to play YouTube videos inside an app. Paste the Dart code below into the widget editor. The widget accepts a single parameter — videoId (String) — which is the 11-character YouTube video ID extracted from any YouTube URL (for example, in https://www.youtube.com/watch?v=dQw4w9WgXcQ, the ID is dQw4w9WgXcQ). Define the widget parameters in the Settings panel on the right: add a parameter named videoId of type String with no default value. Set the widget's default width to double.infinity and height to 220.0. IMPORTANT: Custom Widgets do not render in FlutterFlow's web Test Mode canvas. The preview will show a placeholder box. To test actual video playback, use Run Mode on a connected Android or iOS device, or create a build (APK/IPA). This is a Flutter platform limitation, not a bug in your code. Once saved, the widget appears in your widget palette. Place it on any page by going to the Widget tab → Custom Widgets → YouTubePlayerWidget. Bind the videoId parameter to the video ID you store in your Data Type from the YouTube API Call in Step 3.

youtube_player_widget.dart
1import 'package:flutter/material.dart';
2import 'package:youtube_player_flutter/youtube_player_flutter.dart';
3
4class YouTubePlayerWidget extends StatefulWidget {
5 final String videoId;
6 final double width;
7 final double height;
8
9 const YouTubePlayerWidget({
10 Key? key,
11 required this.videoId,
12 this.width = double.infinity,
13 this.height = 220.0,
14 }) : super(key: key);
15
16 @override
17 State<YouTubePlayerWidget> createState() => _YouTubePlayerWidgetState();
18}
19
20class _YouTubePlayerWidgetState extends State<YouTubePlayerWidget> {
21 late YoutubePlayerController _controller;
22
23 @override
24 void initState() {
25 super.initState();
26 _controller = YoutubePlayerController(
27 initialVideoId: widget.videoId,
28 flags: const YoutubePlayerFlags(
29 autoPlay: false,
30 mute: false,
31 enableCaption: true,
32 ),
33 );
34 }
35
36 @override
37 void dispose() {
38 _controller.dispose();
39 super.dispose();
40 }
41
42 @override
43 Widget build(BuildContext context) {
44 return SizedBox(
45 width: widget.width,
46 height: widget.height,
47 child: YoutubePlayer(
48 controller: _controller,
49 showVideoProgressIndicator: true,
50 progressIndicatorColor: Colors.red,
51 ),
52 );
53 }
54}

Pro tip: To extract the video ID from a full YouTube URL string stored in your data, use the YoutubePlayer.convertUrlToId() static method provided by the package: final id = YoutubePlayer.convertUrlToId(urlString) ?? ''; This handles youtube.com/watch?v=, youtu.be/, and embed/ URL formats automatically.

Expected result: A YouTubePlayerWidget appears in your Custom Widgets palette. Placing it on a page shows a placeholder in Test Mode. A device or APK build shows a functional YouTube IFrame player that plays the video matching the videoId parameter.

3

Create the FlutterFlow API Call group for YouTube Data API v3

Click API Calls in the left nav panel. Click + Add → Create API Group. Name it YouTube and set the Base URL to https://www.googleapis.com/youtube/v3. In the Headers section of the API Group, add a default header: Accept = application/json. Leave the Authorization header empty — API key authentication in YouTube goes as a URL query parameter, not a header. Now add your first API Call inside the group. Click + Add → Create API Call inside the YouTube group. Name it Get Playlist Videos. Set the Method to GET and the Endpoint to /playlistItems. In the Query Parameters section, add four parameters: - key: click the {{ }} toggle and add a variable, name it apiKey — you'll supply this at call time from FlutterFlow's App State or App Values - playlistId: add a variable named playlistId - part: enter the static value snippet,contentDetails - maxResults: enter 50 Add a second API Call named Get Video Stats. Method GET, endpoint /videos. Query parameters: key (variable apiKey), id (variable videoIds — this will be a comma-separated list of up to 50 video IDs), part (static: snippet,statistics,contentDetails). Add a third API Call named Get Channel Info. Method GET, endpoint /channels. Query parameters: key (variable apiKey), id (variable channelId), part (static: snippet,statistics,contentDetails). For each call, click Response & Test. In the Variables section, fill in your actual API key and a real playlist or channel ID, then click Test. Review the JSON response. Click Generate JSON Paths — FlutterFlow will offer to create response fields. Select the fields you need: for Get Playlist Videos, select items[].snippet.title, items[].snippet.thumbnails.medium.url, items[].contentDetails.videoId, items[].snippet.publishedAt. Click Save to create a Data Type from these paths. For Get Video Stats, select items[].id, items[].statistics.viewCount, items[].statistics.likeCount, items[].statistics.commentCount. For Get Channel Info, select items[].snippet.title, items[].statistics.subscriberCount, items[].statistics.viewCount, items[].statistics.videoCount.

api_call_config.txt
1// API Call config (reference — FlutterFlow generates this visually)
2// GET https://www.googleapis.com/youtube/v3/playlistItems
3// Query params:
4// key: {{ apiKey }}
5// playlistId: {{ playlistId }}
6// part: snippet,contentDetails
7// maxResults: 50
8// pageToken: {{ pageToken }} (optional, for pagination)
9
10// GET https://www.googleapis.com/youtube/v3/videos
11// Query params:
12// key: {{ apiKey }}
13// id: {{ videoIds }} (comma-separated, up to 50)
14// part: snippet,statistics,contentDetails
15
16// JSON Path examples for parsing playlistItems response:
17// Video ID: $.items[*].contentDetails.videoId
18// Title: $.items[*].snippet.title
19// Thumbnail: $.items[*].snippet.thumbnails.medium.url
20// Published: $.items[*].snippet.publishedAt
21
22// JSON Path examples for videos response:
23// View count: $.items[*].statistics.viewCount
24// Like count: $.items[*].statistics.likeCount
25// Duration: $.items[*].contentDetails.duration (ISO-8601, e.g. PT4M13S)
26// Note: duration needs parsing — PT4M13S = 4 minutes 13 seconds

Pro tip: Store your API key in FlutterFlow's App State as a persisted string constant rather than hardcoding it in each API Call variable field. Go to App State → + Add Field → name it youtubeApiKey → set type to String → check Persisted → save. Set the value on app init via a Set App State action. This lets you rotate the key in one place if needed.

Expected result: Three API Calls (Get Playlist Videos, Get Video Stats, Get Channel Info) appear inside the YouTube API group. Each test returns real data from YouTube, and FlutterFlow has generated Data Types for the fields you selected.

4

Build a video list page and bind API data to widgets

Create a new page for your video feed. Go to Pages → + Add Page → Blank. Name it VideoFeed. In the Action Flow Editor for the page's On Load event, add a Backend/API Call action pointing to Get Playlist Videos. Pass the playlistId as a constant (your specific playlist ID) and the apiKey from App State. Store the response in a Page State variable named playlistResponse. Drag a ListView widget onto the page from the widget panel. In the ListView's Generate Children section, bind the source to your Data Type for playlist items populated from the API response. Inside the list item template, add: - A CachedNetworkImage (or the standard Image widget) bound to items[].snippet.thumbnails.medium.url for the thumbnail - A Text widget bound to items[].snippet.title for the video title - A Text widget bound to items[].snippet.publishedAt (you can format this in FlutterFlow's string formatting options) Set each list item as a GestureDetector or add a tap action. On tap, navigate to a VideoDetail page and pass the contentDetails.videoId and snippet.title as page parameters. On the VideoDetail page, place your YouTubePlayerWidget Custom Widget at the top. Bind its videoId parameter to the videoId page parameter received from navigation. Below the player, add Text widgets for the video title and any other details. To add view counts and statistics, you need a second API call. In the VideoDetail page's On Load action flow, add a Backend/API Call for Get Video Stats, passing the videoId. Store the result and bind the view count, like count, and comment count to Text widgets on the page. Note on the ISO-8601 duration format: the contentDetails.duration field returns strings like PT4M13S (4 minutes, 13 seconds) or PT1H2M30S. FlutterFlow doesn't natively parse this — either display it raw or convert it in a Custom Function: parse out hours (H), minutes (M), and seconds (S) using string operations and format as '4:13' for display.

parse_youtube_duration.dart
1// Custom Function: parse ISO-8601 duration to readable format
2// Add in Custom Code → Functions
3String parseYouTubeDuration(String iso8601) {
4 final regex = RegExp(
5 r'PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?',
6 );
7 final match = regex.firstMatch(iso8601);
8 if (match == null) return iso8601;
9 final hours = int.tryParse(match.group(1) ?? '0') ?? 0;
10 final minutes = int.tryParse(match.group(2) ?? '0') ?? 0;
11 final seconds = int.tryParse(match.group(3) ?? '0') ?? 0;
12 if (hours > 0) {
13 return '$hours:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
14 }
15 return '$minutes:${seconds.toString().padLeft(2, '0')}';
16}

Pro tip: Use FlutterFlow's conditional visibility to show a loading spinner while the Get Playlist Videos API Call is in progress. Bind the spinner's visibility to the isLoading state of your API Call action, and the ListView's visibility to the inverse — this gives users immediate visual feedback that data is loading.

Expected result: The VideoFeed page loads a scrollable list of videos from the YouTube playlist with thumbnails and titles. Tapping a video navigates to VideoDetail where the youtube_player_flutter widget plays the video and statistics are displayed below.

5

Implement quota-aware design: replace search with playlist browsing and add caching

The single most important performance decision in a YouTube-connected FlutterFlow app is: never call search.list on user input events. A search.list call costs 100 quota units — with a 10,000-unit daily budget, your app can make only ~100 searches per day before hitting a 403 quotaExceeded error. Here is how to design around that ceiling. For browsing content you control (your own channel's videos), use the uploads playlist approach instead of search. In Get Channel Info, the response includes contentDetails.relatedPlaylists.uploads — this is a playlist ID for all of a channel's uploads. Fetch this playlist with Get Playlist Videos (1 unit per 50 videos) instead of search.list. Cache this playlist data in Firestore or Supabase so repeated app loads by different users all share a single API draw rather than each triggering a new call. If you do need keyword search (search.list), trigger it ONLY when the user explicitly taps a Search button — never on TextField onChange. Add a debounce of at least 1 second if you want type-ahead, and make the Search button the primary interaction. Store recent search results in Firestore (or Supabase) keyed by the search query string. Before calling the YouTube API, check if a cached result exists and is less than 1 hour old; if so, return the cached result instead. To implement caching in FlutterFlow: create a Firestore collection named youtube_cache with fields: query (String), results (JSON), cached_at (Timestamp). In your search action flow, first run a Firestore query checking for a matching entry less than 1 hour old (using a date comparison condition). If found, use the cached results. If not found, call the YouTube API, then write the results to youtube_cache. This pattern can stretch a 10,000-unit budget across hundreds of users. For teams building YouTube-integrated apps as a product feature, RapidDev's engineers have built quota management architectures like this for multiple FlutterFlow clients — you can book a free scoping call at rapidevelopers.com/contact to discuss your specific quota and caching needs.

index.js
1// Firebase Cloud Function proxy for OAuth private data
2// (Only needed for private channel management / analytics)
3// Deploy to: Firebase Console → Functions
4const functions = require('firebase-functions');
5const admin = require('firebase-admin');
6const { google } = require('googleapis');
7
8admin.initializeApp();
9
10exports.youtubePrivateData = functions.https.onRequest(async (req, res) => {
11 res.set('Access-Control-Allow-Origin', '*');
12 if (req.method === 'OPTIONS') {
13 res.set('Access-Control-Allow-Methods', 'GET');
14 res.set('Access-Control-Allow-Headers', 'Content-Type,Authorization');
15 return res.status(204).send('');
16 }
17 // Verify Firebase Auth token from FlutterFlow
18 const authHeader = req.headers.authorization || '';
19 const idToken = authHeader.replace('Bearer ', '');
20 try {
21 await admin.auth().verifyIdToken(idToken);
22 } catch (e) {
23 return res.status(401).json({ error: 'Unauthorized' });
24 }
25 // Use server-side OAuth credentials (stored in Cloud Functions config)
26 const oauth2Client = new google.auth.OAuth2(
27 process.env.YOUTUBE_CLIENT_ID,
28 process.env.YOUTUBE_CLIENT_SECRET,
29 process.env.YOUTUBE_REDIRECT_URI
30 );
31 oauth2Client.setCredentials({
32 refresh_token: process.env.YOUTUBE_REFRESH_TOKEN
33 });
34 const youtube = google.youtube({ version: 'v3', auth: oauth2Client });
35 const { endpoint, params } = req.query;
36 // Route to the requested YouTube endpoint
37 const response = await youtube.channels.list({
38 part: ['snippet', 'statistics'],
39 mine: true,
40 });
41 return res.json(response.data);
42});

Pro tip: Check your current quota usage daily while building: Google Cloud Console → APIs & Services → Dashboard → click YouTube Data API v3 → Quotas. The Metrics tab shows unit consumption by method, so you can see exactly which endpoints are being called most.

Expected result: Your app's search triggers only on button tap, recent search results are cached in Firestore, and the uploads playlist is used for channel browsing instead of search. Quota consumption in Google Cloud Console shows well under 10,000 units per day even with active user testing.

6

Proxy OAuth private data through Firebase Cloud Functions (optional, for private channel management)

If your app needs to access private YouTube data — such as your own channel's unlisted videos, comment moderation, or YouTube Analytics time-series metrics — you must use Google OAuth 2.0. The OAuth client secret cannot be placed in FlutterFlow's API Call group (it would ship in the compiled app), so this step walks through the Firebase Cloud Function proxy pattern. In Google Cloud Console, go to APIs & Services → Credentials → Create Credentials → OAuth 2.0 Client ID. Select Web application (not Android/iOS — the web type is needed for server-to-server use). Set the redirect URI to your Cloud Function's URL. Download the client ID and secret. In Firebase Console, create a Cloud Function (use the code snippet in this step as your starting template). Store the client secret, client ID, and a long-lived refresh token as environment variables using firebase functions:config:set in the Firebase CLI (not shown inline — RapidDev sets this up as a deployment step for clients). The function receives requests from FlutterFlow, verifies the user's Firebase Auth token, uses the stored credentials to call YouTube on the server, and returns the result. In FlutterFlow, create a new API Group named YouTubePrivate with the base URL pointing to your Cloud Function's HTTPS endpoint (visible in Firebase Console → Functions after deployment). Add API Calls within this group for the private operations you need — for example, GET /myChannel for channel stats with the mine=true parameter (which requires OAuth and can only be served from the proxy). Because the proxy returns the same JSON structure as the YouTube API, you can reuse the same Data Types and JSON Path bindings you set up in Step 3. For most apps — particularly read-only video browsing, channel pages, and playlist feeds — this step is entirely optional. An API key is sufficient for all public data, and most FlutterFlow YouTube integrations never need the proxy.

Pro tip: The YouTube Analytics API (youtubeanalytics.googleapis.com/v2) is a SEPARATE API from the YouTube Data API v3. Time-series view counts, traffic sources, and audience retention data come from Analytics, not Data. Enable both APIs in Cloud Console and create a second API Group in FlutterFlow (or proxy route) if you need Analytics data alongside the standard Data API.

Expected result: A Firebase Cloud Function is deployed that proxies authenticated YouTube API calls. FlutterFlow API Calls in the YouTubePrivate group hit the function URL, verify user identity, and return private channel data. No OAuth secret or refresh token exists anywhere in the FlutterFlow project.

Common use cases

Video-learning app that browses and plays a curated YouTube playlist

Build a FlutterFlow app that fetches all videos from one or more curated YouTube playlists, displays them in a scrollable ListView with thumbnails and titles, and plays the selected video inside the app using the youtube_player_flutter widget. This covers the full public-data path: API key, playlistItems endpoint (1 unit per call), and in-app IFrame playback — no backend proxy required.

FlutterFlow Prompt

An app with a home screen showing a vertical list of YouTube videos from a specific playlist ID, each card showing the thumbnail, title, and duration. Tapping a card opens a detail screen that plays the video in-app with a description below.

Copy this prompt to try it in FlutterFlow

Creator analytics dashboard that tracks channel performance

Build a FlutterFlow dashboard that shows a creator's YouTube channel statistics — subscriber count, total views, video count — alongside a list of recent uploads with per-video view counts, like counts, and engagement rates. Use the channels and videos endpoints (both ~1 unit each) and cache results in Firestore so the app doesn't drain quota when many users load the screen simultaneously.

FlutterFlow Prompt

A channel stats screen showing subscriber count, total views, and a list of the last 20 uploaded videos sorted by view count, with each row showing the thumbnail, title, publish date, view count, and likes. Refresh button at the top.

Copy this prompt to try it in FlutterFlow

Multi-channel video feed with keyword search

Build a FlutterFlow app that lets users search YouTube for videos on a topic using the search endpoint, view results in a list, and watch them in-app. Because search costs 100 quota units per call, trigger search only when the user taps a Search button — never on every keystroke — and cache recent results in Supabase to serve repeat searches from the database instead of re-querying YouTube.

FlutterFlow Prompt

A search screen with a text field and a Search button. Results show in a ListView with video thumbnails, titles, and channel names. Tapping a result plays the video in-app. Recent searches are shown below the search field for quick re-use.

Copy this prompt to try it in FlutterFlow

Troubleshooting

YouTube API returns 403 Forbidden with error reason 'quotaExceeded'

Cause: Your Google Cloud project has exhausted its 10,000 free quota units for the day. The most common cause is calling search.list repeatedly — each search call costs 100 units. A screen that runs search.list on load and is opened 100 times burns the entire daily budget. Quota resets at midnight Pacific Time.

Solution: Replace search.list with playlist-based browsing wherever possible: fetch the uploads playlist ID from the channels endpoint and use playlistItems.list (1 unit per call) to list a channel's videos. Add Firestore or Supabase caching for all read results so multiple users share a single quota draw. Trigger search only on explicit button tap, not on text input changes. Check current quota usage at Google Cloud Console → APIs & Services → Dashboard → YouTube Data API v3. If your use case genuinely requires more quota, apply for an increase via the quota increase form linked in the Cloud Console.

YouTubePlayerWidget shows a blank box or placeholder — video never plays

Cause: Custom Widgets do not render in FlutterFlow's web Test Mode canvas. The canvas shows a grey placeholder for any Custom Widget by design. This is not an error in your code — it is a Flutter/FlutterFlow platform limitation.

Solution: Test video playback using Run Mode on a connected device, or generate an APK (Android) or TestFlight build (iOS) and install it on a real device. Ensure the youtube_player_flutter dependency is correctly added in the Dependencies field of the Custom Widget editor (not in Custom Actions). Also verify the videoId parameter you're passing is exactly 11 characters — a full YouTube URL passed instead of just the ID will cause the player to fail silently. Use YoutubePlayer.convertUrlToId(url) in a Custom Function if your data stores full URLs.

typescript
1// Custom Function to extract video ID from any YouTube URL format
2String? extractYouTubeId(String url) {
3 return YoutubePlayer.convertUrlToId(url);
4}

API Call returns 400 Bad Request or empty items[] array for playlistItems

Cause: The playlist ID is incorrect, the playlist is private, or the playlist belongs to a channel not associated with the API key's project. A common mistake is passing a channel ID (UCxxxxxxxxxx) where a playlist ID (PLxxxxxxxxxx or UUxxxxxxxxxx for uploads) is required. The playlistItems endpoint does not accept channel IDs.

Solution: First verify you are passing a playlist ID (starts with PL, UU, LL, or RD), not a channel ID. To get the uploads playlist ID for a channel, call GET /channels?id={channelId}&part=contentDetails and read the response field items[0].contentDetails.relatedPlaylists.uploads. Paste this playlist ID into your Get Playlist Videos call. Confirm the playlist is set to Public in YouTube Studio — private and unlisted playlists require OAuth, not just an API key.

API Call returns 400 with 'keyInvalid' or 401 with 'badRequest'

Cause: The API key is missing from the query parameters, has been deleted or restricted in Google Cloud Console, or has never been saved correctly in FlutterFlow's variable binding. A common FlutterFlow mistake is setting the key as a static string in the API Group headers instead of as a query parameter — YouTube API keys must be query params (key=YOUR_KEY), not Authorization headers.

Solution: Open the API Call in FlutterFlow → check the Query Parameters section. Confirm a parameter named 'key' is present with a value bound to your API key variable. Do not add the key as a header — YouTube ignores it there and returns 401. Verify the key is active and not deleted: Google Cloud Console → APIs & Services → Credentials. If you recently restricted the key by application, make sure the SHA-1 fingerprint and package name match exactly what your FlutterFlow project uses.

Best practices

  • Always use playlistItems.list or videos.list for channel content browsing instead of search.list — playlist and video endpoints cost ~1 quota unit while search costs 100 units, making the difference between 10,000 daily API calls and 100
  • Cache all YouTube API responses in Firestore or Supabase with a timestamp, and serve cached data to app users rather than making a live YouTube API call on every screen load — this stretches your quota across all users instead of each user consuming their own portion
  • Restrict your YouTube API key in Google Cloud Console to your app's package name and SHA-1 fingerprint (mobile) or referrer domain (web) — this prevents unauthorized quota consumption if the key is ever extracted from the compiled binary
  • Test playback on a real device or APK build from the start of development — the youtube_player_flutter Custom Widget shows only a placeholder in FlutterFlow's Test Mode canvas, and waiting until the end to test playback leads to late-stage debugging surprises
  • Trigger search.list only on explicit user action (a Search button tap), never on TextField onChange — add a 500ms debounce minimum if type-ahead search is required, and display a warning UI when the user is close to quota exhaustion
  • Store your YouTube API key in FlutterFlow's App State as a single persisted constant rather than hardcoding it in each API Call variable field — this makes key rotation a one-step update instead of a multi-page find-and-replace
  • Use the YoutubePlayer.convertUrlToId() static method from the youtube_player_flutter package to normalize video IDs from any YouTube URL format before passing them to the player widget — this prevents silent failures when your data stores full URLs instead of bare IDs
  • Never attempt to extract or play raw YouTube video stream URLs directly — this violates YouTube's Terms of Service and risks your developer account being suspended; always use the IFrame player via youtube_player_flutter for compliant playback

Alternatives

Frequently asked questions

Do I need a Google account or YouTube channel to use the YouTube Data API in FlutterFlow?

You need a Google account to create a Google Cloud project and generate an API key — you don't need your own YouTube channel for public data reads. Any video, channel, or playlist on YouTube can be fetched with an API key alone, regardless of whether you own content. A YouTube channel is only required if you want to surface your own channel's statistics or manage your own uploads through the API.

Can I play YouTube videos in a FlutterFlow web app?

Yes, but with a caveat. The youtube_player_flutter Custom Widget uses the IFrame player, which works in web builds. However, web builds have a CORS consideration — the IFrame itself loads from YouTube's domain, so playback is not affected by CORS. The API Calls to googleapis.com work fine from FlutterFlow's server proxy layer. Note that the widget will not show in FlutterFlow's Test Mode canvas for web either — test in a deployed web build or native device build.

What happens when I hit the 10,000 quota unit daily limit?

Once the daily quota is exhausted, all YouTube Data API calls from your Google Cloud project return a 403 error with the reason quotaExceeded. Your app will show empty lists or error states until the quota resets at midnight Pacific Time. To prevent this from reaching users, implement Firestore or Supabase caching so API calls only happen when cached data is stale, and avoid using the expensive search.list endpoint in production flows that fire automatically.

Can I use the YouTube API to download or save videos to the device?

No — downloading YouTube video files is prohibited by YouTube's Terms of Service, and the YouTube Data API v3 does not return downloadable stream URLs. The API provides metadata (titles, thumbnails, descriptions, statistics) and the player is embedded via the official IFrame. Any package that claims to extract raw YouTube stream URLs violates ToS and can result in developer account termination. Use the youtube_player_flutter widget for all in-app playback.

Is an API key enough for showing a YouTube channel's subscriber count and video statistics?

Yes — subscriber count, view count, video count, like count, and comment count are all public statistics available with just an API key. Call GET /channels?id={channelId}&part=statistics or GET /videos?id={videoId}&part=statistics and parse the statistics object. The only scenario where an API key is insufficient is when the channel has explicitly hidden its subscriber count (subscriberCount will be 0 or absent in that case), or when you need private analytics data like traffic sources and audience retention, which require OAuth via the separate YouTube Analytics API.

How do I show videos from multiple YouTube channels in one FlutterFlow list?

Store your list of channel IDs in a Firestore collection or Supabase table. On app load, iterate over the channel IDs and call GET /channels for each to get their uploads playlist ID, then fetch each channel's recent videos via playlistItems. Because the videos.list endpoint accepts up to 50 comma-separated video IDs per call, you can batch the statistics fetch: collect all video IDs from all channels, split them into groups of 50, and make one videos.list call per group. Combine the results in a local Data Type list sorted by published date to create a unified multi-channel feed.

RapidDev

Talk to an Expert

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

Book a free consultation

Integrations are where projects stall

We wire FlutterFlow integrations like this one every week — auth, webhooks, edge cases included. Fixed-price builds from $13K, delivered in 6–10 weeks.

Talk to an integration engineer

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.