2

I've been trying to use a service account to download files from Google Drive via the API and some examples I found in the documentation, and for a while it worked fine. A few days ago though, I started getting this error:

{
 "error": {
  "errors": [
   {
    "domain": "usageLimits",
    "reason": "dailyLimitExceededUnreg",
    "message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.",
    "extendedHelp": "https://code.google.com/apis/console"
   }
  ],
  "code": 403,
  "message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup."
 }
}

Well, actually I get a different 403 in my console (only files with binary content can be downloaded), but when I click on the link in that error, it leads to the above JSON.

EDIT: The above bolded part was the real issue, and then when I clicked the link it was unauthorized. My mistake!

This is the code I'm running, basically pulled from their docs:

def get_drive_service(key_file):
    sa_creds = service_account.Credentials.from_service_account_file(key_file)
    scoped_creds = sa_creds.with_scopes(SCOPES)
    drive_service = build('drive', 'v3', credentials=scoped_creds)
    return drive_service

def download_report(drive_service, id):
    request = drive_service.files().get_media(fileId=id)
    print(request.to_json())
    fh = io.BytesIO()
    downloader = MediaIoBaseDownload(fh, request, chunksize=1024*1024)
    done = False
    while done is False:
        status, done = downloader.next_chunk()
        print("Download %d%%." % int(status.progress() * 100))
    return fh

drive_service = get_drive_service(<key_file>)
buffer = download_report(drive_service, <file_id>)

I found some similar questions on SO, and tried everything that seemed relevant, but nothing has worked. The API is enabled, and the quotas are not an issue, when I use files().list().execute() I get exactly the list of files I would expect, and as I said the code used to work fine.

I'm flummoxed. Can anyone please help me understand what I'm missing here?

kentm
  • 31
  • 1
  • 4

1 Answers1

3

Daily Limit for Unauthenticated Use Exceeded.

This error message is normally the result of not applying the authorization credentials in your code. I couldn't spot any issues off the bat with your code though. The first thing i would like to suggest is that you double check that you have service account credentials added in your project and not the wrong type. However i would have expected a different error message if this was the issue.

Try this its based upon the offical samples manage downloads

from apiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials


SCOPES = ['https://www.googleapis.com/auth/drive']
KEY_FILE_LOCATION = '<REPLACE_WITH_JSON_FILE>'


def initialize_drive():
  """Initializes an drive service object.

  Returns:
    An authorized drive service object.
  """
  credentials = ServiceAccountCredentials.from_json_keyfile_name(
      KEY_FILE_LOCATION, SCOPES)

  # Build the service object.
  service = build('drive', 'v3', credentials=credentials)

  return service

def download_report(drive_service, id):
  file_id = '0BwwA4oUTeiV1UVNwOHItT0xfa2M'
  request = drive_service.files().get_media(fileId=file_id)
  fh = io.BytesIO()
  downloader = MediaIoBaseDownload(fh, request)
  done = False
while done is False:
  status, done = downloader.next_chunk()
  print "Download %d%%." % int(status.progress() * 100)
 return fh

only files with binary content can be downloaded

Remember there are two types of files on drive. Google drive mimetype files which need to be downloaded using the export method and all other binary type files. Binary files are downloaded using the method you are using now.

def main():
 service = initialize_drive()

 buffer = download_report(service, <file_id>)

if __name__ == '__main__':
  main()

Export method

file_id = '1ZdR3L3qP4Bkq8noWLJHSr_iBau0DNT4Kli4SxNc2YEo'
request = drive_service.files().export_media(fileId=file_id,
                                             mimeType='application/pdf')
fh = io.BytesIO()
downloader = MediaIoBaseDownload(fh, request)
done = False
while done is False:
    status, done = downloader.next_chunk()
    print "Download %d%%." % int(status.progress() * 100)
Linda Lawton - DaImTo
  • 106,405
  • 32
  • 180
  • 449
  • For the first download method, the code is the same except you used a different method of building credentials; I tried and the results are the same though. And I thought you might be onto something regarding the Export method (it's an XLSX file), but when I tried that I got a 403 error saying the file was too large (it's not; it's < 30kb), and when I click the link it's back to the old unregistered use error. So somehow this authentication just isn't working. Thanks for trying though! – kentm Mar 15 '22 at 11:50
  • Open your credentials json file and make sure that it says its a service account. Service accounts really should just work. As for the file size. do a file.list and double check that im not sure how you are checking size. – Linda Lawton - DaImTo Mar 15 '22 at 12:11
  • Wait check this [tasks.py#L50](https://github.com/googleapis/google-api-python-client/blob/main/samples/service_account/tasks.py#L50) – Linda Lawton - DaImTo Mar 15 '22 at 12:18
  • 1
    My credentials are 100% for a service account. Let me try that `credentials.authorize()` snippet you found... – kentm Mar 15 '22 at 12:30
  • Using `credentials.authorize()` gives the same results, unfortunately. – kentm Mar 15 '22 at 12:42
  • This is a service account created on google developer console right not one that was created over on google cloud. seriously cant be that you would get a different error. Post an issue on the github issue forum maybe someone else has an idea. im out of ideas. It really should just work. – Linda Lawton - DaImTo Mar 15 '22 at 13:15
  • 1
    Hi @DalmTo thanks, you're right, the authentication wasn't the issue in the end, it was that the content wasn't binary. A non-binary file was accidentally dropped into that folder, and was always getting tried first, which was throwing that 403 non-binary error. Then when I clicked the link in that error, it wasn't using the full request, which is how I got the unauthorized issue. The SA was working fine, as you said. Thanks for your help! – kentm Mar 19 '22 at 17:44
  • Do a file list first always double check the mime type before you try to download. Or catch that error and ignore it. – Linda Lawton - DaImTo Mar 20 '22 at 11:42
  • Trying the export_media option for mimeType="image/jpeg" I'm receiving a 403 error saying "Export only supports Docs editor files"... ```Details: "[{'domain': 'global', 'reason': 'fileNotExportable', 'message': 'Export only supports Docs Editors files.'}]">``` – Ryan Farber Sep 15 '22 at 21:21
  • 1
    @RyanFarber open a new question with your code and i will be able to explain better. In short export is only used for google drive type files. You dont need to export an image just use file.get and download it. – Linda Lawton - DaImTo Sep 16 '22 at 08:25
  • @DalmTo yup, I was confused when the get_media method didn't write a file to disk...but with fresh eyes realized it saves the file to a variable in memory and I have to download it via `fh.seek(0); with open(fh, "wb") as fid; fid.write(fh.read())` (although I don't write one-liners irl :) – Ryan Farber Sep 16 '22 at 15:44