How to generate Airtable OAuth authentication token using Python

I’m trying to set up OAuth authentication for Airtable using Python. I have working code for Google’s OAuth that uses google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file to handle the browser authentication flow and save tokens to a file.

from google_auth_oauthlib.flow import InstalledAppFlow

def authenticate_user():
    user_creds = None
    # Check if token file exists with saved credentials
    if os.path.exists(AUTH_TOKEN_FILE):
        user_creds = Credentials.from_authorized_user_file(AUTH_TOKEN_FILE, PERMISSION_SCOPES)
    
    # Handle authentication if no valid credentials
    if not user_creds or not user_creds.valid:
        if user_creds and user_creds.expired and user_creds.refresh_token:
            user_creds.refresh(Request())
        else:
            auth_flow = InstalledAppFlow.from_client_secrets_file(CLIENT_CONFIG_FILE, PERMISSION_SCOPES)
            user_creds = auth_flow.run_local_server(port=0)
        
        # Store credentials for future use
        with open(AUTH_TOKEN_FILE, 'w') as auth_file:
            auth_file.write(user_creds.to_json())
    
    return user_creds

The issue is that Airtable doesn’t provide a downloadable credentials file like Google does. I tried creating my own JSON config file with the OAuth parameters I got from Airtable:

{"installed":
    {"client_id":"my_client_id",
     "auth_uri":"https://airtable.com/oauth2/v1/authorize",
     "token_uri":"https://oauth2.googleapis.com/token",
     "auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs",
     "client_secret":"my_secret",
     "redirect_uris":["http://localhost"]}}

But Airtable returns an error about the client ID being invalid, even though it’s correct. The redirect URI http://localhost works fine with Google’s flow.

I also tried using other OAuth libraries with PKCE support:

from oauthlib.oauth2 import WebApplicationClient
from requests_oauthlib import OAuth2Session
import secrets
import base64
import hashlib

def create_pkce_codes():
    """ Create code_verifier and code_challenge for PKCE authentication. """
    verifier = base64.urlsafe_b64encode(secrets.token_bytes(32)).rstrip(b"=").decode('utf-8')
    challenge = base64.urlsafe_b64encode(hashlib.sha256(verifier.encode('utf-8')).digest()).rstrip(b"=").decode('utf-8')
    return verifier, challenge

def start_oauth_flow():
    app_id = 'my_app_id'
    callback_url = 'https://localhost:8080/'  
    permissions = ['schema.bases:read']

    # Create PKCE pair
    verifier_code, challenge_code = create_pkce_codes()

    # Setup OAuth client
    oauth_client = WebApplicationClient(app_id)
    session = OAuth2Session(client=oauth_client, redirect_uri=callback_url, scope=permissions)

    # Build authorization URL
    auth_url, session_state = session.authorization_url(
        url='https://airtable.com/oauth2/v1/authorize',
        code_challenge_method="S256",
        code_challenge=challenge_code
    )

    print("Visit this URL to authorize:", auth_url)
    callback_response = input('Enter the complete redirect URL: ')

    # Get access token
    access_token = session.fetch_token(
        token_url="https://api.airtable.com/oauth/token",
        authorization_response=callback_response,
        client_secret=None,
        code_verifier=verifier_code)

    print("Token received:", access_token)

This approach gets me closer but I’m getting an INVALID_API_VERSION error when trying to exchange the authorization code for an access token. The redirect URL shows up but can’t be accessed since it’s localhost.

I need help with either:

  1. Finding the right redirect URI configuration that works with both Airtable and the Google OAuth library
  2. How to specify the correct API version when using OAuth2Session with Airtable

Hit this exact same issue last year building an integration. You’re mixing Google’s OAuth patterns with Airtable’s setup.

First - that token URI in your JSON config is wrong. You’re pointing to Google’s token endpoint instead of Airtable’s. Should be https://airtable.com/oauth2/v1/token not the googleapis one.

Second - don’t try forcing Google’s OAuth library to work with Airtable. I wasted days on that. Airtable uses PKCE which Google’s flow handles differently.

Your PKCE code throws API version errors because you’re mixing v1 and v2 endpoints. Use https://airtable.com/oauth2/v1/token for token exchange, not the /api/ path.

Here’s what worked for me:

import requests
import secrets
import base64
import hashlib
from urllib.parse import urlencode, parse_qs, urlparse

def get_airtable_token():
    client_id = 'your_client_id'
    redirect_uri = 'http://localhost:8080/callback'
    
    # Generate PKCE
    code_verifier = base64.urlsafe_b64encode(secrets.token_bytes(32)).decode('utf-8').rstrip('=')
    code_challenge = base64.urlsafe_b64encode(
        hashlib.sha256(code_verifier.encode('utf-8')).digest()
    ).decode('utf-8').rstrip('=')
    
    # Auth URL
    params = {
        'client_id': client_id,
        'redirect_uri': redirect_uri,
        'response_type': 'code',
        'scope': 'data.records:read data.records:write schema.bases:read',
        'code_challenge': code_challenge,
        'code_challenge_method': 'S256'
    }
    
    auth_url = f"https://airtable.com/oauth2/v1/authorize?{urlencode(params)}"
    print(f"Go to: {auth_url}")
    
    callback_url = input("Paste the callback URL: ")
    code = parse_qs(urlparse(callback_url).query)['code'][0]
    
    # Exchange for token
    token_data = {
        'client_id': client_id,
        'code': code,
        'redirect_uri': redirect_uri,
        'grant_type': 'authorization_code',
        'code_verifier': code_verifier
    }
    
    response = requests.post('https://airtable.com/oauth2/v1/token', data=token_data)
    return response.json()

Key differences: stick to v1 endpoints, don’t mix client secrets with PKCE, use proper scope format.

The localhost callback works fine - just copy the URL from your browser after the redirect fails.