Attio Export to Google Sheets
Attio data into Google Sheets without paid ETL or middleware. Free Apps Script, scheduled refresh. This export is for teams that want a no-cost path from Attio to Sheets for reporting, BI imports, pivot-table workflows, and lightweight operational dashboards.
Who this is for
This export is for Attio teams that need spreadsheet access without buying a dedicated ETL connector. It fits when Sheets is the handoff point for leadership reporting, finance checks, pivot tables, CSV workflows, or a BI tool that already imports from Google Sheets.
It is also a fit when the right answer is a simple scheduled pull. There is no Attio app to install, no webhook receiver to host, and no OAuth flow to maintain. The script runs in your Google account and reads one Attio workspace through a bearer API key.
How the export works
The export runs as a polling job. On each run, the script reads the configured Attio surfaces, flattens nested values into spreadsheet columns, then writes a rectangular grid to the matching Google Sheet tab.
Apps Script execution model
The script runs inside your Google Apps Script project, under your Google account, on a time-based trigger. Nothing is hosted by Neon Deer for the scheduled export. Installable Apps Script triggers run under the account that created them, so the owner account matters during setup and long-term maintenance.
How Attio records are pulled
Object exports call POST /v2/objects/{object}/records/query with limit and offset in the JSON body.
Attio documents default values of 500 for limit and 0 for offset. List entries use the same
limit-and-offset shape through POST /v2/lists/{list}/entries/query. Workspace members are simpler: GET /v2/workspace_members
returns the documented data[] list in one response.
How nested Attio fields are flattened
Attio attribute values are nested and type-specific. Status values contain a nested status object. Email values expose fields such as email_address, email_domain, and email_root_domain. Location values use fields such as line_1, line_2, locality, region, postcode, and country_code. Record references expose target object and target record identifiers.
The script flattens those nested values with a double-underscore header convention, such as status__title, email__email_address,
and location__locality. The canonical location example is location__locality, not location__city, because Attio's
documented location field is locality.
How tabs are written
Each WRITER_OBJECT entry writes one Google Sheet tab. The script builds headers and rows into a rectangular two-dimensional array, then uses
SpreadsheetApp and Range.setValues(values) for the batch write. The tab can be cleared before the new export is written, which
keeps each refresh as the current snapshot rather than an append-only log.
What we configure during scope
Scope turns the reusable script pattern into a sheet your team can use. These decisions control what Attio data is pulled, how it is shaped, and which Google account owns the schedule.
| Decision | What we decide with you |
|---|---|
| WRITER_OBJECT entries | Which Attio surfaces feed which Google Sheet tabs. |
| Object coverage | Which standard or custom Attio objects export, such as companies, people, deals, or any custom object identified by slug or UUID. |
| List coverage | Which Attio lists export, identified by list UUID or slug. |
| Workspace members | Whether the team roster syncs as its own tab. |
| Refresh cadence | Time-based trigger interval: every minute, 5, 10, 15, or 30 minutes, hourly, daily, or weekly. The typical default is daily or hourly. |
| Field exclusions and DATE_FORMAT | Which attributes to omit, how multiselects serialize, and the date pattern for Attio timestamps. |
| Apps Script account context | Which Google account owns the trigger, since installable triggers run under the creator's account. |
How it uses the Attio API and Google Apps Script
Attio authentication uses a single-workspace API key sent in the Authorization: Bearer <token> header. Attio documents this bearer
pattern in its authentication guide. This script does
not use Attio OAuth because it is not a multi-workspace Attio app.
The Attio API surface is intentionally narrow. Object records come from the records query endpoint, list rows come from the list entries query endpoint, and the team roster comes from the workspace members endpoint.
Pagination uses limit and offset in the JSON body for the records and list entries endpoints. Attio documents defaults of
500 and 0, and its pagination guide says to stop when a response returns fewer items than the requested limit. Attio's rate limiting guide documents 100 reads per second and 25 writes per second across the API, with HTTP 429 and Retry-After
when a limit is hit.
Flattening is necessary because Attio's attribute type system returns type-specific nested values, not one flat row shape. The Apps Script side uses Properties Service for ATTIO_API_KEY, ClockTriggerBuilder for everyMinutes(1|5|10|15|30), everyHours(n), everyDays(n), and related clock schedules, UrlFetchApp for Attio HTTP calls, and Range.setValues for batch writes to Sheets.
Execution and quota limits
Google Apps Script quotas currently cap each
run at 6 min / execution for both consumer and Google Workspace accounts. The same quota table lists daily URL Fetch quotas of
20,000 calls for consumer accounts and 100,000 calls for Google Workspace accounts.
Those limits shape large exports. A big workspace may need chunking across multiple runs even when the Attio API rate limit is not the bottleneck.
Sheets also expects a rectangular array for setValues, so the transform has to build consistent headers and row widths before writing.
How setup works
Setup is mostly configuration. We scope the export first, then you install the script in your own Google account, store the Attio token in Script Properties, point the script at a Sheet, and schedule the run.
- Scope what to export. We confirm which Attio objects, lists, and workspace_members data should map to which Google Sheet tabs.
- Generate an Attio API key. In Attio, open Workspace settings, go to Developers, and generate a single-workspace API key for the workspace the script should read.
- Create the Apps Script project and paste the script. You create a Google Apps Script project in your Google account and paste the script source we share after scope.
- Add ATTIO_API_KEY to Script Properties. In Apps Script, open Project Settings, then Script Properties, and add ATTIO_API_KEY with the Attio API key as the value.
- Create the destination Google Sheet. You create the Google Sheet that will receive the export and set that Sheet ID in the script.
- Configure the WRITER_OBJECT array. Each WRITER_OBJECT entry sets the target, object_name, and sheet_name for one exported tab.
- Install a time-based trigger. Run the script once to test, then install the Apps Script time-based trigger on the agreed cadence.
Known limitations
Custom script, not an Attio app
This is delivered as Google Apps Script source for your Apps Script project. It is not an Attio Marketplace install, Attio app, or hosted connector.
Polling, not real time
Refresh timing depends on the Apps Script time-based trigger interval. The integration does not subscribe to Attio webhooks and does not update the Sheet at the instant a record changes.
6-minute per-execution cap
Apps Script execution time is capped at six minutes per run by Google. Workspaces with large object counts or many lists may need to scope each run to a subset of the data, or split the export across multiple triggered scripts, so the job completes before the cap.
Multiselects join as strings by implementation, not by Attio
Attio documents repeated value arrays for multiselect-style attributes. Comma-joining those values into one Sheet cell is a script transform choice, not an Attio-native serialization.
Location field is locality, not city
Flattened headers should use Attio's official location field names, such as location__locality, location__region, and location__country_code. A location__city header is an implementation alias, not a source-backed Attio field.
No two-way write back to Attio
The script reads from Attio and writes to Google Sheets. It does not push edited Sheet rows, formulas, or derived values back into Attio.
Trigger runs under one Google account
Installable Apps Script triggers run under the account of the person who created them. If that account loses access or is removed, the scheduled export can stop.
Script Properties protect source code, not collaborators
Script Properties keep ATTIO_API_KEY out of the script source, but anyone with edit access to the Apps Script project can run code that
reads the token. Treat Apps Script project collaborators as trusted users.
Frequently asked questions
Do I need a paid ETL connector for Attio to Sheets?
No. The stack is the Google Apps Script, a single-workspace Attio API key, and a Google account. There is no paid ETL connector, middleware service, Attio app, OAuth flow, or webhook receiver in this pattern.
Is this real-time?
No. It polls Attio on a Google Apps Script time-based trigger. If you need real-time updates, that is a different scope using a webhook-based custom app rather than this Sheets export script.
What Attio data can the script export?
It can export records from standard or custom Attio objects through the records query endpoint, entries from Attio lists through the list entries query endpoint, and the workspace member roster through the workspace members endpoint.
How does pagination work?
The Attio records and list entries query endpoints use limit and offset in the JSON body. Both default to limit 500 and offset 0. The script increments offset by the limit and stops when a page returns fewer than the requested limit.
What happens at the Attio rate limit?
Attio returns HTTP 429 with a Retry-After reset value. Attio documents that rate-limited requests are not processed, so the script can safely retry after the Retry-After time instead of immediately repeating the request.
Can it run every minute?
Yes. Apps Script time-based triggers support everyMinutes(n), where n can be 1, 5, 10, 15, or 30. Hourly, daily, and weekly clock triggers are also supported.
How long can one run take?
Google Apps Script quotas currently cap execution at 6 minutes per run for both consumer and Google Workspace accounts. Very large exports need chunking across multiple runs.
Is my Attio API key safe in Apps Script?
It is stored in Script Properties instead of the script source, which keeps the token out of the code body. Anyone with edit access to the Apps Script project can still run code that reads script properties, so protect project access accordingly.
API sources checked
- Attio REST API overview
- Attio Authentication
- Attio List records
- Attio List entries
- Attio List workspace members
- Attio Pagination guide
- Attio Rate limiting guide
- Attio Attribute types overview
- Attio Attribute types: Location
- Attio Attribute types: Email address
- Attio Attribute types: Status
- Attio Attribute types: Record reference
- Google Apps Script Quotas
- Google Apps Script Properties Service guide
- Google Apps Script PropertiesService class reference
- Google Apps Script Installable Triggers
- Google Apps Script ClockTriggerBuilder
- Google Apps Script UrlFetchApp
- Google Apps Script SpreadsheetApp / Range setValues