mrkeyoor.com_
Thu 06 Aug 02:40 UTC
PyPIWeb Backendupdated 06 Aug 2026

google-api-python-client

One Python package that talks to every Google API that publishes a discovery document: Drive, Gmail, Calendar, Sheets, YouTube, Admin SDK, Cloud Compute and hundreds more. You call build('drive', 'v3', credentials=creds) and get back a Resource object whose methods are generated at runtime from a JSON description of the API, so drive.files().list(pageSize=10).execute() works without any Drive-specific code in the library. Since 2.0 those discovery documents ship inside the package rather than being downloaded at build() time, which made startup reliable and pushed the install past 50 MB. Google states plainly in the README that the library is complete and in maintenance mode: critical bugs and security issues get fixed, new features do not land, and for new code the maintainers point you at the per-service Cloud Client Libraries instead.

Verdict

The only practical way to reach most Google Workspace APIs from Python, and it works fine once credentials are sorted. Treat it as frozen infrastructure: no new features are coming, it is synchronous and untyped, and anything on Google Cloud proper belongs in a Cloud Client Library instead.

API stability5/5The 2.x surface has not broken since 2021 and maintenance mode guarantees it will not. Weekly releases only refresh the bundled discovery documents, so upgrades are close to risk-free
Docs3/5The repo docs cover auth, batching, media, pagination and thread safety clearly, but generated per-API reference is thin, several pages still use Python 2 print syntax and dead Google Plus examples, and the real method arguments live in each API's own Google documentation
Maintenance3/5Officially supported by Google with automated weekly releases and roughly 21 open issues plus about 32 open PRs, but the README states no new features will be added and steers new users elsewhere
Ecosystem5/544.7M weekly downloads, 8.9k stars, and it is the client every Google Workspace quickstart, tutorial and Stack Overflow answer is written against

Use it if

  • You need a Google API that has no dedicated Cloud Client Library, which covers most of Workspace: Gmail, Calendar, Drive, Sheets, Docs, Admin SDK, YouTube Data
  • You call several different Google APIs from one service and want a single dependency and a single auth story rather than one package per API
  • You want batch requests, where up to 1000 calls go out in one HTTP round trip through new_batch_http_request()
  • You need resumable uploads or chunked downloads for large Drive or YouTube media without writing the resumable protocol yourself
Skip it if

Setup reality

pip install google-api-python-client brings httplib2, uritemplate, google-auth, google-auth-httplib2 and google-api-core, and the roughly 15 MB wheel unpacks to more than 50 MB on disk because every discovery document is bundled. The install is the easy part. Credentials are where the time goes: API keys for public data, an installed-app OAuth flow with a client_secret.json and a locally cached token for user data, a service account for server-to-server, and domain-wide delegation on top of that if you are impersonating Workspace users. Getting a 403 usually means you forgot to enable the API in the Cloud Console project, and scope changes require deleting the cached token before the new scope takes effect. Two more traps: pass static_discovery=True (or leave it at the default with credentials supplied) so build() does not try a network call, and pass cache_discovery=False if you see the noisy file_cache is only supported with oauth2client warning. Finally, if you multi-thread anything, wire up the per-thread Http() pattern from day one instead of retrofitting it after the first mystery failure.

Patterns

Build a service client with an API keybuild-service-api-key

from googleapiclient.discovery import build

youtube = build("youtube", "v3", developerKey=API_KEY, static_discovery=True)

response = (
    youtube.search()
    .list(q="python", part="snippet", maxResults=5)
    .execute()
)
for item in response["items"]:
    print(item["snippet"]["title"])

API keys only reach public data. static_discovery=True guarantees the bundled discovery document is used and no network call happens during build().

Authenticate as a service accountservice-account-auth

from google.oauth2 import service_account
from googleapiclient.discovery import build

SCOPES = ["https://www.googleapis.com/auth/drive.readonly"]

creds = service_account.Credentials.from_service_account_file(
    "service-account.json", scopes=SCOPES
)

# Workspace domain-wide delegation: act as a real user
creds = creds.with_subject("user@example.com")

drive = build("drive", "v3", credentials=creds)

A service account has its own empty Drive. Without with_subject() and domain-wide delegation configured by an admin, files().list() returns nothing rather than an error.

Run the installed-app OAuth flow with a cached tokenoauth-installed-app

import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build

SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"]
creds = None

if os.path.exists("token.json"):
    creds = Credentials.from_authorized_user_file("token.json", SCOPES)

if not creds or not creds.valid:
    if creds and creds.expired and creds.refresh_token:
        creds.refresh(Request())
    else:
        flow = InstalledAppFlow.from_client_secrets_file(
            "client_secret.json", SCOPES
        )
        creds = flow.run_local_server(port=0)
    with open("token.json", "w") as fh:
        fh.write(creds.to_json())

gmail = build("gmail", "v1", credentials=creds)

google-auth-oauthlib is a separate install. If you change SCOPES you must delete token.json, otherwise the cached token keeps the old scope and calls fail with 403 insufficient permissions.

Page through a large listpaginate-results

files = drive.files()
request = files.list(pageSize=100, fields="nextPageToken, files(id, name)")

while request is not None:
    response = request.execute()
    for f in response.get("files", []):
        print(f["id"], f["name"])
    request = files.list_next(request, response)

Call execute() once per loop iteration. list_next() returns None when there are no more pages, and it needs nextPageToken to be present in your fields mask.

Ask for only the fields you needpartial-response-fields

response = (
    drive.files()
    .list(q="mimeType='application/pdf'", fields="files(id,name,size)")
    .execute()
)

The server returns the full resource by default. A fields mask cuts response size and often makes the difference on rate-limited APIs; slashes nest, commas separate.

Handle API errors properlyhandle-http-errors

import json
from googleapiclient.errors import HttpError

try:
    drive.files().get(fileId="missing").execute()
except HttpError as error:
    status = error.resp.status
    detail = json.loads(error.content).get("error", {})
    if status == 404:
        print("not found")
    elif status in (403, 429) and detail.get("errors", [{}])[0].get("reason") == "rateLimitExceeded":
        print("back off and retry")
    else:
        raise

A 403 from Google means either a quota problem or a permission problem, and only the reason field inside error.content tells you which.

Upload a large file in chunksresumable-upload

from googleapiclient.http import MediaFileUpload

media = MediaFileUpload(
    "report.pdf", mimetype="application/pdf", chunksize=1024 * 1024, resumable=True
)
request = drive.files().create(
    body={"name": "report.pdf"}, media_body=media, fields="id"
)

response = None
while response is None:
    status, response = request.next_chunk()
    if status:
        print(f"uploaded {int(status.progress() * 100)}%")
print(response["id"])

Chunks for files over 256 KB must be a multiple of 256 KB. Media uploads cannot be put inside a batch request.

Download a file to memory or diskdownload-media

import io
from googleapiclient.http import MediaIoBaseDownload

request = drive.files().get_media(fileId=file_id)
buffer = io.BytesIO()
downloader = MediaIoBaseDownload(buffer, request)

done = False
while not done:
    status, done = downloader.next_chunk()
    print(f"{int(status.progress() * 100)}%")

with open("out.bin", "wb") as fh:
    fh.write(buffer.getvalue())

get_media() only works for binary files. Google Docs, Sheets and Slides need files().export_media() with a target mimeType instead.

Send many calls in one HTTP requestbatch-requests

def on_response(request_id, response, exception):
    if exception is not None:
        print(request_id, "failed:", exception)
    else:
        print(request_id, response["name"])

batch = drive.new_batch_http_request(callback=on_response)
for i, file_id in enumerate(file_ids):
    batch.add(drive.files().get(fileId=file_id), request_id=str(i))
batch.execute()

Maximum 1000 calls per batch, and execute() blocks until every callback has run. A failed sub-request does not fail the batch, so you must inspect the exception argument.

Use the client from multiple threadsthread-safe-clients

import httplib2
import google_auth_httplib2
import googleapiclient.http
from googleapiclient import discovery

def build_request(http, *args, **kwargs):
    new_http = google_auth_httplib2.AuthorizedHttp(creds, http=httplib2.Http())
    return googleapiclient.http.HttpRequest(new_http, *args, **kwargs)

authorized_http = google_auth_httplib2.AuthorizedHttp(creds, http=httplib2.Http())
service = discovery.build(
    "drive", "v3", requestBuilder=build_request, http=authorized_http
)

httplib2.Http() is not thread-safe. Without this pattern, concurrent execute() calls on one service object interleave on the same socket and return each other's data.

Silence the file_cache discovery warningsilence-cache-warning

service = build(
    "sheets", "v4", credentials=creds, cache_discovery=False
)

The file_cache is only supported with oauth2client<4.0.0 warning is harmless but fills logs; since discovery documents are bundled, disabling the cache costs nothing.

Get autocomplete and type checkingadd-type-stubs

# pip install google-api-python-client-stubs

from googleapiclient.discovery import build

# mypy and Pyright now resolve the resource chain:
drive = build("drive", "v3", credentials=creds)
result = drive.files().list(pageSize=10).execute()

Stubs are a separate community package, not shipped by Google, and they only cover the more popular APIs; anything else stays Any.

Alternatives

PackageRegistryPick it when
google-cloud-storagePyPIAn example of the per-service Cloud Client Libraries the maintainers recommend for new Google Cloud code
gspreadPyPIYou only touch Google Sheets and want a small API instead of raw Sheets v4 request bodies
pydrive2PyPIDrive-only work where you want file objects and a simpler auth wrapper over this same client