| Title: | Pure R Client for the 'Airtable' REST API |
|---|---|
| Description: | A modern R client for the 'Airtable' REST API. Provides low-level wrappers for all API endpoints, high-level functions for reading, writing, upserting, and syncing records, and a DBI-compliant interface for RStudio/Positron connection pane integration. |
| Authors: | Noam Ross [aut, cre] (author with heavy LLM assistance), Darko Bergant [aut] (Original airtabler package), Collin Schwantes [aut] (airtabler fork maintainer) |
| Maintainer: | Noam Ross <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.0.0.9000 |
| Built: | 2026-06-05 19:49:43 UTC |
| Source: | https://github.com/noamross/airtable2 |
Returns this session's best-effort tally of API calls made against a
workspace during the current calendar month (UTC). Airtable free/team plans
cap each workspace at roughly 1000 calls per month and provide no way to
query the remaining quota, so airtable2 keeps its own counter on disk.
air_api_usage(workspace = NULL)air_api_usage(workspace = NULL)
workspace |
Workspace ID (starts with |
A list of class air_api_usage with elements workspace_id,
count, since, and last. Has a {cli} print method.
## Not run: air_api_usage("wspXXXXXXXXXXXXXX") ## End(Not run)## Not run: air_api_usage("wspXXXXXXXXXXXXXX") ## End(Not run)
The Airtable REST API returns a temporary download URL in the url field
of every attachment object. This URL is served from
airtableusercontent.com and expires after approximately 2 hours.
Do not save or share the raw API url as a permanent link — it will stop
working shortly after it is issued.
air_attachment_preview_url(attachment)air_attachment_preview_url(attachment)
attachment |
A single attachment metadata list (as returned by
|
A character vector of NA_character_, one element per attachment.
The function always returns NA because a stable viewer URL cannot be
constructed from API metadata alone. See the @section above for the
correct manual workflow.
Airtable maintains a separate attachment viewer URL (served from
airtable.com) that does not expire (unless the attachment or record is
deleted). This stable URL requires the recipient to have access to the base
or interface in which the attachment lives.
Crucially, the stable viewer URL cannot be derived from any field that the
REST API returns (id, url, filename, size, type, thumbnails).
It is only obtainable by navigating to the record in the Airtable web app,
clicking the attachment to open its preview, and copying the URL from the
browser address bar.
Because no stable URL can be constructed programmatically from attachment
metadata, air_attachment_preview_url() always returns NA_character_ and
emits an informative message directing users to the correct workflow.
# Suppose you have attachment metadata from air_read(): att <- list( id = "attABCDEF123456", url = "https://v5.airtableusercontent.com/v3/some_signed_path", filename = "report.pdf", size = 204800L, type = "application/pdf" ) # The temporary download URL (expires in ~2 hours): att$url # Attempting to get a stable preview URL — always NA with an explanation: ## Not run: air_attachment_preview_url(att) ## End(Not run) # To obtain a permanent, shareable link for a colleague: # 1. Open the record in the Airtable web app. # 2. Click the attachment to open its viewer. # 3. Copy the URL from the browser address bar (it is from airtable.com). # 4. Share that URL — it does not expire as long as the attachment exists # and the recipient has access to the base.# Suppose you have attachment metadata from air_read(): att <- list( id = "attABCDEF123456", url = "https://v5.airtableusercontent.com/v3/some_signed_path", filename = "report.pdf", size = 204800L, type = "application/pdf" ) # The temporary download URL (expires in ~2 hours): att$url # Attempting to get a stable preview URL — always NA with an explanation: ## Not run: air_attachment_preview_url(att) ## End(Not run) # To obtain a permanent, shareable link for a colleague: # 1. Open the record in the Airtable web app. # 2. Click the attachment to open its viewer. # 3. Copy the URL from the browser address bar (it is from airtable.com). # 4. Share that URL — it does not expire as long as the attachment exists # and the recipient has access to the base.
Navigates to the Airtable web interface for the given resource. Automatically resolves IDs from connection objects, determines the resource type from the ID prefix, or resolves human-readable names to IDs.
air_browse(id = NULL, base_id = NULL, table_id = NULL, ...)air_browse(id = NULL, base_id = NULL, table_id = NULL, ...)
id |
Character, connection, or
|
base_id |
Character or |
table_id |
Character or |
... |
Additional arguments passed to |
If id is NULL (the default), air_browse() opens the session default
base set via air_set_base() or the airtable2.base_id option.
Name resolution precedence:
If id is a human name and a base_id is available (explicit arg or
session default), id is treated as a table name and resolved via
at_get_schema(). Case-sensitive first; falls back to case-insensitive
with a warning if needed.
If id is a human name and no base context is available, id is
treated as a base name and resolved via at_list_bases().
If no match is found, cli_abort() is called with a helpful message
listing how to view valid names.
The URL that was opened (invisibly).
## Not run: # Open the session default base (requires air_set_base() or option) air_browse() # Open a base by ID air_browse("appXXXXXX") # Open a workspace air_browse("wspXXXXXX") # Open a table within a base (base_id is a proper formal, not ...) air_browse("tblXXXXXX", base_id = "appXXXXXX") # Open a view air_browse("viwXXXXXX", base_id = "appXXXXXX", table_id = "tblXXXXXX") # Resolve a table by name (requires base context) air_browse("My Table", base_id = "appXXXXXX") # Resolve a base by name (no base context) air_browse("My Base") ## End(Not run)## Not run: # Open the session default base (requires air_set_base() or option) air_browse() # Open a base by ID air_browse("appXXXXXX") # Open a workspace air_browse("wspXXXXXX") # Open a table within a base (base_id is a proper formal, not ...) air_browse("tblXXXXXX", base_id = "appXXXXXX") # Open a view air_browse("viwXXXXXX", base_id = "appXXXXXX", table_id = "tblXXXXXX") # Resolve a table by name (requires base context) air_browse("My Table", base_id = "appXXXXXX") # Resolve a base by name (no base context) air_browse("My Base") ## End(Not run)
A convenience wrapper around DBI::dbConnect() using the airtable2()
driver. When base is specified, the connection shows that base's tables
directly. When base is omitted, all accessible bases are shown as schemas
in the connection pane. Use bases to restrict the pane to a specific
subset of bases.
air_connect( base = NULL, bases = NULL, .token = NULL, include_views = FALSE, .connect_code = NULL )air_connect( base = NULL, bases = NULL, .token = NULL, include_views = FALSE, .connect_code = NULL )
base |
Character. A Base ID (starts with |
bases |
Character vector of Base IDs or names. When supplied, the
connection pane shows only those bases (as schemas). Cannot be combined
with |
.token |
Character. Airtable Personal Access Token (resolved via
|
include_views |
Logical. If |
.connect_code |
Character. Optional custom reconnect code for the IDE
connection pane. Defaults to a |
An AirtableConnection object.
## Not run: # Connect to a specific base by name con <- air_connect(base = "Project Tracker") # Connect to all accessible bases con <- air_connect() # Show only selected bases in the pane con <- air_connect(bases = c("appXXXXXX", "appYYYYYY")) ## End(Not run)## Not run: # Connect to a specific base by name con <- air_connect(base = "Project Tracker") # Connect to all accessible bases con <- air_connect() # Show only selected bases in the pane con <- air_connect(bases = c("appXXXXXX", "appYYYYYY")) ## End(Not run)
A convenience wrapper around at_delete_records() with messaging.
air_delete(record_ids, table, base_id = NULL, .token = NULL, progress = NULL)air_delete(record_ids, table, base_id = NULL, .token = NULL, progress = NULL)
record_ids |
Character vector of record IDs to delete. |
table |
Table name or ID. |
base_id |
Base ID (e.g., |
.token |
Personal access token (resolved via |
progress |
Logical or |
Invisible NULL. Side effect: deletes records.
## Not run: air_delete(c("recABC", "recDEF"), "Contacts", "appXXXXXX") ## End(Not run)## Not run: air_delete(c("recABC", "recDEF"), "Contacts", "appXXXXXX") ## End(Not run)
Runs through canonical airtable2 operations against a BollardsForArt demo
base, pausing at each step so you can watch changes appear in the browser.
If no base_id is provided and none is set as the session default, calls
air_demo_setup() first.
air_demo( base_id = NULL, workspace_id = Sys.getenv("AIRTABLE_WORKSPACE_ID"), .token = NULL )air_demo( base_id = NULL, workspace_id = Sys.getenv("AIRTABLE_WORKSPACE_ID"), .token = NULL )
base_id |
Base ID to use. If |
workspace_id |
Passed to |
.token |
API token (see |
Open the Airtable base in a browser alongside the console. The walkthrough
pauses with <Enter> prompts whenever you need to switch to a different
table, and sleeps two seconds between steps so changes can appear.
Steps covered:
Read all Artists with a progress bar
Write one new artist; read back showing airtable_created_time
Write 120 community supporters in 12 batches – progress bar is clearly visible; read back over 2 pages to demonstrate read pagination
Bulk-upsert 30 artists with a progress bar
Sync back to the original 15 with a progress bar (watch deletions)
Upsert a new Engagement Score column into Artists from R
Upload an image attachment to a Project record
Link artists to projects via multipleRecordLinks
Left-join Airtable columns into a local R tibble
View the base schema
Seed a _metadata table with air_meta_init(), edit table/column names
as rows in Airtable, then apply with air_meta_sync()
Connect via the DBI interface: DBI::dbConnect(), DBI::dbListTables(),
DBI::dbReadTable(), DBI::dbWriteTable()
View API usage
All operations keyed on Name are idempotent; re-running will not
accumulate duplicate records.
Invisibly returns a list of results from each step.
## Not run: # Run with an existing demo base (set via air_set_base() or env var) air_set_base("appXXXXXXXXXXXXXX") air_demo() # Or create a new demo base first (needs AIRTABLE_WORKSPACE_ID) air_demo(workspace_id = Sys.getenv("AIRTABLE_WORKSPACE_ID")) ## End(Not run)## Not run: # Run with an existing demo base (set via air_set_base() or env var) air_set_base("appXXXXXXXXXXXXXX") air_demo() # Or create a new demo base first (needs AIRTABLE_WORKSPACE_ID) air_demo(workspace_id = Sys.getenv("AIRTABLE_WORKSPACE_ID")) ## End(Not run)
Creates a new Airtable base with tables and sample records that exercise all the key field types and airtable2 features. The demo base is themed around BollardsForArt, a fictional creative-arts advocacy nonprofit that installs unauthorized public art, fights for arts funding, runs community workshops, and tracks their campaigns and artists.
air_demo_setup( workspace_id = Sys.getenv("AIRTABLE_WORKSPACE_ID"), name = "bollardsforart_demo", .token = NULL )air_demo_setup( workspace_id = Sys.getenv("AIRTABLE_WORKSPACE_ID"), name = "bollardsforart_demo", .token = NULL )
workspace_id |
Workspace ID (defaults to AIRTABLE_WORKSPACE_ID env var).
Find yours in the browser URL: |
name |
Base name (default: |
.token |
API token (see |
The demo base contains:
Artists table: text, number, checkbox, single/multi-select, date, email (15+ rows with diverse international artists)
Projects table: text, number, date, single-select, attachments, linked records to Artists (15+ rows with imaginative installation names)
Supporters table: text, email, date – starts empty; air_demo() step 3
bulk-writes 120 records to demonstrate the progress bar over 12 batches
Grants table: text, number, date, single-select, linked records to Projects (15+ rows of funding sources and applications)
Fields include descriptions (column metadata) set via at_update_field().
Project records have image attachments uploaded via at_upload_attachment().
Linked-records fields connect Projects to Artists and Grants to Projects.
This function creates a real Airtable base and consumes several API calls. The base cannot be deleted via the API on free/team-tier accounts; clean up manually via the Airtable web interface.
The base ID (invisibly). Prints a summary of what was created.
## Not run: # Requires AIRTABLE_WORKSPACE_ID and AIRTABLE_API_KEY env vars base_id <- air_demo_setup() air_set_base(base_id) air_read("Artists") ## End(Not run)## Not run: # Requires AIRTABLE_WORKSPACE_ID and AIRTABLE_API_KEY env vars base_id <- air_demo_setup() air_set_base(base_id) air_read("Artists") ## End(Not run)
Exports the full schema and all table data from a base. By default,
attachments are downloaded to disk (attachments = "file") since the
purpose of a dump is to create a full backup.
air_dump( base_id, dir = NULL, format = c("list", "json", "csv"), attachments = c("file", "meta", "blob"), .token = NULL )air_dump( base_id, dir = NULL, format = c("list", "json", "csv"), attachments = c("file", "meta", "blob"), .token = NULL )
base_id |
Base ID (e.g., |
dir |
Directory to write files to. When
|
format |
One of |
attachments |
How to handle attachment fields: |
.token |
Personal access token (resolved via |
For format = "list": a named list with schema and a tibble per
table. For format = "json" or "csv": the directory path (invisibly).
## Not run: # Full backup with attachments air_dump("appXXXXXX", dir = "backup/") # Quick dump without downloading attachments air_dump("appXXXXXX", dir = "backup/", attachments = "meta") # CSV dump (flattened for spreadsheet compatibility) air_dump("appXXXXXX", dir = "backup/", format = "csv") ## End(Not run)## Not run: # Full backup with attachments air_dump("appXXXXXX", dir = "backup/") # Quick dump without downloading attachments air_dump("appXXXXXX", dir = "backup/", attachments = "meta") # CSV dump (flattened for spreadsheet compatibility) air_dump("appXXXXXX", dir = "backup/", format = "csv") ## End(Not run)
Inverse of air_flatten_collaborator().
air_expand_collaborator(x, pattern = "(.+) <(.+)>")air_expand_collaborator(x, pattern = "(.+) <(.+)>")
x |
A character vector (e.g., |
pattern |
Regex with two capture groups (name, email).
Default: |
A list of named lists with name and email.
Inverse of air_flatten_multiselect().
air_expand_multiselect(x, sep = NULL)air_expand_multiselect(x, sep = NULL)
x |
A character vector of delimited values. |
sep |
Delimiter to split on. Default |
A list of character vectors.
Convenience function for constructing field configurations.
air_field_template(name, type, description = NULL, options = NULL)air_field_template(name, type, description = NULL, options = NULL)
name |
Field name. |
type |
Field type (e.g., |
description |
Optional field description. |
options |
Optional field-specific options (list). See Airtable docs for available options per type. |
A list suitable for use in air_table_template() or
at_create_field().
air_field_template("Status", "singleSelect", options = list(choices = list( list(name = "Active"), list(name = "Inactive") )) )air_field_template("Status", "singleSelect", options = list(choices = list( list(name = "Active"), list(name = "Inactive") )) )
Generic that dispatches on the air_* S3 class of a column, applying the
appropriate per-type flattener. Plain (already-flat) vectors are returned
unchanged.
air_flatten(x, ...)air_flatten(x, ...)
x |
A column, typically an |
... |
Passed to the underlying flattener, e.g. |
A character vector (or x unchanged for non-air_* input).
Flatten an attachments list-column to a summary string
air_flatten_attachments(x, field = "filename", sep = NULL)air_flatten_attachments(x, field = "filename", sep = NULL)
x |
A list-column where each element is a data frame or list of attachment objects. |
field |
Which attachment field to extract (e.g., |
sep |
Delimiter. Default |
A character vector.
Flatten a collaborator list-column to strings
air_flatten_collaborator(x, format = "{name} <{email}>")air_flatten_collaborator(x, format = "{name} <{email}>")
x |
A list-column where each element is a named list with |
format |
A glue-style template. Available fields: |
A character vector.
Flatten a record-links list-column to delimited strings
air_flatten_links(x, sep = NULL)air_flatten_links(x, sep = NULL)
x |
A list-column where each element is a character vector of record IDs. |
sep |
Delimiter. Default |
A character vector.
Flatten a multi-select list-column to delimited strings
air_flatten_multiselect(x, sep = NULL)air_flatten_multiselect(x, sep = NULL)
x |
A list-column where each element is a character vector. |
sep |
Delimiter to join values. Default |
A character vector.
Fetches a remote Airtable table and joins it with a local data frame.
These are convenience wrappers around air_read() plus base-R merge().
The ... are forwarded to air_read() (e.g. formula, fields).
air_left_join(x, table, base_id = NULL, by = NULL, ..., .token = NULL) air_inner_join(x, table, base_id = NULL, by = NULL, ..., .token = NULL) air_full_join(x, table, base_id = NULL, by = NULL, ..., .token = NULL)air_left_join(x, table, base_id = NULL, by = NULL, ..., .token = NULL) air_inner_join(x, table, base_id = NULL, by = NULL, ..., .token = NULL) air_full_join(x, table, base_id = NULL, by = NULL, ..., .token = NULL)
x |
A local data frame. |
table |
Table name or ID. |
base_id |
Base ID (e.g., |
by |
Character vector of column name(s) to join on. If |
... |
Additional arguments passed to |
.token |
Personal access token (resolved via |
A tibble.
## Not run: scores <- tibble::tibble(Name = c("Alice", "Bob"), Score = c(90, 85)) air_left_join(scores, "Contacts", "appXXXX", by = "Name") ## End(Not run)## Not run: scores <- tibble::tibble(Name = c("Alice", "Bob"), Score = c(90, 85)) air_left_join(scores, "Contacts", "appXXXX", by = "Name") ## End(Not run)
air_left_join_upload() is the complement of air_left_join(). Instead of
pulling remote data into a local data frame, it pushes the columns that a
local data frame x carries onto matching records that already exist in an
Airtable table, matching by a join key. It enriches existing records with
new or changed field values.
air_left_join_upload( x, table, base_id = NULL, by = NULL, fields = NULL, add_fields = c("yes", "warn", "error"), .token = NULL )air_left_join_upload( x, table, base_id = NULL, by = NULL, fields = NULL, add_fields = c("yes", "warn", "error"), .token = NULL )
x |
A local data frame whose columns should be uploaded. |
table |
Table name or ID. |
base_id |
Base ID (e.g., |
by |
The join key: a column present in both |
fields |
Optional character vector limiting which of |
add_fields |
What to do when an upload column does not exist in the
table: |
.token |
Optional API token. |
Matching is by by (a key present in both x and the remote table). For
each matched record, only the upload columns whose value is new (the
field does not yet exist remotely) or changed (differs from the current
remote value) are sent, minimising API calls. Columns whose value already
equals the remote value are not re-sent.
This function never inserts new records (unmatched local rows are skipped and
counted) and never deletes remote fields. To insert as well as update, use
air_upsert().
The remote table is read minimally: only the key field plus any to-upload
fields that already exist are fetched. The actual write is delegated to
air_upsert() (matching by airtable_id), so batching, throttling, API
counting, and field creation are reused.
Invisibly, a tibble of the records that were updated: their
airtable_id, the key, and the changed field values.
air_left_join() for the read direction, air_upsert() to also
insert new records.
## Not run: # Local scores you computed; push them onto existing Contacts by Name. scores <- tibble::tibble(Name = c("Alice", "Bob"), Score = c(90, 85)) air_left_join_upload(scores, "Contacts", "appXXXX", by = "Name") # Different local/remote key names. air_left_join_upload(scores, "Contacts", "appXXXX", by = c(person = "Name")) ## End(Not run)## Not run: # Local scores you computed; push them onto existing Contacts by Name. scores <- tibble::tibble(Name = c("Alice", "Bob"), Score = c(90, 85)) air_left_join_upload(scores, "Contacts", "appXXXX", by = "Name") # Different local/remote key names. air_left_join_upload(scores, "Contacts", "appXXXX", by = c(person = "Name")) ## End(Not run)
Returns one row per field across all tables, useful for inspecting and editing base structure as a data frame.
air_meta(base_id, .token = NULL)air_meta(base_id, .token = NULL)
base_id |
Base ID (e.g., |
.token |
Personal access token (resolved via |
A tibble with columns: table_name, table_id, field_name,
field_id, field_type, description.
## Not run: meta <- air_meta("appXXXXXX") meta ## End(Not run)## Not run: meta <- air_meta("appXXXXXX") meta ## End(Not run)
Reads the current base schema via air_meta() and upserts it into a
designated table within the same base. Run this once to initialise the
metadata store, then edit the table in Airtable and call air_meta_sync()
to push changes back.
air_meta_init(base_id, meta_table = "_metadata", .token = NULL)air_meta_init(base_id, meta_table = "_metadata", .token = NULL)
base_id |
Base ID (e.g., |
meta_table |
Name of the table to store metadata in.
Default |
.token |
Personal access token (resolved via |
Invisible upsert result.
## Not run: # Initialise the _metadata table air_meta_init("appXXXXXX") # Use a custom table name air_meta_init("appXXXXXX", meta_table = "_docs") ## End(Not run)## Not run: # Initialise the _metadata table air_meta_init("appXXXXXX") # Use a custom table name air_meta_init("appXXXXXX", meta_table = "_docs") ## End(Not run)
Compares a modified metadata tibble (from air_meta()) against the current
schema and applies name/description changes via PATCH. Changes to
table_name rename the table; changes to field_name or description
rename or re-describe the field.
air_meta_push(base_id, meta, .token = NULL)air_meta_push(base_id, meta, .token = NULL)
base_id |
Base ID (e.g., |
meta |
A tibble from |
.token |
Personal access token (resolved via |
Invisible NULL. Side effect: updates table and field metadata.
The preferred metadata workflow: pull field names and descriptions from
a source (default: the "_metadata" table inside the same base), compare
against the live schema, and PATCH any changed fields.
air_meta_sync(base_id, source = "_metadata", ..., .token = NULL)air_meta_sync(base_id, source = "_metadata", ..., .token = NULL)
base_id |
Base ID (e.g., |
source |
Where to pull the edited metadata from. Defaults to
|
... |
Ignored; forces |
.token |
Personal access token (resolved via |
Invisible NULL. Side-effect: PATCHes changed fields in the base.
Seed – run air_meta_init() once to create / populate the
"_metadata" table from the live schema.
Edit – open the "_metadata" table in Airtable and change
field_name / description cells to your liking.
Sync – call air_meta_sync() (no arguments needed) to PATCH the
schema with your edits.
source is resolved in this order:
A data.frame → used directly.
A length-1 character path to an existing .csv file → read with
utils::read.csv().
A length-1 character path to an existing .json file → read with
jsonlite::fromJSON().
A length-1 character string that matches neither 2 nor 3 → treated as
a table name inside the base and read with air_read().
The source tibble must contain at least field_id, table_id,
field_name, and description columns (the shape produced by
air_meta() and air_meta_init()). An extra meta_key column (produced
by older seed runs) is silently ignored.
## Not run: # 1. Seed the _metadata table from the live schema (run once) air_meta_init("appXXXXXX") # 2. Edit field_name / description cells directly in Airtable ... # 3. Pull edits back and patch the schema air_meta_sync("appXXXXXX") # 4. Alternatively, sync from a local CSV air_meta_sync("appXXXXXX", source = "my_meta.csv") ## End(Not run)## Not run: # 1. Seed the _metadata table from the live schema (run once) air_meta_init("appXXXXXX") # 2. Edit field_name / description cells directly in Airtable ... # 3. Pull edits back and patch the schema air_meta_sync("appXXXXXX") # 4. Alternatively, sync from a local CSV air_meta_sync("appXXXXXX", source = "my_meta.csv") ## End(Not run)
Establishes a connection via air_connect() and ensures it is registered
with the RStudio/Positron connection pane.
air_pane(base = NULL, bases = NULL, .token = NULL, include_views = FALSE)air_pane(base = NULL, bases = NULL, .token = NULL, include_views = FALSE)
base |
Character. A Base ID (starts with |
bases |
Character vector of Base IDs or names. When supplied, the
connection pane shows only those bases (as schemas). Cannot be combined
with |
.token |
Character. Airtable Personal Access Token (resolved via
|
include_views |
Logical. If |
An AirtableConnection object (invisibly).
High-level function that auto-paginates, fetches schema for type coercion, and returns a properly typed tibble.
air_read( table, base_id = NULL, view = NULL, fields = NULL, formula = NULL, sort = NULL, max_records = Inf, page_size = 100L, coerce = TRUE, attachments = c("meta", "file", "blob"), attachment_dir = NULL, parallel = NULL, progress = NULL, .token = NULL )air_read( table, base_id = NULL, view = NULL, fields = NULL, formula = NULL, sort = NULL, max_records = Inf, page_size = 100L, coerce = TRUE, attachments = c("meta", "file", "blob"), attachment_dir = NULL, parallel = NULL, progress = NULL, .token = NULL )
table |
Table name or ID. |
base_id |
Base ID (e.g., |
view |
Optional view name or ID to filter by. |
fields |
Optional character vector of field names to return. |
formula |
Optional Airtable formula string for filtering. |
sort |
Optional named character vector for sorting (names = field names,
values = |
max_records |
Maximum number of records to return. Default |
page_size |
Records per page (max 100). |
coerce |
If |
attachments |
How to handle attachment fields: |
attachment_dir |
Directory for downloading attachments (required when
|
parallel |
Logical or |
progress |
Logical or |
.token |
Personal access token (resolved via |
A tibble with columns airtable_id, airtable_created_time, and
one column per field.
## Not run: # Read all records from a table df <- air_read("Contacts", "appXXXXXX") # Read specific fields with a filter df <- air_read("Contacts", "appXXXXXX", fields = c("Name", "Email"), formula = "{Age} > 30" ) # Download attachments to disk df <- air_read("Projects", "appXXXXXX", attachments = "file", attachment_dir = "downloads/" ) ## End(Not run)## Not run: # Read all records from a table df <- air_read("Contacts", "appXXXXXX") # Read specific fields with a filter df <- air_read("Contacts", "appXXXXXX", fields = c("Name", "Email"), formula = "{Age} > 30" ) # Download attachments to disk df <- air_read("Projects", "appXXXXXX", attachments = "file", attachment_dir = "downloads/" ) ## End(Not run)
Downloads attachment files from a specified field, returning them as either in-memory blobs or saved files.
air_read_attachments( base_id, table, field, record_ids = NULL, dest = c("blob", "file"), dir = NULL, parallel = NULL, .token = NULL )air_read_attachments( base_id, table, field, record_ids = NULL, dest = c("blob", "file"), dir = NULL, parallel = NULL, .token = NULL )
base_id |
Base ID (e.g., |
table |
Table name or ID. |
field |
Name of the attachment field. |
record_ids |
Optional character vector of specific record IDs to fetch.
If |
dest |
Either |
dir |
Directory to save files to (required if |
parallel |
Logical or |
.token |
Personal access token (resolved via |
A tibble with airtable_id, filename, url, and either blob
(raw list-column) or local_path (character).
Constructs a base request with auth, user-agent, retry policy, and throttle.
air_req(endpoint, token = NULL, host = c("api", "content"))air_req(endpoint, token = NULL, host = c("api", "content"))
endpoint |
Path appended to the host root
(e.g., |
token |
Personal access token (resolved via |
host |
Which Airtable host to target: |
An httr2_request object ready for further modification.
Takes an ID string (e.g., "appXXXXXX", "tblXXXXXX", "wspXXXXXX", "viwXXXXXX") or a full URL and extracts the ID, determining its type.
air_resolve_id(x)air_resolve_id(x)
x |
Character string. An ID, a connection object, or a URL. |
A list with components: type ("workspace", "base", "table", "view", or "record"),
id (the extracted ID), and any other parsed components.
air_browse() for opening the ID in a browser.
air_resolve_id("appXXXXXX") air_resolve_id("wspXXXXXX") air_resolve_id("https://airtable.com/appXXXXXX/tblXXXXXX/viwXXXXXX")air_resolve_id("appXXXXXX") air_resolve_id("wspXXXXXX") air_resolve_id("https://airtable.com/appXXXXXX/tblXXXXXX/viwXXXXXX")
Recreates a base from output of air_dump(). When attachments is
"file", uploads attachment files from the dump directory after record
creation. For CSV dumps, automatically detects and parses the flattened format.
air_restore( dump, base_name = NULL, workspace_id = NULL, attachments = c("file", "meta"), attachment_dir = NULL, restore_linked_fields = TRUE, .token = NULL )air_restore( dump, base_name = NULL, workspace_id = NULL, attachments = c("file", "meta"), attachment_dir = NULL, restore_linked_fields = TRUE, .token = NULL )
dump |
Either a list (from |
base_name |
Name for the new base. If |
workspace_id |
Workspace ID to create the base in. |
attachments |
How to handle attachment fields: |
attachment_dir |
Directory for downloading attachments (required when
|
restore_linked_fields |
If |
.token |
Personal access token (resolved via |
The new base ID (invisibly).
Linked-record fields (multipleRecordLinks) and the computed fields that
depend on them (rollup, lookup, count) are skipped during the initial
field-creation pass because their options reference base-specific IDs.
When restore_linked_fields = TRUE (the default), after all records are
inserted a two-step pass runs:
Field definitions: multipleRecordLinks fields are recreated with
linkedTableId remapped to the new base's table IDs.
Cell values: the link columns in the dump contain old record IDs.
These are remapped to the new record IDs (matched by insertion order) and
written back via air_upsert().
## Not run: # Restore from a directory dump air_restore("backup/", workspace_id = "wspXXXXXX") # Restore from a CSV dump air_restore("backup/", workspace_id = "wspXXXXXX", format = "csv") ## End(Not run)## Not run: # Restore from a directory dump air_restore("backup/", workspace_id = "wspXXXXXX") # Restore from a CSV dump air_restore("backup/", workspace_id = "wspXXXXXX", format = "csv") ## End(Not run)
Returns table and field metadata in a structured tibble format.
air_schema(base_id, .token = NULL)air_schema(base_id, .token = NULL)
base_id |
Base ID (e.g., |
.token |
Personal access token (resolved via |
A tibble with columns table_id, table_name, and fields
(a list-column of tibbles with id, name, type, description).
## Not run: schema <- air_schema("appXXXXXX") schema$table_name schema$fields[[1]] ## End(Not run)## Not run: schema <- air_schema("appXXXXXX") schema$table_name schema$fields[[1]] ## End(Not run)
Validates that base_id is accessible with the current token, prints a
{cli} confirmation, and sets options(airtable2.base_id = base_id) for
the rest of the session. Functions that accept base_id will use this
default when base_id is NULL.
air_set_base(base_id, .token = NULL)air_set_base(base_id, .token = NULL)
base_id |
Base ID (starts with |
.token |
Personal access token (resolved via |
base_id (invisibly).
## Not run: air_set_base("appXXXXXXXXXXXXXX") ## End(Not run)## Not run: air_set_base("appXXXXXXXXXXXXXX") ## End(Not run)
Validates the token by calling at_whoami(), prints a {cli} confirmation
with the authenticated user's email, and sets options(airtable2.token = tok).
air_set_token(tok)air_set_token(tok)
tok |
Personal Access Token (PAT) string. |
tok (invisibly).
## Not run: air_set_token(Sys.getenv("AIRTABLE_API_KEY")) ## End(Not run)## Not run: air_set_token(Sys.getenv("AIRTABLE_API_KEY")) ## End(Not run)
Applies the appropriate flatten function to each list-column based on schema information.
air_simplify(data, schema = NULL)air_simplify(data, schema = NULL)
data |
A tibble (typically from |
schema |
Optional list of field definitions (from |
A tibble with list-columns replaced by character representations.
Compares local data against the current table contents using a hash of specified fields, then creates/updates/deletes as needed.
air_sync( data, table, key, base_id = NULL, hash_fields = NULL, delete_missing = TRUE, typecast = TRUE, add_fields = c("error", "warn", "yes"), attachments = c("meta", "file", "blob"), attachment_dir = NULL, progress = NULL, .token = NULL )air_sync( data, table, key, base_id = NULL, hash_fields = NULL, delete_missing = TRUE, typecast = TRUE, add_fields = c("error", "warn", "yes"), attachments = c("meta", "file", "blob"), attachment_dir = NULL, progress = NULL, .token = NULL )
data |
A data frame representing the desired state of the table. May contain computed field columns (they are ignored). |
table |
Table name or ID. |
key |
Column name in |
base_id |
Base ID (e.g., |
hash_fields |
Character vector of fields to include in the change-
detection hash. If |
delete_missing |
If |
typecast |
If |
add_fields |
What to do when |
attachments |
How to handle attachment fields: |
attachment_dir |
Directory for downloading attachments (required when
|
progress |
Logical or |
.token |
Personal access token (resolved via |
Computed fields (formulas, rollups, autoNumber, createdTime, lastModifiedTime, createdBy, lastModifiedBy, etc.) are automatically:
Excluded from the change-detection hash (they change server-side and would cause spurious "changed" detections on every sync)
Excluded from the upload payload (the API rejects writes to them)
Attachment fields (multipleAttachments) are always excluded from the
change-detection hash because their URLs are volatile (expire hourly).
When attachments is "file" or "blob", attachment content is uploaded
for newly created records after the sync completes.
A list with counts: created, updated, deleted, unchanged
(invisibly).
## Not run: desired <- data.frame(Name = c("Alice", "Bob"), Age = c(30, 26)) result <- air_sync(desired, "Contacts", key = "Name", base_id = "appXXXXXX") result$created result$unchanged ## End(Not run)## Not run: desired <- data.frame(Name = c("Alice", "Bob"), Age = c(30, 26)) result <- air_sync(desired, "Contacts", key = "Name", base_id = "appXXXXXX") result$created result$unchanged ## End(Not run)
Compares filenames between local data and remote records, uploads new/changed files, skips unchanged.
air_sync_attachments( base_id, table, field, data, key, parallel = NULL, .token = NULL )air_sync_attachments( base_id, table, field, data, key, parallel = NULL, .token = NULL )
base_id |
Base ID (e.g., |
table |
Table name or ID. |
field |
Name of the attachment field. |
data |
A tibble with a key column and |
key |
Column name that identifies which record to attach to. |
parallel |
Logical or |
.token |
Personal access token (resolved via |
A list with counts: uploaded, skipped (invisibly).
Convenience function for constructing table configurations suitable for
at_create_table() or at_create_base().
air_table_template(name, fields, description = NULL)air_table_template(name, fields, description = NULL)
name |
Table name. |
fields |
A list of field specifications (e.g., from
|
description |
Optional table description. |
A list suitable for passing to the Airtable API.
fields <- list( air_field_template("Name", "singleLineText"), air_field_template("Score", "number", options = list(precision = 2)) ) air_table_template("Results", fields, description = "Exam results")fields <- list( air_field_template("Name", "singleLineText"), air_field_template("Score", "number", options = list(precision = 2)) ) air_table_template("Results", fields, description = "Exam results")
Looks for a token in this order:
Explicit .token argument
getOption("airtable2.token")
Sys.getenv("AIRTABLE_API_KEY")
air_token(token = NULL)air_token(token = NULL)
token |
A personal access token string, or |
A string (the token).
## Not run: # Uses AIRTABLE_API_KEY env var by default token <- air_token() # Or pass explicitly token <- air_token("patXXXXXXXX") ## End(Not run)## Not run: # Uses AIRTABLE_API_KEY env var by default token <- air_token() # Or pass explicitly token <- air_token("patXXXXXXXX") ## End(Not run)
Uses Airtable's native upsert (PATCH with performUpsert) to create or
update records based on merge fields. Supports two matching modes:
air_upsert( data, table, merge_on, base_id = NULL, typecast = TRUE, add_fields = c("error", "warn", "yes"), attachments = c("meta", "file", "blob"), attachment_dir = NULL, progress = NULL, .token = NULL )air_upsert( data, table, merge_on, base_id = NULL, typecast = TRUE, add_fields = c("error", "warn", "yes"), attachments = c("meta", "file", "blob"), attachment_dir = NULL, progress = NULL, .token = NULL )
data |
A data frame of records to upsert. May include an |
table |
Table name or ID. |
merge_on |
Character vector of 1-3 field names to match on (for records
without an |
base_id |
Base ID (e.g., |
typecast |
If |
add_fields |
What to do when
|
attachments |
How to handle attachment fields: |
attachment_dir |
Directory for downloading attachments (required when
|
progress |
Logical or |
.token |
Personal access token (resolved via |
If data contains an airtable_id column, records with non-NA IDs are
updated directly by record ID (more efficient).
Records without an airtable_id (or where it is NA) are matched using
the merge_on field(s) via Airtable's upsert mechanism.
Computed fields (formulas, rollups, autoNumber, createdTime,
lastModifiedTime, etc.) and attachment fields are automatically excluded
from the upload payload. When attachments is "file" or "blob",
attachment content is uploaded separately after record creation/update.
Optionally creates missing columns.
A list with created and updated character vectors of record IDs
(invisibly).
## Not run: data <- data.frame(Name = c("Alice", "Bob"), Age = c(31, 26)) result <- air_upsert(data, "Contacts", merge_on = "Name", base_id = "appXXXXXX") result$created result$updated ## End(Not run)## Not run: data <- data.frame(Name = c("Alice", "Bob"), Age = c(31, 26)) result <- air_upsert(data, "Contacts", merge_on = "Name", base_id = "appXXXXXX") result$created result$updated ## End(Not run)
Converts a data frame into records and creates them in the specified table.
Automatically batches in groups of 10. Computed fields (formulas, rollups,
autoNumber, createdTime, lastModifiedTime, etc.) and attachment fields are
automatically excluded from the upload payload. When attachments is
"file" or "blob", attachment content is uploaded separately after record
creation using the dedicated upload endpoint.
air_write( data, table, base_id = NULL, typecast = TRUE, add_fields = c("error", "warn", "yes"), attachments = c("meta", "file", "blob"), attachment_dir = NULL, progress = NULL, .token = NULL )air_write( data, table, base_id = NULL, typecast = TRUE, add_fields = c("error", "warn", "yes"), attachments = c("meta", "file", "blob"), attachment_dir = NULL, progress = NULL, .token = NULL )
data |
A data frame of records to create. Should not contain
|
table |
Table name or ID. |
base_id |
Base ID (e.g., |
typecast |
If |
add_fields |
What to do when
|
attachments |
How to handle attachment fields: |
attachment_dir |
Directory for downloading attachments (required when
|
progress |
Logical or |
.token |
Personal access token (resolved via |
A character vector of the created record IDs (invisibly).
## Not run: data <- data.frame(Name = c("Alice", "Bob"), Age = c(30, 25)) ids <- air_write(data, "Contacts", "appXXXXXX") # Write records and upload attachments from list-column ids <- air_write(data, "Projects", "appXXXXXX", attachments = "file", attachment_dir = "files/" ) # Write records and add new columns if they don't exist ids <- air_write(data, "Contacts", "appXXXXXX", add_fields = "yes") ## End(Not run)## Not run: data <- data.frame(Name = c("Alice", "Bob"), Age = c(30, 25)) ids <- air_write(data, "Contacts", "appXXXXXX") # Write records and upload attachments from list-column ids <- air_write(data, "Projects", "appXXXXXX", attachments = "file", attachment_dir = "files/" ) # Write records and add new columns if they don't exist ids <- air_write(data, "Contacts", "appXXXXXX", add_fields = "yes") ## End(Not run)
Upload attachments to records
air_write_attachments( base_id, table, field, data, parallel = NULL, .token = NULL )air_write_attachments( base_id, table, field, data, parallel = NULL, .token = NULL )
base_id |
Base ID (e.g., |
table |
Table name or ID. |
field |
Name of the attachment field. |
data |
A tibble with |
parallel |
Logical or |
.token |
Personal access token (resolved via |
Invisible NULL. Side effect: uploads attachments.
Create a DBI driver for Airtable. Use with DBI::dbConnect() to create an
Airtable DBI connection.
airtable2() at() airtable() ## S4 method for signature 'AirtableDriver' dbConnect( drv, token = NULL, base_id = NULL, include_views = FALSE, connect_code = NULL, bases_filter = NULL, ... ) ## S4 method for signature 'AirtableDriver' dbDataType(dbObj, obj, ...) ## S4 method for signature 'AirtableDriver' dbUnloadDriver(drv, ...)airtable2() at() airtable() ## S4 method for signature 'AirtableDriver' dbConnect( drv, token = NULL, base_id = NULL, include_views = FALSE, connect_code = NULL, bases_filter = NULL, ... ) ## S4 method for signature 'AirtableDriver' dbDataType(dbObj, obj, ...) ## S4 method for signature 'AirtableDriver' dbUnloadDriver(drv, ...)
drv, dbObj
|
An |
token |
Personal access token (resolved via |
base_id |
Optional Airtable base ID. If omitted, all accessible bases are shown as schemas in the connection pane. |
include_views |
Logical. If |
connect_code |
Optional custom reconnect code string for the IDE
connection pane. If |
bases_filter |
Optional character vector of base IDs or names to restrict the connection pane to specific bases. |
... |
Additional arguments passed to DBI methods. |
obj |
An R object to map to an Airtable field type. |
An AirtableDriver object.
Use this driver to create DBI-compliant connections to Airtable for use with RStudio/Positron's connection pane. See airtable2-package for package-level documentation and AirtableConnection for details on available DBI methods.
## Not run: con <- DBI::dbConnect(airtable2(), base_id = "appXXXXXX") DBI::dbListTables(con) DBI::dbDisconnect(con) ## End(Not run)## Not run: con <- DBI::dbConnect(airtable2(), base_id = "appXXXXXX") DBI::dbListTables(con) DBI::dbDisconnect(con) ## End(Not run)
Stores Airtable credentials and connection state for DBI methods. Use
airtable2() with DBI::dbConnect() to create connections.
## S4 method for signature 'AirtableConnection' dbDisconnect(conn, ...) ## S4 method for signature 'AirtableConnection' dbIsValid(dbObj, ...) ## S4 method for signature 'AirtableConnection' dbListTables(conn, ...) ## S4 method for signature 'AirtableConnection,character' dbExistsTable(conn, name, ...) ## S4 method for signature 'AirtableConnection,character' dbListFields(conn, name, ...) ## S4 method for signature 'AirtableConnection,character' dbReadTable(conn, name, ...) ## S4 method for signature 'AirtableConnection,character' dbWriteTable(conn, name, value, overwrite = FALSE, append = FALSE, ...) ## S4 method for signature 'AirtableConnection,character' dbRemoveTable(conn, name, ...) ## S4 method for signature 'AirtableConnection' dbGetInfo(dbObj, ...)## S4 method for signature 'AirtableConnection' dbDisconnect(conn, ...) ## S4 method for signature 'AirtableConnection' dbIsValid(dbObj, ...) ## S4 method for signature 'AirtableConnection' dbListTables(conn, ...) ## S4 method for signature 'AirtableConnection,character' dbExistsTable(conn, name, ...) ## S4 method for signature 'AirtableConnection,character' dbListFields(conn, name, ...) ## S4 method for signature 'AirtableConnection,character' dbReadTable(conn, name, ...) ## S4 method for signature 'AirtableConnection,character' dbWriteTable(conn, name, value, overwrite = FALSE, append = FALSE, ...) ## S4 method for signature 'AirtableConnection,character' dbRemoveTable(conn, name, ...) ## S4 method for signature 'AirtableConnection' dbGetInfo(dbObj, ...)
conn, dbObj
|
An |
... |
Additional arguments passed to Airtable helpers. |
name |
Table name. |
value |
Data frame to write. |
overwrite, append
|
DBI write mode flags. |
dbReadTable() works on any accessible table. You
can also pass "TableName WHERE <formula>" as the name to filter records
using Airtable's formula syntax.
dbWriteTable() works on existing tables only.
With append = TRUE it creates new records; with overwrite = TRUE it
syncs (upsert + delete-missing) using the first column as the key.
Use at_create_table() to create tables.
dbWriteTable() errors if the table does not already exist.
Airtable's API cannot delete tables. Use the
Airtable web UI instead. dbRemoveTable() will error with a clear
message.
Arbitrary SQL is not supported. Use the high-level
helpers (air_read(), air_write(), air_upsert(), air_sync()) for
more ergonomic access.
Airtable does not support database transactions.
When a base_id is given, the connection
shows that base's tables directly. Without a base_id, all accessible
bases appear as schemas in the connection pane.
For most use cases, the high-level functions like air_read(), air_write(),
air_upsert(), and air_sync() provide more ergonomic interfaces for
Airtable operations.
S4 class representing the Airtable DBI driver. Use airtable2() to
construct an instance and pass it to DBI::dbConnect().
Eagerly materialized DBI result object used by DBI::dbSendQuery().
Unlike traditional DBI results which may be cursor-based, AirtableResult
objects fetch all matching records immediately (due to Airtable API
limitations).
## S4 method for signature 'AirtableConnection,character' dbSendQuery(conn, statement, ...) ## S4 method for signature 'AirtableResult,numeric' dbFetch(res, n = -1, ...) ## S4 method for signature 'AirtableResult' dbClearResult(res, ...) ## S4 method for signature 'AirtableResult' dbHasCompleted(res, ...) ## S4 method for signature 'AirtableResult' dbGetRowCount(res, ...) ## S4 method for signature 'AirtableResult' dbGetStatement(res, ...) ## S4 method for signature 'AirtableResult' dbIsValid(dbObj, ...) ## S4 method for signature 'AirtableResult' dbGetRowsAffected(res, ...) ## S4 method for signature 'AirtableResult,missing' dbFetch(res, n = -1, ...)## S4 method for signature 'AirtableConnection,character' dbSendQuery(conn, statement, ...) ## S4 method for signature 'AirtableResult,numeric' dbFetch(res, n = -1, ...) ## S4 method for signature 'AirtableResult' dbClearResult(res, ...) ## S4 method for signature 'AirtableResult' dbHasCompleted(res, ...) ## S4 method for signature 'AirtableResult' dbGetRowCount(res, ...) ## S4 method for signature 'AirtableResult' dbGetStatement(res, ...) ## S4 method for signature 'AirtableResult' dbIsValid(dbObj, ...) ## S4 method for signature 'AirtableResult' dbGetRowsAffected(res, ...) ## S4 method for signature 'AirtableResult,missing' dbFetch(res, n = -1, ...)
conn |
An |
statement |
Table name, optionally followed by |
... |
Additional arguments passed to Airtable helpers. |
res, dbObj
|
An |
n |
Number of rows to fetch. Use a negative value to fetch all rows. Note: Due to eager evaluation, this parameter is primarily for compatibility. |
All data is fetched when the result is created. There is no cursor-based iteration.
AirtableResult objects are for reading data only.
The statement parameter supports Airtable
formula syntax after the table name (e.g., "Contacts WHERE Age > 30").
This is not SQL.
Create a new base
at_create_base(name, tables, workspace_id = NULL, token = NULL)at_create_base(name, tables, workspace_id = NULL, token = NULL)
name |
Name for the new base. |
tables |
A list of table configurations. Each should include at minimum
|
workspace_id |
Workspace ID to create the base in. |
token |
Personal access token (resolved via |
The created base object (list with id, name, tables).
## Not run: new_base <- at_create_base( name = "My New Base", workspace_id = "wspXXXXXXXXXXXXXX", tables = list(list( name = "Items", fields = list(list(name = "Name", type = "singleLineText")) )) ) new_base$id ## End(Not run)## Not run: new_base <- at_create_base( name = "My New Base", workspace_id = "wspXXXXXXXXXXXXXX", tables = list(list( name = "Items", fields = list(list(name = "Name", type = "singleLineText")) )) ) new_base$id ## End(Not run)
Create a field in a table
at_create_field( name, table_id, type, base_id = NULL, description = NULL, options = NULL, token = NULL )at_create_field( name, table_id, type, base_id = NULL, description = NULL, options = NULL, token = NULL )
name |
Field name. |
table_id |
Table ID. |
type |
Field type (e.g., |
base_id |
Base ID. If |
description |
Optional field description. |
options |
Field-specific options (list). Depends on field type. |
token |
Personal access token (resolved via |
The created field object.
## Not run: # Add a number field at_create_field( name = "Score", table_id = "tblXXXXXXXXXXXXXX", type = "number", base_id = "appXXXXXXXXXXXXXX", options = list(precision = 1L) ) # Add a single-select field at_create_field( name = "Status", table_id = "tblXXXXXXXXXXXXXX", type = "singleSelect", base_id = "appXXXXXXXXXXXXXX", options = list(choices = list(list(name = "Open"), list(name = "Closed"))) ) ## End(Not run)## Not run: # Add a number field at_create_field( name = "Score", table_id = "tblXXXXXXXXXXXXXX", type = "number", base_id = "appXXXXXXXXXXXXXX", options = list(precision = 1L) ) # Add a single-select field at_create_field( name = "Status", table_id = "tblXXXXXXXXXXXXXX", type = "singleSelect", base_id = "appXXXXXXXXXXXXXX", options = list(choices = list(list(name = "Open"), list(name = "Closed"))) ) ## End(Not run)
Creates up to 10 records per API call. If more than 10 records are provided, they are automatically batched.
at_create_records( base_id, table_id, records, typecast = FALSE, token = NULL, progress = NULL )at_create_records( base_id, table_id, records, typecast = FALSE, token = NULL, progress = NULL )
base_id |
Base ID (e.g., |
table_id |
Table name or ID. |
records |
A list of record objects. Each should be a list with a
|
typecast |
If |
token |
Personal access token (resolved via |
progress |
Logical or |
A list of created record objects (with assigned IDs).
## Not run: records <- list( list(fields = list(Name = "Alice", Age = 30)), list(fields = list(Name = "Bob", Age = 25)) ) created <- at_create_records("appXXXXXXXXXXXXXX", "Contacts", records) vapply(created, function(r) r$id, character(1)) ## End(Not run)## Not run: records <- list( list(fields = list(Name = "Alice", Age = 30)), list(fields = list(Name = "Bob", Age = 25)) ) created <- at_create_records("appXXXXXXXXXXXXXX", "Contacts", records) vapply(created, function(r) r$id, character(1)) ## End(Not run)
Create a table in a base
at_create_table(name, fields, base_id = NULL, description = NULL, token = NULL)at_create_table(name, fields, base_id = NULL, description = NULL, token = NULL)
name |
Table name. |
fields |
A list of field configurations. The first field becomes the primary field. |
base_id |
Base ID. If |
description |
Optional table description. |
token |
Personal access token (resolved via |
The created table object (list with id, name, fields).
## Not run: tbl <- at_create_table( name = "Tasks", fields = list( list(name = "Title", type = "singleLineText"), list(name = "Done", type = "checkbox", options = list(icon = "check", color = "greenBright")), list(name = "Due", type = "date", options = list(dateFormat = list(name = "iso"))) ), base_id = "appXXXXXXXXXXXXXX" ) tbl$id ## End(Not run)## Not run: tbl <- at_create_table( name = "Tasks", fields = list( list(name = "Title", type = "singleLineText"), list(name = "Done", type = "checkbox", options = list(icon = "check", color = "greenBright")), list(name = "Due", type = "date", options = list(dateFormat = list(name = "iso"))) ), base_id = "appXXXXXXXXXXXXXX" ) tbl$id ## End(Not run)
Deletes up to 10 records per API call. Automatically batched.
at_delete_records(base_id, table_id, record_ids, token = NULL, progress = NULL)at_delete_records(base_id, table_id, record_ids, token = NULL, progress = NULL)
base_id |
Base ID (e.g., |
table_id |
Table name or ID. |
record_ids |
Character vector of record IDs to delete. |
token |
Personal access token (resolved via |
progress |
Logical or |
A list of delete confirmation objects (each with id and
deleted = TRUE).
## Not run: at_delete_records( "appXXXXXXXXXXXXXX", "Contacts", c("recXXXXXXXXXXXXXX", "recYYYYYYYYYYYYYY") ) ## End(Not run)## Not run: at_delete_records( "appXXXXXXXXXXXXXX", "Contacts", c("recXXXXXXXXXXXXXX", "recYYYYYYYYYYYYYY") ) ## End(Not run)
Get information about a single base
at_get_base(base_id, token = NULL)at_get_base(base_id, token = NULL)
base_id |
Base ID (e.g., |
token |
Personal access token (resolved via |
A list with id, name, and permissionLevel, or NULL if not
found.
## Not run: info <- at_get_base("appXXXXXXXXXXXXXX") info$name info$permissionLevel ## End(Not run)## Not run: info <- at_get_base("appXXXXXXXXXXXXXX") info$name info$permissionLevel ## End(Not run)
Get collaborators for a base
at_get_collaborators(base_id, token = NULL)at_get_collaborators(base_id, token = NULL)
base_id |
Base ID (e.g., |
token |
Personal access token (resolved via |
The parsed collaborator response (list).
## Not run: collabs <- at_get_collaborators("appXXXXXXXXXXXXXX") ## End(Not run)## Not run: collabs <- at_get_collaborators("appXXXXXXXXXXXXXX") ## End(Not run)
Get a single record
at_get_record(base_id, table_id, record_id, token = NULL)at_get_record(base_id, table_id, record_id, token = NULL)
base_id |
Base ID (e.g., |
table_id |
Table name or ID. |
record_id |
Record ID (e.g., |
token |
Personal access token (resolved via |
A single record object (list with id, createdTime, fields).
## Not run: rec <- at_get_record( "appXXXXXXXXXXXXXX", "Contacts", "recXXXXXXXXXXXXXX" ) rec$fields$Name ## End(Not run)## Not run: rec <- at_get_record( "appXXXXXXXXXXXXXX", "Contacts", "recXXXXXXXXXXXXXX" ) rec$fields$Name ## End(Not run)
Get the schema (tables + fields) for a base
at_get_schema(base_id, token = NULL)at_get_schema(base_id, token = NULL)
base_id |
Base ID (e.g., |
token |
Personal access token (resolved via |
A list of table objects, each containing id, name,
description, fields, and views.
## Not run: tables <- at_get_schema("appXXXXXXXXXXXXXX") vapply(tables, function(t) t$name, character(1)) ## End(Not run)## Not run: tables <- at_get_schema("appXXXXXXXXXXXXXX") vapply(tables, function(t) t$name, character(1)) ## End(Not run)
Get a specific view's metadata
at_get_view(base_id, table_id, view_id, token = NULL)at_get_view(base_id, table_id, view_id, token = NULL)
base_id |
Base ID. |
table_id |
Table ID. |
view_id |
View ID. |
token |
Personal access token (resolved via |
A list with view metadata (name, type, formula, filterByFormula, etc.).
## Not run: view <- at_get_view( "appXXXXXXXXXXXXXX", "tblXXXXXXXXXXXXXX", "viwXXXXXXXXXXXXXX" ) view$name view$type ## End(Not run)## Not run: view <- at_get_view( "appXXXXXXXXXXXXXX", "tblXXXXXXXXXXXXXX", "viwXXXXXXXXXXXXXX" ) view$name view$type ## End(Not run)
List all accessible bases
at_list_bases(token = NULL)at_list_bases(token = NULL)
token |
Personal access token (resolved via |
A tibble with columns id, name, and permissionLevel.
## Not run: # List all bases accessible with the current token at_list_bases() ## End(Not run)## Not run: # List all bases accessible with the current token at_list_bases() ## End(Not run)
List records from a table
at_list_records( base_id, table_id, fields = NULL, formula = NULL, sort = NULL, view = NULL, max_records = Inf, page_size = 100L, cell_format = NULL, time_zone = NULL, user_locale = NULL, return_fields_by_id = FALSE, token = NULL, progress = NULL )at_list_records( base_id, table_id, fields = NULL, formula = NULL, sort = NULL, view = NULL, max_records = Inf, page_size = 100L, cell_format = NULL, time_zone = NULL, user_locale = NULL, return_fields_by_id = FALSE, token = NULL, progress = NULL )
base_id |
Base ID (e.g., |
table_id |
Table name or ID. |
fields |
Character vector of field names to return. |
formula |
Airtable formula string for filtering. |
sort |
A named character vector: names are field names, values are
|
view |
View name or ID to filter by. |
max_records |
Maximum total records to return. |
page_size |
Records per page (max 100). |
cell_format |
Either |
time_zone |
Time zone for date formatting (when |
user_locale |
Locale for date formatting. |
return_fields_by_id |
If |
token |
Personal access token (resolved via |
progress |
Logical or |
A list of record objects (each with id, createdTime, fields).
## Not run: # List all records from a table records <- at_list_records("appXXXXXXXXXXXXXX", "Contacts") # Filter records with a formula records <- at_list_records( "appXXXXXXXXXXXXXX", "Contacts", formula = "{Active} = TRUE()", fields = c("Name", "Email") ) ## End(Not run)## Not run: # List all records from a table records <- at_list_records("appXXXXXXXXXXXXXX", "Contacts") # Filter records with a formula records <- at_list_records( "appXXXXXXXXXXXXXX", "Contacts", formula = "{Active} = TRUE()", fields = c("Name", "Email") ) ## End(Not run)
List views in a table
at_list_views(base_id, table_id, token = NULL)at_list_views(base_id, table_id, token = NULL)
base_id |
Base ID. |
table_id |
Table ID. |
token |
Personal access token (resolved via |
A tibble with columns id, name, and type.
## Not run: views <- at_list_views("appXXXXXXXXXXXXXX", "tblXXXXXXXXXXXXXX") views$name ## End(Not run)## Not run: views <- at_list_views("appXXXXXXXXXXXXXX", "tblXXXXXXXXXXXXXX") views$name ## End(Not run)
Safely probes the current token to determine what workspaces and bases
are accessible. Modelled after usethis::git_sitrep() and usethis::proj_sitrep().
Useful for debugging authentication issues and understanding the scope of
the current token.
at_sitrep(token = NULL)at_sitrep(token = NULL)
token |
Personal access token (resolved via |
A list with:
user: User info from at_whoami() (id, and email/scopes if the
token grants them)
scopes: Character vector of token scopes, or NULL if not exposed
bases: Tibble of accessible bases (id, name, permissionLevel)
error: NULL if successful, otherwise a message string
Note: the non-enterprise Airtable API does not expose workspace names or the workspace each base belongs to, so workspaces cannot be enumerated from a token alone.
## Not run: # Check what your current token can access info <- at_sitrep() print(info$user) print(info$scopes) head(info$bases) ## End(Not run)## Not run: # Check what your current token can access info <- at_sitrep() print(info$user) print(info$scopes) head(info$bases) ## End(Not run)
Update field metadata
at_update_field( base_id, table_id, field_id, name = NULL, description = NULL, token = NULL )at_update_field( base_id, table_id, field_id, name = NULL, description = NULL, token = NULL )
base_id |
Base ID. |
table_id |
Table ID. |
field_id |
Field ID. |
name |
New field name (or |
description |
New description (or |
token |
Personal access token (resolved via |
The updated field object.
## Not run: at_update_field( base_id = "appXXXXXXXXXXXXXX", table_id = "tblXXXXXXXXXXXXXX", field_id = "fldXXXXXXXXXXXXXX", name = "Full Name", description = "First and last name" ) ## End(Not run)## Not run: at_update_field( base_id = "appXXXXXXXXXXXXXX", table_id = "tblXXXXXXXXXXXXXX", field_id = "fldXXXXXXXXXXXXXX", name = "Full Name", description = "First and last name" ) ## End(Not run)
Updates records using PATCH (partial) or PUT (destructive). Handles
auto-batching in groups of 10. Also supports upsert via the
upsert_fields argument.
at_update_records( base_id, table_id, records, method = c("PATCH", "PUT"), typecast = FALSE, upsert_fields = NULL, token = NULL, progress = NULL )at_update_records( base_id, table_id, records, method = c("PATCH", "PUT"), typecast = FALSE, upsert_fields = NULL, token = NULL, progress = NULL )
base_id |
Base ID (e.g., |
table_id |
Table name or ID. |
records |
A list of record objects. Each should be a list with |
method |
Either |
typecast |
If |
upsert_fields |
Character vector (1-3 fields) to merge on for upsert.
If |
token |
Personal access token (resolved via |
progress |
Logical or |
A list with records, and (for upserts) createdRecords and
updatedRecords character vectors.
## Not run: # Patch an existing record at_update_records( "appXXXXXXXXXXXXXX", "Contacts", records = list(list(id = "recXXXXXXXXXXXXXX", fields = list(Age = 31))) ) # Upsert by Name field at_update_records( "appXXXXXXXXXXXXXX", "Contacts", records = list(list(fields = list(Name = "Alice", Age = 31))), upsert_fields = "Name" ) ## End(Not run)## Not run: # Patch an existing record at_update_records( "appXXXXXXXXXXXXXX", "Contacts", records = list(list(id = "recXXXXXXXXXXXXXX", fields = list(Age = 31))) ) # Upsert by Name field at_update_records( "appXXXXXXXXXXXXXX", "Contacts", records = list(list(fields = list(Name = "Alice", Age = 31))), upsert_fields = "Name" ) ## End(Not run)
Update table metadata
at_update_table( base_id, table_id, name = NULL, description = NULL, token = NULL )at_update_table( base_id, table_id, name = NULL, description = NULL, token = NULL )
base_id |
Base ID. |
table_id |
Table ID. |
name |
New table name (or |
description |
New description (or |
token |
Personal access token (resolved via |
The updated table object.
## Not run: at_update_table( base_id = "appXXXXXXXXXXXXXX", table_id = "tblXXXXXXXXXXXXXX", name = "Renamed Table" ) ## End(Not run)## Not run: at_update_table( base_id = "appXXXXXXXXXXXXXX", table_id = "tblXXXXXXXXXXXXXX", name = "Renamed Table" ) ## End(Not run)
Upload an attachment to a record field
at_upload_attachment( base_id, table_id, record_id, field_id, file, token = NULL )at_upload_attachment( base_id, table_id, record_id, field_id, file, token = NULL )
base_id |
Base ID. |
table_id |
Table ID or name containing the record. |
record_id |
Record ID. |
field_id |
Field ID or name. |
file |
Path to the file to upload (max 5 MB). |
token |
Personal access token (resolved via |
The attachment object returned by the API.
## Not run: at_upload_attachment( base_id = "appXXXXXXXXXXXXXX", table_id = "Projects", record_id = "recXXXXXXXXXXXXXX", field_id = "Files", file = "report.pdf" ) ## End(Not run)## Not run: at_upload_attachment( base_id = "appXXXXXXXXXXXXXX", table_id = "Projects", record_id = "recXXXXXXXXXXXXXX", field_id = "Files", file = "report.pdf" ) ## End(Not run)
Get current user info
at_whoami(token = NULL)at_whoami(token = NULL)
token |
Personal access token (resolved via |
A list with id and (if scoped) email, scopes.
## Not run: me <- at_whoami() me$email me$id ## End(Not run)## Not run: me <- at_whoami() me$email me$id ## End(Not run)