title: Youtube Analytics API returns 403 forbidden even if token is valid
body: <p>I have an issue with access to Youtube Analytics API for random youtube channels. </p>
  
  <p>After a successful authorization with following scopes:</p>
  
  <ul>
  <li><a href="https://www.googleapis.com/auth/youtube.readonly" rel="noreferrer">https://www.googleapis.com/auth/youtube.readonly</a></li>
  <li><a href="https://www.googleapis.com/auth/yt-analytics.readonly" rel="noreferrer">https://www.googleapis.com/auth/yt-analytics.readonly</a></li>
  </ul>
  
  <p>I'm saving the <code>token</code> and <code>refresh token</code> in the database. Everything works well for some time. After a while (eg. three months) when my app makes a request, Google returns 403:</p>
  
  <pre><code>{
    "error": {
      "errors": [
        {
          "domain": "global",
          "reason": "forbidden",
          "message": "Forbidden"
        }
      ],
      "code": 403,
      "message": "Forbidden"
    }
  }
  </code></pre>
  
  <p>but only for Youtube Analytics API, other endpoints in Youtube Data API works fine with this token. 
  That happen for random accounts (channels). Owners of this channels didn't revoke access to my app, didn't change account password etc. </p>
  
  <p>This issue affects about 40% of all channels in my application (the time when Youtube Analytics API stops working is different, from 1 to 6 months after obtaining OAuth2 token). Then I have to send them periodically a new authorization url.</p>
  
  <p>Where is the problem?</p>
  
  <p>This is how I generate an auth url and make requests:</p>
  
  <ul>
  <li><p>Auth URL:</p>
  
  <pre><code>flow = client.flow_from_clientsecrets(
      secret_file_path,
      scope=["https://www.googleapis.com/auth/youtube.readonly",
             "https://www.googleapis.com/auth/yt-analytics.readonly"],
      redirect_uri=redirect_url,
      prompt="consent"
  )
  flow.params["access_type"] = "offline"
  url = flow.step1_get_authorize_url(state=state)
  </code></pre></li>
  <li><p>Stats request:</p>
  
  <pre><code>auth = client.OAuth2Credentials.from_json(credentials_from_db)
  http_auth = auth.authorize(httplib2.Http())
  api = discovery.build("youtubeAnalytics", "v1", http=http_auth,
                     cache_discovery=False)
  api.reports().query(
      ids="channel==%s" % channel_id,
      metrics="estimatedMinutesWatched",
      dimensions="video",
      start_date=start_date,
      end_date=end_date,
      max_results=20,
      filters="video=={}".format(",".join(video_ids))
  ).execute(http=http)
  </code></pre></li>
  </ul>
  
  <p>I'm using google-api-python-client 1.6.5</p>
  
  <p><strong>Edit</strong></p>
  
  <p>I attached screenshot while debugging requests to google API using <code>google-api-python-client</code>. Here's what's going on:</p>
  
  <ol>
  <li>Retrieving channel's base stats using Youtube Data API</li>
  <li>Retrieving channel's advanced stats using Youtube Analytics API</li>
  </ol>
  
  <p>My point is that, the refresh token is successfully exchanged in both cases but works only with Youtube Data API. I purposely exchange it twice. The same result is when I access only Youtube Analytics API with one successful token exchange (without calling Youtube Data API).</p>
  
  <p>And the most fun part is that, this code works for some time (a couple weeks or months) and then stops :-)</p>
  
  <p><a href="https://i.stack.imgur.com/jOMIh.png" rel="noreferrer"><img src="https://i.stack.imgur.com/jOMIh.png" alt="API requests"></a></p>