pyAirtable Documentation

repository·main·Indexed 21 days ago

https://github.com/gtalarico/pyairtable

A Python client for the Airtable API that provides a programmatic way to interact with bases, tables, and records. It features a hierarchical object model (Api, Base, Table, Workspace), a Pythonic ORM for mapping records to objects, and a CLI for interacting with API endpoints. The library includes specialized modules for managing schemas, webhooks, and comments, as well as tools for constructing Airtable-compatible formula strings.

Tokens
16.7K
Snippets
53
Records
81
Agent score
74%

What's inside pyAirtable

  1. Access Enterprise features

    main

    pyAirtable provides classes and methods to interact with enterprise organizations. Note that these methods require an Enterprise plan. If you attempt to call these methods against a base or workspace that is not part of an enterprise organization, Airtable will return a 404 error, and pyAirtable will include a reminder in the exception to check your billing plan.

    Available entry points include:

    • pyairtable.Api.enterprise() to access enterprise-level operations.
    • pyairtable.Base.collaborators() and pyairtable.Base.shares() for base-specific enterprise management.
    • pyairtable.Workspace.collaborators() for workspace-level management.
    • pyairtable.Enterprise.info() to retrieve organization information.
    # Example of accessing the enterprise object via the API
    from pyairtable import Api
    api = Api(api_key='...')
    enterprise = api.enterprise()
  2. Use the core pyairtable hierarchy (Api, Base, Table, Workspace)

    main

    The primary way to interact with Airtable is through a hierarchical object model. You typically start by initializing an Api instance with your API key, then navigate down to a Base, and finally to a specific Table to perform CRUD operations. For enterprise users, Enterprise and Workspace objects provide higher-level management capabilities.

    # Conceptual usage pattern
    from pyairtable import Api
    
    api = Api('your_api_key')
    base = api.get('app_id')
    table = base.get('table_id')
    # Now you can interact with the table
  3. Use the pyairtable ORM (Object-Relational Mapper)

    main
    For a more Pythonic experience, use the pyairtable.orm module. By subclassing pyairtable.orm.Model, you can map Airtable records to Python objects. This allows you to work with typed fields and structured data rather than raw dictionaries. When saving models, the operation returns a SaveResult object which provides details about the persistence operation.
  4. Work with Linked Records using LinkField and SingleLinkField

    main

    Use LinkField and SingleLinkField to traverse relationships between tables. These fields fetch the linked records automatically upon first access.

    • LinkField: Always returns a list of records (even if only one exists in Airtable).
    • SingleLinkField: Use this if you expect exactly one linked record. It respects the Airtable prefersSingleRecordLink configuration.

    Linked models are not refreshed until you call Model.fetch() on the instance.

    from pyairtable.orm import Model, fields as F
    
    class Person(Model):
        class Meta: ...
        name = F.TextField("Name")
        company = F.SingleLinkField("Company", "Company")
    
    class Company(Model):
        class Meta: ...
        name = F.TextField("Name")
        people = F.LinkField("People", Person)
    
    # Usage:
    # person = Person.from_id("...")
    # person.company  # Fetches the Company record
    # person.company.name
  5. Handle cyclical links in bidirectional models

    main

    When modeling bidirectional links (e.g., Company has Employees, and Employee has a Company), you may encounter circular dependencies. To resolve this, you can define the target model in the field using one of three methods:

    1. Fully qualified module path: Provide a string like "your.module.Model".
    2. Class name string: Provide just the class name string (e.g., "Person") if it's in the same module.
    3. Sentinel value: Use F.LinkSelf to point to the model where the field is defined.

    LinkField arguments: LinkField(attribute_name, airtable_field_name, model_definition).

    from pyairtable.orm import Model, fields as F
    
    class Company(Model):
        class Meta: ...
        name = F.TextField("Name")
        employees = F.LinkField("Employees", "path.to.Person")  # Option 1: Full path
    
    class Person(Model):
        class Meta: ...
        name = F.TextField("Name")
        company = F.SingleLinkField[Company]("Company", Company)
        manager = F.SingleLinkField["Person"]("Manager", "Person")  # Option 2: Class name
        reports = F.LinkField["Person"]("Reports", F.LinkSelf)      # Option 3: LinkSelf
  6. Manage Airtable schemas and webhooks

    main

    The library provides specialized modules for managing advanced Airtable features:

    • pyairtable.models.schema: Used for interacting with and managing the structure of your Airtable bases.
    • pyairtable.models.webhook: Used to manage webhooks for receiving real-time notifications about changes in your Airtable data.
    • pyairtable.models.comment: Used for managing comments within Airtable records.
  7. Optimize performance with Memoization

    main

    To avoid making hundreds of API calls when traversing nested linked records, you can use memoization. This pre-fetches and reuses model instances.

    Global/Model Level

    Set memoize = True in the Meta configuration of a model to enable it by default.

    Per-call Level

    Pass memoize=True to retrieval methods to pre-fetch records into the cache.

    Memoization Support Table

    Retrieval functionReuses saved models?Calls API?
    Model.all()NeverAlways
    Model.first()NeverAlways
    Model.from_record()NeverNever
    Model.from_id()YesYes (unless fetch=False)
    Model.from_ids()YesYes (unless fetch=False)
    LinkField.populate()YesYes (unless lazy=True)
    SingleLinkField.populate()YesYes (unless lazy=True)
    # Pre-fetching all books and authors to avoid N+1 queries
    books = Book.all(memoize=True)
    authors = Author.all(memoize=True)
    
    for author in authors:
        for book in author.books:
            print(book.title) # Uses memoized instance
  8. Construct formulas using pyAirtable syntax

    main

    pyAirtable allows you to build Airtable formulas using Python syntax at runtime. These formula objects are automatically converted into the correct Airtable API string format when passed to methods like Table.all(). This abstraction prevents manual string manipulation and handles complex types like dates correctly.

    from datetime import date
    from pyairtable.formulas import AND, GTE, Field, match
    
    formula = AND(
        match("Customer", 'Alice'),
        GTE(Field("Delivery Date"), date.today())
    )
    # formula will be converted to: "AND({Customer}='Alice', {Delivery Date}>=DATETIME_PARSE('2023-12-10'))"
  9. How Airtable webhooks work with pyAirtable

    main

    Airtable's Webhooks API allows you to receive programmatic notifications when Airtable data or metadata changes. The integration workflow with pyAirtable follows these steps:

    1. Create: Use Base.add_webhook to register a new webhook with a specific URL.
    2. Receive: Airtable sends POST notifications to your provided URL.
    3. Validate: Use WebhookNotification.from_request to verify the authenticity of the incoming notification using the request body, the X-Airtable-Content-MAC header, and your webhook secret.
    4. Process: Use Webhook.payloads to retrieve the actual data changes (payloads).

    Important: Cursor Management To avoid processing the same data multiple times or missing data during interruptions, you must persist the cursor of the webhook payload. When calling webhook.payloads(cursor=...), you should store the latest payload.cursor + 1 in a database so your next execution knows where to start.

    from flask import Flask, request
    from pyairtable import Api
    from pyairtable.models import WebhookNotification
    
    # Example workflow for receiving and processing webhooks
    @app.route("/airtable-webhook", methods=["POST"])
    def airtable_webhook():
        body = request.data
        header = request.headers["X-Airtable-Content-MAC"]
        secret = app.config["AIRTABLE_WEBHOOK_SECRET"]
        
        # 1. Validate the notification
        event = WebhookNotification.from_request(body, header, secret)
        
        airtable = Api(app.config["AIRTABLE_API_KEY"])
        webhook = airtable.base(event.base.id).webhook(event.webhook.id)
        
        # 2. Retrieve the last saved cursor from your database
        cursor = int(your_database.get(event.webhook, 0)) + 1
    
        # 3. Iterate through new payloads using the cursor
        for payload in webhook.payloads(cursor=cursor):
            process_payload(payload)
            # 4. Persist the next cursor to prevent duplicate processing
            your_database.set(event.webhook, payload.cursor + 1)
    
        return ("", 204)
  10. CLI Command Shortcuts

    main

    The pyAirtable CLI supports partial command matching. If a partial command is provided and there is only one unambiguous completion, the CLI will execute the full command.

    Example:

    • pyairtable e will be interpreted as pyairtable enterprise.

    Warning:

    • pyairtable b is ambiguous (could be base or bases) and will not work as a shortcut.
  11. Quickstart with Api and Table classes

    main

    The pyairtable.Api class manages the connection to Airtable, and the pyairtable.Table class provides methods to interact with specific tables.

    Common operations include:

    • api.table(base_id, table_id): Connects to a specific table.
    • table.all(): Retrieves all records.
    • table.create(fields): Creates a new record.
    • table.update(record_id, fields): Updates an existing record.
    • table.delete(record_id): Deletes a record.
    import os
    from pyairtable import Api
    
    # Initialize the API connection using an environment variable
    api = Api(os.environ['AIRTABLE_API_KEY'])
    
    # Access a specific table
    table = api.table('appExampleBaseId', 'tblExampleTableId')
    
    # Retrieve all records
    records = table.all()
    print(records)
    
    # Create a new record
    new_record = table.create({"Name": "Bob"})
    print(new_record)
    
    # Update an existing record
    table.update("recwAcQdqwe21asdf", {"Name": "Robert"})
    
    # Delete a record
    table.delete("recwAcQdqwe21asdf")