# Google APIs gebruiken in Python [[TOC]] Python is de favoriet voor scripts, data-pipelines en automatisering. De officiele Google-clientbibliotheek voor Python werkt met elke Workspace-API via hetzelfde fluent patroon. Of je nu een eenmalig script schrijft of een serverproces draait, de opzet is altijd hetzelfde. In dit artikel installeer je de bibliotheken, regel je authenticatie en doe je je eerste calls, inclusief paginering en nette foutafhandeling. ## Installatie Je hebt twee pakketten nodig: de clientbibliotheek en de auth-bibliotheek. Voor OAuth-flows komt daar nog een helper bij. ```bash pip install google-api-python-client google-auth google-auth-oauthlib ``` :::info title="Pin je versies en draai een audit" Pin de versies in je `requirements.txt` of `pyproject.toml` en draai `pip-audit` na installatie. Zo weet je dat je geen kwetsbare versies binnenhaalt en blijft je build reproduceerbaar. Recente versies van `google-api-python-client` (de 2.x-reeks) cachen de discovery-documenten in het pakket zelf, waardoor de installatie groter is maar `build()` niet meer afhankelijk is van een netwerk-call naar de discovery-service. ::: ## Authenticeren met een service account Voor server-naar-server gebruik je `google.oauth2.service_account` met de gewenste scopes. Dit is de aangewezen methode voor achtergrondprocessen zonder gebruiker aan de knoppen. ```python from google.oauth2 import service_account from googleapiclient.discovery import build SCOPES = ['https://www.googleapis.com/auth/spreadsheets.readonly'] creds = service_account.Credentials.from_service_account_file( '/secrets/sa-key.json', scopes=SCOPES ) service = build('sheets', 'v4', credentials=creds) ``` :::warn title="Behandel je sleutel als een wachtwoord" Een service-account-JSON geeft directe toegang. Commit de sleutel nooit in Git, plaats hem buiten je webroot en lees hem bij voorkeur uit een secret manager in plaats van een bestand op schijf. Beperk de scopes tot precies wat het script nodig heeft. ::: ## Authenticeren met OAuth Voor data van een ingelogde gebruiker gebruik je een interactieve flow die een token opslaat voor hergebruik. ```python from google_auth_oauthlib.flow import InstalledAppFlow from google.oauth2.credentials import Credentials flow = InstalledAppFlow.from_client_secrets_file('client_secret.json', SCOPES) creds = flow.run_local_server(port=0) ``` :::tip title="Hergebruik de token bij de volgende run" Sla de verkregen credentials op als JSON en herlaad ze met `Credentials.from_authorized_user_file`. Zo hoeft de gebruiker niet elke keer opnieuw in te loggen, want de refresh token blijft geldig totdat hij wordt ingetrokken of verloopt. ::: ## Je eerste call Het patroon is fluent: je bouwt een verzoek op en sluit af met `execute()`. :::howto title="Zo doe je je eerste call" 1. Bouw de service met `build(naam, versie, credentials=creds)`. 2. Navigeer naar de resource, bijvoorbeeld `service.spreadsheets().values()`. 3. Roep de methode aan met named parameters. 4. Sluit af met `.execute()` om het verzoek te versturen. 5. Lees het resultaat uit als dictionary. ::: ```python result = service.spreadsheets().values().get( spreadsheetId=SHEET_ID, range='A1:C10' ).execute() rijen = result.get('values', []) ``` ## Paginering Lijst-endpoints retourneren een `nextPageToken`. Veel resources bieden een `list_next`-helper die dit voor je afhandelt, zodat je niet zelf met tokens hoeft te jongleren. ```python request = service.files().list(pageSize=100) while request is not None: response = request.execute() verwerk(response.get('files', [])) request = service.files().list_next(request, response) ``` ## Foutafhandeling :::warn title="Vang HttpError op en gebruik backoff" Vang `HttpError` uit `googleapiclient.errors` op. De statuscode zit in `error.resp.status`. Bij `429` of bij `403` met `rateLimitExceeded` moet je opnieuw proberen met exponential backoff. Bouw dit niet pas in als het al misgaat in productie. ::: ```python from googleapiclient.errors import HttpError try: service.files().list().execute() except HttpError as e: print(f'Fout {e.resp.status}: {e}') ``` Een eenvoudige backoff houdt je script overeind bij tijdelijke limieten: ```python import time from googleapiclient.errors import HttpError def met_backoff(verzoek, pogingen=5): for poging in range(pogingen): try: return verzoek.execute() except HttpError as e: if e.resp.status in (429, 403) and poging < pogingen - 1: time.sleep(2 ** poging) continue raise ``` ## Veelgestelde vragen :::faq ### Wat is het verschil tussen google-api-python-client en de cloud-bibliotheken? De `google-api-python-client` dekt de discovery-API's zoals de meeste Workspace-diensten. De cloud-bibliotheken (`google-cloud-*`) zijn specifiek voor Google Cloud-diensten en hebben een eigen, vaak idiomatischer idioom met getypte objecten in plaats van dictionaries. ### Hoe stel ik impersonation (domain-wide delegation) in? Roep `creds.with_subject('gebruiker@domein.nl')` aan op een service-account-credential waarvoor domain-wide delegation is geactiveerd in de Admin-console. Het script handelt dan namens die gebruiker. ### Kan ik async werken? De officiele client is synchroon. Voor async draai je calls in een threadpool (bijvoorbeeld via `asyncio.to_thread`) of gebruik je een community-bibliotheek met asyncio-ondersteuning. ### Hoe weet ik welke methoden beschikbaar zijn? Raadpleeg de API-referentie van de betreffende dienst, of gebruik de ingebouwde discovery: de service-objecten spiegelen de API-structuur exact, dus `dir()` op een resource toont de beschikbare methoden. ### Waarom is mijn installatie zo groot geworden? De 2.x-reeks bundelt de discovery-documenten in het pakket zodat `build()` geen netwerk-call meer hoeft te doen. Dat maakt de installatie tientallen megabytes groter, maar betrouwbaarder en sneller bij het opstarten. ::: Met deze opzet schrijf je in Python betrouwbare, herhaalbare integraties met elke Workspace-API.