title: YouTube Data API v3 Refresh Token Keeps Expiring on App with Publishing Status Set to Testing
body: <p><strong>WHAT I'M TRYING TO DO:</strong></p>
  <p>I'm trying to build a Python 3.9 program to make daily calls of the YouTube Data API v3 using OAuth2 credentials (set to the &quot;testing&quot; publishing status, as a &quot;web application&quot; type, and &quot;external&quot; user type), by storing the refresh token to get a new access token each time I make a unique call.</p>
  <p>I've been using the <a href="https://developers.google.com/youtube/v3/guides/authentication" rel="nofollow noreferrer">YouTube Data API v3 official documentation</a>, the Python code examples from the <a href="https://github.com/googleapis/googleapis" rel="nofollow noreferrer">Google API repository on GitHub</a>, along with this OAuth token solution I found from  <a href="https://youtu.be/vQQEaSnQ_bs" rel="nofollow noreferrer">Corey Schafer on YouTube</a>.</p>
  <p><strong>WHAT I'VE TRIED SO FAR:</strong></p>
  <p>Here's my Python code (I've scrambled the playlist ID for the sake of anonymity, but the code runs fine if you put in your own playlist ID for a channel that you have credentials for):</p>
  <pre><code># YouTube Data API v3
  # Pulling data for the brand account
  
  import os
  import pickle
  from google_auth_oauthlib.flow import InstalledAppFlow
  from google.auth.transport.requests import Request
  from googleapiclient.discovery import build
  
  credentials = None
  
  
  # youtube_data_token_brand.pickle stores the user's credentials from previously successful logins
  if os.path.exists('youtube_data_token_brand.pickle'):
      print('Loading Credentials From File...')
      with open('youtube_data_token_brand.pickle', 'rb') as token:
          credentials = pickle.load(token)
  
  # If there are no valid credentials available, then either refresh the token or log in.
  if not credentials or not credentials.valid:
      if credentials and credentials.expired and credentials.refresh_token:
          print('Refreshing Access Token...')
          credentials.refresh(Request())
      else:
          print('Fetching New Tokens...')
          flow = InstalledAppFlow.from_client_secrets_file(
              'client_secrets_youtube_data_brand.json',
              scopes=[
                  'https://www.googleapis.com/auth/youtube.readonly'
              ]
          )
  
          flow.run_local_server(port=8080, prompt='consent',
                                authorization_prompt_message='')
          credentials = flow.credentials
  
          # Save the credentials for the next run
          with open('youtube_data_token_brand.pickle', 'wb') as f:
              print('Saving Credentials for Future Use...')
              pickle.dump(credentials, f)
  
  youtube = build('youtube', 'v3', credentials=credentials)
  
  request = youtube.playlistItems().list(
          part=&quot;status, contentDetails&quot;, playlistId='UUlG34RnfYmCsNFgxmTmYjPA', maxResults=28
      )
  
  response = request.execute()
  
  for item in response[&quot;items&quot;]:
      vid_id = (item[&quot;contentDetails&quot;][&quot;videoId&quot;])
      yt_link = f&quot;https://youtu.be/{vid_id}&quot;
      print(yt_link)
  </code></pre>
  <p><strong>THE RESULTS I'VE BEEN GETTING:</strong></p>
  <p>My program runs for about a week, then I get the following errors (again, I've redacted part of the file path for anonymity):</p>
  <pre><code>/Users/…/PycharmProjects/GoogleAPIs/HelloYouTubeDataAPIOAuth.py
  
  Loading Credentials From File...
  
  Refreshing Access Token...
  
  Traceback (most recent call last):
    File &quot;/Users/.../PycharmProjects/GoogleAPIs/HelloYouTubeDataAPIOAuth.py&quot;, line 23, in &lt;module&gt;
      credentials.refresh(Request())
    File &quot;/Users/.../PycharmProjects/GoogleAPIs/venv/lib/python3.9/site-packages/google/oauth2/credentials.py&quot;, line 200, in refresh
      access_token, refresh_token, expiry, grant_response = _client.refresh_grant(
    File &quot;/Users/.../PycharmProjects/GoogleAPIs/venv/lib/python3.9/site-packages/google/oauth2/_client.py&quot;, line 248, in refresh_grant
      response_data = _token_endpoint_request(request, token_uri, body)
    File &quot;/Users/.../PycharmProjects/GoogleAPIs/venv/lib/python3.9/site-packages/google/oauth2/_client.py&quot;, line 124, in _token_endpoint_request
      _handle_error_response(response_body)
    File &quot;/Users/.../PycharmProjects/GoogleAPIs/venv/lib/python3.9/site-packages/google/oauth2/_client.py&quot;, line 60, in _handle_error_response
      raise exceptions.RefreshError(error_details, response_body)
  google.auth.exceptions.RefreshError: ('invalid_grant: Bad Request', '{\n  &quot;error&quot;: &quot;invalid_grant&quot;,\n  &quot;error_description&quot;: &quot;Bad Request&quot;\n}')
  
  Process finished with exit code 1
  </code></pre>
  <p>I can circumvent these errors by deleting the 'youtube_data_token_brand.pickle' file from my directory and re-running the program (which then asks me to log in with my google account and manually reauthorizing access via the OAuth2 steps).</p>
  <p>This leads me to believe that my refresh token is expiring (I swear I read somewhere in the documentation that it shouldn't expire until access is revoked - which I have not done - but I can't find that note anymore after repeatedly searching for it).</p>
  <p>Interestingly, I'm able to run the same program for a different YouTube account I control, and that account hasn't experienced the same refresh token error issues. I've also been able to use the identical token.pickle approach for storing the refresh token to other Google APIs (Google Analytics, YouTube Analytics, etc) and not experienced this issue with any of them.</p>
  <p>Thanks in advance for any help you can provide!</p>