---
title: "How to Export Data from Attio: API Limits, CSV, and Portability"
slug: how-to-export-data-from-attio-api-limits-csv-and-portability
date: 2026-09-16
author: Roopendra Talekar
categories: [Attio]
excerpt: "Learn every method to export data from Attio — CSV views, workspace backups, and the REST API — plus real rate limits, enriched data restrictions, and migration trade-offs."
tldr: "Attio offers CSV view exports, admin-only workspace backups (ZIP of CSVs), and a REST API capped at 100 reads/sec. Enriched data cannot be exported — plan around that gap."
canonical: https://clonepartner.com/blog/how-to-export-data-from-attio-api-limits-csv-and-portability
---

# How to Export Data from Attio: API Limits, CSV, and Portability


# How to Export Data from Attio: API Limits, CSV, and Portability

Attio gives you three ways to get data out: **native CSV/Excel export** from any view, a **full workspace backup** (admin-only, zipped CSVs), and the **REST API** for programmatic extraction at up to 100 read requests per second. Each method has blind spots — most notably, enriched data cannot be exported through any of them — so picking the right approach depends on what you need, how much of it, and whether it contains enriched attributes.

This guide covers each extraction method in detail, documents the real API constraints, and flags the edge cases encountered across large-scale CRM migrations.

## Which export method should you use?

Use this decision tree before reading further:

```
Is this a one-time export?
├── Yes → Do you need enriched attribute values?
│   ├── Yes → Use API (read enriched fields → write to custom attributes → export CSV)
│   └── No → Under 10,000 records per object?
│       ├── Yes → CSV export from view (fastest)
│       └── No → API with pagination (avoids view column limits and row truncation)
└── No (recurring) → Do you need relationship IDs preserved?
    ├── Yes → Custom API script or Flatly to cloud storage
    └── No → Coefficient (scheduled Google Sheets refresh) or Zapier/Make
```

If you're doing a [full migration to another CRM](https://clonepartner.com/blog/blog/the-ultimate-crm-data-migration-checklist-a-10-point-plan-for-a-zero-loss-transition), always use the API. [CSV exports flatten relationships into column values](https://clonepartner.com/blog/blog/csv-saas-data-migration) and drop enriched fields. The API preserves linked record IDs, which is what you need to reconstruct the relationship graph in a target system.

## What data can you export from Attio?

**Attio's exportable data** includes records (Companies, People, Deals, Users, Workspaces), custom objects, list entries, notes, tasks, comments, files, and attribute configurations. Attio is a [relationship-intelligence CRM built around records, lists, and attributes](https://clonepartner.com/blog/blog/ultimate-guide-attio-crm-2025), with a highly flexible data model where you define the objects and attributes that matter to your team.

What you **cannot** export is equally important:

- It is not possible to export enriched data from Attio. If you overwrite Attio's enriched data manually, data that you have entered yourself is able to be exported. If a view includes enriched attributes, those cells will be blank in the exported file.
- **Emails** synced into Attio cannot be exported at all — not through the UI and not through the workspace backup.
- **Meetings and call recordings** are excluded from the workspace export but can be pulled via dedicated API endpoints.

This enriched-data limitation is the single biggest surprise for teams planning a migration off Attio. Enriched attributes — things like auto-populated company descriptions, employee counts, and social handles sourced by Attio — render as blank cells in every CSV export. This is a **licensing constraint, not a technical bug**: Attio's enrichment data is sourced and maintained by Attio under third-party data agreements, and they contractually cannot allow it to leave the platform.

## How to export a view or list as CSV

You can export any table or kanban view in Attio as a CSV or Excel file. Every column visible in your table view will become a column in the exported file. For kanban views, the attributes shown on each card are exported instead.

### Steps

1. Navigate to the object or list you want to export (e.g., Companies, People, a custom object, or any list).
2. Make sure your table view includes columns for all the attributes you want to export. If you're exporting a kanban view, ensure the cards show the attributes you'd like to include. To ensure your export reflects any filters you've applied, click **Save for everyone** before exporting.
3. Click **Import / Export** on the top right of any view, then select **Export view as CSV** or **Export view as Excel**. Once downloaded, open the file in Excel, Google Sheets, Numbers, or any similar application.

### Who can export

By default, all workspace members can export views on all records pages. Full access, read and write, and read only members can export views on lists. Workspace admins on the Enterprise plan can limit exports to admins only: Click your workspace name in the upper-left and choose Workspace settings from the dropdown. In the sidebar on the left, click Security. Under Advanced data controls, toggle on **Disable data exports for non-admins**.

> [!WARNING]
> The CSV export only includes columns visible in the current view. If you need all attributes, add every column before exporting. Hidden columns are silently omitted.

### Formula injection protection

Attio includes an optional setting that protects against formula injection in CSV exports. It is available to workspace admins on all plans, and is disabled by default. Formula injection occurs when data containing spreadsheet formulas has been entered into a text attribute. When exported and opened in Excel or a similar application, those formulas may execute automatically. When this setting is enabled, Attio adds a prefix to text field values to prevent that from happening.

**Important operational note**: Enabling this protection prepends characters (typically a tab character `\t` or single quote `'`) to text values. If you're piping CSVs into an automated pipeline that expects raw strings, this will break field matching. Test against your downstream system before enabling in production.

### CSV encoding and format details

Attio exports use **UTF-8 encoding**. Multi-select values are exported as comma-separated strings within a single cell. Linked record values are exported as display names (e.g., a company name), not as record IDs — which is why the API is required when you need to preserve relationship links. Date fields export in ISO 8601 format (`YYYY-MM-DD`).

## How to run a full workspace backup

**Attio's workspace export** generates a downloadable ZIP file containing multiple CSV files covering your entire workspace. It is the closest thing to a full database dump that the Attio UI offers.

### What's included in the workspace export

The workspace backup contains CSVs for:

- Companies, People, Deals, Users, and Workspaces
- Custom objects
- Lists and list attributes
- Attribute options (select/multi-select values)
- Notes
- Tasks
- Comments
- Files
- Call recording activity (metadata only — no audio, video, or transcripts)
- Workspace members

### What's excluded

| Data type | Exportable via workspace backup? | Alternative |
|---|---|---|
| Emails | No | Not exportable through any method |
| Meetings | No | API: `GET /v2/meetings/{meeting_id}` |
| Call recordings (audio/video/transcripts) | No | API: `GET /v2/call-recordings/{call_recording_id}` |
| Enriched attribute values | No | Manually overwrite first, or use API-to-custom-attribute workaround |

### Steps

1. Click your workspace name in the upper-left and select **Workspace settings**.
2. Under the **General** tab, click **Start new export** under **Export Workspace data**.
3. Wait for the export to reach 100%, then click the download icon.

Workspace exports are restricted to admins. Non-admin users cannot trigger or download a workspace backup, regardless of their object-level permissions.

### Workspace backup failure modes

- **Export stalls at 99%**: This typically indicates a timeout on large workspaces. Retry during off-peak hours (overnight) when API load is lower. There is no partial resume — the export restarts from scratch.
- **ZIP file size**: No documented maximum, but workspaces with 500k+ records and large file attachments have produced ZIPs exceeding several gigabytes. Ensure you have local disk space before downloading.
- **Encoding issues**: All CSVs in the backup use UTF-8. If you're opening in Excel on Windows, use **Data → From Text/CSV** and specify UTF-8 explicitly to avoid mojibake on non-ASCII characters.
- **Missing custom object CSVs**: If a custom object has zero records, it may be omitted from the ZIP entirely. Don't interpret absence as an export failure.

> [!TIP]
> Run a workspace backup *before* any bulk operation — bulk deletes, migrations, or mass attribute changes. Attio has no native undo for bulk actions, and this ZIP is your safety net.

## How to export data using the Attio API

The Attio REST API (v2) gives you full programmatic access to records, list entries, notes, tasks, and more in JSON format. Use it for automated CRM pipelines, migrating from systems not easily exported as CSV, or building custom integrations.

### Authentication

The API uses OAuth 2.0 or API key authentication. For one-off exports and migrations, an API key is simpler. Generate one at **Workspace Settings → Developers**. For production integrations that access multiple workspaces, use OAuth 2.0.

Required scopes for a read-only export:
- `record_permission:read` — to read records
- `object_configuration:read` — to read object and attribute schemas
- `list_entry:read` — to read list entries

### Pagination

Attio uses **offset-based pagination**. The `limit` parameter controls the maximum number of results an endpoint can return. The `offset` parameter controls how many values to skip over in the result set, defaulting to 0.

| Endpoint | Default limit | Maximum limit |
|---|---|---|
| List records (`POST /objects/{slug}/records/query`) | 500 | 500 |
| List record entries (`GET /lists/{list_id}/entries`) | 100 | 1,000 |
| List notes | 100 | 100 |
| List tasks | 100 | 100 |

**Offset pagination gap risk**: If records are created or deleted mid-pagination, offset-based pagination can skip or duplicate records. For large exports, sort on `created_at` ascending and filter by a stable time range window to get a consistent snapshot. Alternatively, record all returned `record_id` values and deduplicate after collection.

A basic pagination loop in Python:

```python
import requests

API_KEY = "your_api_key"
BASE = "https://api.attio.com/v2"
headers = {"Authorization": f"Bearer {API_KEY}"}

def export_all_records(object_slug: str):
    records = []
    offset = 0
    limit = 500
    while True:
        resp = requests.post(
            f"{BASE}/objects/{object_slug}/records/query",
            headers=headers,
            json={"limit": limit, "offset": offset},
        )
        resp.raise_for_status()
        batch = resp.json()["data"]
        records.extend(batch)
        if len(batch) < limit:
            break  # Last page reached
        offset += limit
    return records
```

> [!NOTE]
> The List records endpoint accepts a `filter_view_id` parameter. Pass the UUID of a saved view, and results are filtered using that view's filter configuration. This cannot be used together with `filter`. Note: sorts, limits, and offsets are applied independently and are not taken from the view. All attributes are returned regardless of which attributes are visible in the view.

### What enriched fields look like in API responses

Unlike CSV exports (where enriched fields are blank), the REST API **does return enriched attribute values**. In the JSON response, enriched attributes appear identically to user-entered attributes — there is no explicit `is_enriched` flag on the value itself. However, the attribute's schema (retrievable via `GET /v2/objects/{object_slug}`) includes metadata that identifies it as an enrichment-sourced attribute.

A record response with both enriched and non-enriched attributes looks like this:

```json
{
  "id": {"record_id": "abc123"},
  "values": {
    "name": [{"value": "Acme Corp", "active_from": "2024-01-01T00:00:00Z"}],
    "description": [{"value": "Enterprise software company", "active_from": "2024-01-01T00:00:00Z"}],
    "employee_range": [{"value": {"option_title": "51-200"}, "active_from": "2024-01-01T00:00:00Z"}],
    "linkedin_url": [{"value": "https://linkedin.com/company/acme", "active_from": "2024-01-01T00:00:00Z"}]
  }
}
```

In this example, `description`, `employee_range`, and `linkedin_url` may be enriched fields — they look structurally identical to `name`. To distinguish them, query the object schema and check for enrichment metadata on each attribute definition.

This API-visible enriched data is what makes the Option 3 workaround (see below) functional: you can read the enriched value via API, write it into a custom non-enriched attribute, and then export it via CSV or backup.

## What are Attio's API rate limits?

The Attio API applies rate limits to all API calls to ensure fair usage and maintain platform availability. The rate limit across the whole API is **100 requests per second for read requests** and **25 requests per second for write requests**.

That 100 reads/second ceiling sounds generous, but there's a second layer:

Attio's List records and List entries endpoints also utilize **score-based rate limits**. Each request receives a complexity score. The score is a function of the request's sorts and filters as well as the total record/entry count for the object/list. Scores may trigger rate limits in two ways: the score for an individual query may exceed the per-query limit (in which case, reduce the complexity of your query and try again), or the summed scores across multiple queries may exceed the limit for a given window of time. Attio uses a **sliding window algorithm with a 10-second window**. Scores are summed across all apps and access tokens using the API.

### Score-based rate limit behavior in practice

The complexity score is not publicly documented as an exact formula, but observed behavior follows a consistent pattern:

| Query type | Object size | Observed behavior |
|---|---|---|
| Unfiltered, unsorted | < 10,000 records | Rarely triggers score limit |
| Unfiltered, unsorted | 50,000–100,000 records | Occasionally triggers per-query limit |
| Filtered + sorted on indexed field | 80,000 records | Frequently triggers per-query limit at maximum limit (500) |
| Filtered + sorted on non-indexed field | Any size | High likelihood of per-query score limit |
| Unfiltered, paginating sequentially | 100,000+ records | Triggers windowed cumulative limit after ~40–50 sequential requests |

**Critical for multi-integration workspaces**: If you're running multiple integrations against the same workspace (Zapier + a custom script + a sync tool), their complexity scores stack within the same 10-second window. Running a migration script while other integrations are active can push total scores over the windowed limit even when each individual script would be fine in isolation. Temporarily disable or throttle other API consumers during large extractions.

You will receive a `429` HTTP status and a `Retry-After` header when you hit any rate limit. Attio may occasionally reduce the rate limit as part of incident response, and may permanently lower it for specific APIs that handle large data volumes.

### Retry-After header format

**Important**: The `Retry-After` header from Attio may be returned as an **HTTP date string** (e.g., `Wed, 21 Oct 2025 07:28:00 GMT`), not as an integer number of seconds. Most HTTP client libraries and retry implementations assume integer format. Parsing an HTTP date string as an integer will silently produce incorrect wait times (typically `NaN` or `0`), causing immediate retry loops that accelerate rate limit exhaustion.

```python
import time
from datetime import datetime, timezone
import requests

def request_with_backoff(method, url, **kwargs):
    for attempt in range(5):
        resp = method(url, **kwargs)
        if resp.status_code != 429:
            return resp
        retry_after = resp.headers.get("Retry-After")
        if retry_after:
            # Retry-After may be an HTTP date string, not an integer
            try:
                retry_date = datetime.strptime(
                    retry_after, "%a, %d %b %Y %H:%M:%S %Z"
                ).replace(tzinfo=timezone.utc)
                wait = (retry_date - datetime.now(timezone.utc)).total_seconds()
            except ValueError:
                # Fall back to treating as integer seconds
                wait = float(retry_after)
        else:
            wait = 2 ** attempt  # Exponential backoff if no header
        time.sleep(max(wait, 0.5))
    resp.raise_for_status()
```

## What data types require the API to export?

Several Attio data types are **only accessible via the API** and cannot be exported through the UI at all:

- **Meetings**: Retrievable through `GET /v2/meetings/{meeting_id}`. The workspace backup excludes them entirely.
- **Call recordings** (audio/video/transcripts): Available through `GET /v2/call-recordings/{call_recording_id}`. The workspace backup only includes metadata.
- **Webhook configurations**: Readable via API but there is no bulk event log endpoint. Webhook history is not exportable.
- **Record relationship graph**: The UI export flattens relationships into display-name column values. The API returns linked record IDs (e.g., `{"record_id": "abc123"}`), which is what you need to reconstruct the relationship graph in a target system.

For migrations, the API's ability to [preserve record IDs and relationship links](https://clonepartner.com/blog/blog/affinity-to-attio-migration-guide) is the critical difference between a flat data dump and a reconstructable data model.

## Enriched data: the portability gap

**Enriched data in Attio** refers to attributes that Attio automatically populates from its own data sources — company descriptions, employee counts, social handles, logos, and similar fields. Attio automatically enriches person and company records with useful data points like logos and profile pictures, social media handles, and descriptions. When Attio has new data it will automatically update enriched attributes so your data is always current.

The catch: it is not possible to export enriched data from Attio. If you overwrite Attio's enriched data manually, data that you have entered yourself is able to be exported.

Enriched attributes are visually distinguished in the UI with lilac-colored cells and a sparkle icon in column headers. In CSV exports, those cells are blank regardless of what value is displayed in the UI.

**Three workarounds, ranked by scalability:**

**Option 1 — Manual overwrite** (impractical at scale): If you manually edit an enriched field, the value becomes "yours" and exports normally. Feasible for fewer than 50 records.

**Option 2 — Third-party re-enrichment** (best for ongoing use): Use a provider like Apollo, Clearbit, or Clay to re-enrich records after migration. Attio has a native Apollo enrichment integration that stores data in dedicated custom fields that *are* exportable. This is the right approach if you're migrating to a CRM that will also need ongoing enrichment.

**Option 3 — API snapshot before export** (best for large one-time migrations): 
1. Use the API to read all records including enriched field values (they are returned in API responses, as shown in the JSON example above).
2. For each enriched attribute, write the value back into a custom non-enriched attribute of the same type (e.g., a text attribute named `company_description_snapshot`).
3. Export those custom attributes via CSV or workspace backup.

This option is fully scriptable. A workspace with 20,000 company records can be snapshotted in under 30 minutes at 25 writes/second, accounting for rate limits.

## Third-party export tools

| Tool | Method | Output | Best for |
|---|---|---|---|
| **Coefficient** | Attio API via add-in | Google Sheets / Excel | Recurring live reports |
| **Flatly** | Attio API | CSV to cloud storage | Automated backups |
| **Zapier / Make** | Attio triggers + actions | Any connected app | Event-driven sync |
| **Custom script** | Direct REST API | JSON / CSV / database | Full control, migrations |

With Coefficient, you choose the object type (Contacts, Companies, or custom objects), select attributes, apply filters, and set a scheduled run to refresh data automatically. This is useful for dashboards and reporting but not for full migrations — the data arrives in spreadsheet format without relationship IDs.

For a complete data extraction that preserves record links, custom API scripts remain the most reliable approach.

## How to plan a complete Attio data extraction

Threshold guidance before you start:

- **Under 5,000 records, no enriched data, one-time export**: CSV export from views is sufficient. Total time: under 10 minutes.
- **5,000–50,000 records, some enriched data**: API extraction with pagination. At 500 records/request and 100 requests/second, pulling 50,000 records takes under 2 seconds of pure API time — but score-based limits and write-back for enriched fields will extend this to 15–30 minutes in practice.
- **50,000+ records, complex relationships, enriched data**: Budget 60–120 minutes for full extraction. Score-based rate limits become the primary constraint above this scale.
- **100,000+ records across multiple objects**: Expect 2–4 hours. Run extractions sequentially by object, not in parallel, to avoid stacking score-based limits.

A full extraction follows this sequence:

1. **Inventory your objects**: List every object type (standard and custom) and count records per object using `GET /v2/objects`. Note which objects have enriched attributes.
2. **Map attributes**: For each object, query `GET /v2/objects/{slug}` to document all attributes — including enriched ones. Plan your Option 3 workaround for enriched fields.
3. **Export structured data via API**: Pull records per object with pagination, preserving record IDs. Pull list entries separately with their entry IDs.
4. **Export notes, tasks, and comments**: These are linked to records via parent record IDs. Use the `/v2/notes` and `/v2/tasks` endpoints with record ID filters.
5. **Run a workspace backup as a safety net**: Even with API as your primary extraction, the backup gives you a cross-reference.
6. **Validate completeness**: Compare record counts between API response totals and workspace backup CSVs. Mismatches indicate pagination gaps (concurrent record creation/deletion) or rate-limit-induced missed pages.

> [!WARNING]
> Attio's API does not have a bulk export endpoint. You extract data object by object, paginated. For a workspace with 100,000+ records across multiple objects, budget 60–120 minutes for a full extraction depending on query complexity and rate limit headroom.

## Common pitfalls when exporting from Attio

- **Enriched data blindspot**: You'll export the CSV, open it, and find critical columns blank. Always check which attributes are enriched (lilac cells, sparkle icon) before relying on a CSV export.
- **View filter not saved**: To ensure your export reflects any filters you've applied, click **Save for everyone** before exporting. Unsaved filters are ignored during export.
- **Score-based rate limiting with multiple integrations**: Two integrations hitting the same workspace can starve each other within the 10-second scoring window. Temporarily disable other API consumers during large migrations.
- **Offset pagination gaps during concurrent writes**: If records are created or deleted mid-pagination, you can skip or duplicate records. Sort on `created_at` ascending and use time-range filtering for consistent snapshots.
- **Retry-After as date string**: Most HTTP clients expect `Retry-After` to be an integer. Attio may return an HTTP date string. Parsing a date string as a float returns `NaN`, causing immediate retry loops. Parse as shown in the code sample above.
- **Custom object CSVs missing from workspace backup**: Objects with zero records may be omitted from the ZIP. Don't interpret absence as an export failure.
- **Formula injection prefix breaking pipelines**: Enabling the formula injection protection setting prepends characters to all text values. This will break exact-match logic in downstream ETL pipelines. Test before enabling.

## Method comparison summary

| Criteria | CSV export | Workspace backup | REST API |
|---|---|---|---|
| Access level | All members (admins can restrict) | Admins only | API key required |
| Enriched data | ❌ Blank cells | ❌ Blank cells | ✅ Returned in responses |
| Relationship IDs | ❌ Display names only | ❌ Display names only | ✅ Preserved |
| Emails | ❌ Not included | ❌ Not included | ❌ Not accessible |
| Meetings | ❌ Not included | ❌ Not included | ✅ Via dedicated endpoint |
| Max records per operation | View row limit | Full workspace | 500/request (paginated) |
| Output format | CSV / Excel | ZIP of CSVs | JSON |
| Best for | Quick snapshots, reporting | Pre-migration safety net | Migrations, integrations |

## Frequently asked questions

### Can you export enriched data from Attio?

No. Attio's auto-enriched attributes (company descriptions, employee counts, social handles) export as blank cells in CSV and workspace backups. Manually overwritten values do export. To preserve enriched data during a migration, copy values into custom non-enriched attributes via the API before exporting.

### What are Attio's API rate limits?

Attio allows 100 read requests per second and 25 write requests per second globally. List records and list entries endpoints also have score-based limits using a 10-second sliding window — complex filters and sorts consume more score. Exceeding limits returns HTTP 429 with a Retry-After header.

### How do I do a full workspace backup in Attio?

Go to Workspace Settings → General → Export Workspace data and click Start new export. Attio generates a ZIP file with CSVs covering records, lists, notes, tasks, comments, files, and more. Only admins can run workspace exports. Emails, meetings, and call recordings are excluded.

### What data is not included in Attio's workspace export?

The workspace export excludes emails (not exportable at all), meetings (API only), call recordings audio/video (API only — metadata is included), and all enriched attribute values. Use the Attio REST API for meetings and call recordings.

### Does Attio have a bulk export API endpoint?

No. Attio does not offer a single bulk-export endpoint. You extract data object by object using paginated queries (default limit 500 records per page). For large workspaces with 100k+ records, a full API extraction typically takes 30–60 minutes depending on complexity and rate limit usage.
