|
| 1 | +--- |
| 2 | +title: Airtable integration |
| 3 | +description: Automatically import items from any Apify dataset into Airtable using the Airtable Data Import Actor. Use standalone or in automated workflows. |
| 4 | +sidebar_label: Airtable |
| 5 | +sidebar_position: 1 |
| 6 | +slug: /integrations/airtable |
| 7 | +--- |
| 8 | + |
| 9 | +import ThirdPartyDisclaimer from '@site/sources/_partials/_third-party-integration.mdx'; |
| 10 | + |
| 11 | +The [Airtable Data Import Actor](https://console.apify.com/actors/f4DM1wGmMQdnTLbrE/info/readme?build=latest) transfers items from any Apify dataset into an [Airtable](https://www.airtable.com/) base. Use it standalone or chain it after other Actors in automated workflows via [integrations](/platform/integrations). |
| 12 | + |
| 13 | +<ThirdPartyDisclaimer /> |
| 14 | + |
| 15 | +## Prerequisites |
| 16 | + |
| 17 | +- An [Apify account](https://console.apify.com/) |
| 18 | +- An [Airtable account](https://www.airtable.com/) |
| 19 | +- The [Airtable Data Import Actor](https://console.apify.com/actors/f4DM1wGmMQdnTLbrE/info/readme?build=latest) |
| 20 | + |
| 21 | +## Connect to Airtable |
| 22 | + |
| 23 | +The Actor uses OAuth 2.0 to connect to your Airtable account: |
| 24 | + |
| 25 | +1. Navigate to the Actor's **Integrations** tab in Apify Console. |
| 26 | +1. Click **Connect** next to Airtable and follow the OAuth consent flow. |
| 27 | +1. Once connected, the OAuth account field is populated automatically. |
| 28 | + |
| 29 | + |
| 30 | + |
| 31 | +The Actor retrieves a fresh access token at the start of each run. Your Airtable credentials are never stored or logged. |
| 32 | + |
| 33 | +## Configure the Actor |
| 34 | + |
| 35 | +Configure the Actor from its **Input** tab in Apify Console. Each field is described below. |
| 36 | + |
| 37 | +### Operation |
| 38 | + |
| 39 | +Controls how the Actor handles the target table and its existing records. |
| 40 | + |
| 41 | +- `Append`: Adds new records to the table. Existing records are never modified or deleted. Use for continuous pipelines. |
| 42 | +- `Override`: Deletes all existing records before importing. Use for full data refreshes. |
| 43 | +- `Create`: Creates a new table with fields from your mappings, then imports data. If the table already exists, the `clearOnCreate` setting determines what happens. |
| 44 | + |
| 45 | +### Clear on create |
| 46 | + |
| 47 | +A safety switch for `Create` mode only. Defaults to `false`. |
| 48 | + |
| 49 | +- `Enabled` (`true`): Clears all existing records and imports. Use for automated fresh starts. |
| 50 | +- `Disabled` (`false`): Throws an error if the table exists, preventing accidental data loss. |
| 51 | + |
| 52 | +### Base |
| 53 | + |
| 54 | +Accepts a base ID (`appXXXXXXXXXXXXXX`, found in the Airtable URL) or a base name. Base IDs are recommended - they are immutable and globally unique. |
| 55 | + |
| 56 | + |
| 57 | + |
| 58 | +### Table |
| 59 | + |
| 60 | +Accepts a table name or table ID (`tblXXXXXXXXXXXXXX`). When using `Create` mode and the table does not exist, the Actor creates it automatically. |
| 61 | + |
| 62 | +### Dataset ID |
| 63 | + |
| 64 | +Specifies the dataset to import from: |
| 65 | + |
| 66 | +- A static dataset ID (for example, `cqxkhXcn2SCjTpeCz`). |
| 67 | +- `{{resource.defaultDatasetId}}` when used as an integration - automatically passes the upstream Actor's dataset. |
| 68 | + |
| 69 | +### Unique ID |
| 70 | + |
| 71 | +Enables duplicate detection. When set, the Actor reads existing values from the mapped Airtable column and skips records with matching values. The value must match one of the `source` fields in your `dataMappings`. |
| 72 | + |
| 73 | +### Field mappings |
| 74 | + |
| 75 | +An array defining how dataset fields map to Airtable columns. |
| 76 | + |
| 77 | + |
| 78 | + |
| 79 | +Each mapping has these properties: |
| 80 | + |
| 81 | +| Property | Description | |
| 82 | +| --- | --- | |
| 83 | +| `source` | Dataset field path, supports dot notation for nested objects (`crawl.depth`, `metadata.uid`) | |
| 84 | +| `target` | Airtable column name | |
| 85 | +| `targetType` | `existing` if the field already exists, `new` to create it automatically | |
| 86 | +| `fieldType` | Airtable field type: `singleLineText`, `multilineText`, `number`, `checkbox` | |
| 87 | + |
| 88 | +#### Dot notation |
| 89 | + |
| 90 | +Use dot notation to access nested object properties. `items[0].name` style array indexing is not supported. |
| 91 | + |
| 92 | +#### Field types |
| 93 | + |
| 94 | +| Field type | Use for | |
| 95 | +| --- | --- | |
| 96 | +| `singleLineText` | Names, URLs, IDs (max 10,000 characters) | |
| 97 | +| `multilineText` | Descriptions, paragraphs, JSON blobs | |
| 98 | +| `number` | Prices, counts, ratings (integer precision) | |
| 99 | +| `checkbox` | Boolean values, yes/no flags | |
| 100 | + |
| 101 | +:::caution Number precision |
| 102 | + |
| 103 | +The Actor creates `number` fields with integer precision. For decimal values, create the field manually in Airtable and use `targetType: "existing"`. |
| 104 | + |
| 105 | +::: |
| 106 | + |
| 107 | +## Import results from another Actor automatically |
| 108 | + |
| 109 | +Chain the Actor after a scraping or extraction Actor so its dataset flows into Airtable on every run. Use this for scheduled scrapers, recurring price checks, or any pipeline where fresh data should reach your base without a manual trigger. |
| 110 | + |
| 111 | +1. Open the upstream Actor in Apify Console and go to the **Integrations** tab. |
| 112 | +1. Click **Add integration** and select **Airtable Data Import Actor**. |
| 113 | +1. Set `datasetId` to `{{resource.defaultDatasetId}}`. |
| 114 | +1. Configure the base, table, operation, and field mappings. |
| 115 | +1. Connect your Airtable account via OAuth. |
| 116 | + |
| 117 | + |
| 118 | + |
| 119 | +## Example of the output |
| 120 | + |
| 121 | +After each run, the Actor writes a JSON summary to its dataset. Each field reports a key result from the import operation: |
| 122 | + |
| 123 | +```json |
| 124 | +{ |
| 125 | + "success": true, |
| 126 | + "operation": "Append", |
| 127 | + "baseName": "My Product Catalog", |
| 128 | + "tableName": "Products", |
| 129 | + "totalItems": 1500, |
| 130 | + "importedCount": 1450, |
| 131 | + "skippedDuplicates": 50, |
| 132 | + "duration": 330 |
| 133 | +} |
| 134 | +``` |
| 135 | + |
| 136 | +## Example configurations |
| 137 | + |
| 138 | +<details> |
| 139 | +<summary>Append with duplicate detection</summary> |
| 140 | + |
| 141 | +```json |
| 142 | +{ |
| 143 | + "operation": "Append", |
| 144 | + "base": "appABC123456789", |
| 145 | + "table": "Products", |
| 146 | + "datasetId": "{{resource.defaultDatasetId}}", |
| 147 | + "uniqueId": "url", |
| 148 | + "dataMappings": [ |
| 149 | + { "source": "title", "target": "Product Name", "targetType": "existing", "fieldType": "singleLineText" }, |
| 150 | + { "source": "price", "target": "Price", "targetType": "existing", "fieldType": "number" }, |
| 151 | + { "source": "url", "target": "URL", "targetType": "existing", "fieldType": "singleLineText" }, |
| 152 | + { "source": "description", "target": "Description", "targetType": "existing", "fieldType": "multilineText" }, |
| 153 | + { "source": "inStock", "target": "In Stock", "targetType": "existing", "fieldType": "checkbox" } |
| 154 | + ] |
| 155 | +} |
| 156 | +``` |
| 157 | + |
| 158 | +</details> |
| 159 | + |
| 160 | +<details> |
| 161 | +<summary>Create new table</summary> |
| 162 | + |
| 163 | +```json |
| 164 | +{ |
| 165 | + "operation": "Create", |
| 166 | + "clearOnCreate": false, |
| 167 | + "base": "appXYZ987654321", |
| 168 | + "table": "Customer Feedback", |
| 169 | + "datasetId": "cqxkhXcn2SCjTpeCz", |
| 170 | + "dataMappings": [ |
| 171 | + { "source": "reviewer.name", "target": "Reviewer Name", "targetType": "new", "fieldType": "singleLineText" }, |
| 172 | + { "source": "rating", "target": "Rating", "targetType": "new", "fieldType": "number" }, |
| 173 | + { "source": "comment", "target": "Review Text", "targetType": "new", "fieldType": "multilineText" }, |
| 174 | + { "source": "verified", "target": "Verified Purchase", "targetType": "new", "fieldType": "checkbox" } |
| 175 | + ] |
| 176 | +} |
| 177 | +``` |
| 178 | + |
| 179 | +</details> |
| 180 | + |
| 181 | +<details> |
| 182 | +<summary>Override for full refresh</summary> |
| 183 | + |
| 184 | +```json |
| 185 | +{ |
| 186 | + "operation": "Override", |
| 187 | + "base": "Daily Competitor Prices", |
| 188 | + "table": "Pricing", |
| 189 | + "datasetId": "{{resource.defaultDatasetId}}", |
| 190 | + "dataMappings": [ |
| 191 | + { "source": "sku", "target": "SKU", "targetType": "existing", "fieldType": "singleLineText" }, |
| 192 | + { "source": "competitor", "target": "Competitor", "targetType": "existing", "fieldType": "singleLineText" }, |
| 193 | + { "source": "price", "target": "Price", "targetType": "existing", "fieldType": "number" } |
| 194 | + ] |
| 195 | +} |
| 196 | +``` |
| 197 | + |
| 198 | +</details> |
| 199 | + |
| 200 | +## Limits |
| 201 | + |
| 202 | +- Text values are truncated at 10,000 characters per field. |
| 203 | +- `number` fields are created with integer precision; create decimal fields manually. |
| 204 | +- Imports process at approximately 50 records per second (10 per batch, 200ms delay). |
| 205 | +- Progress is automatically persisted - if a run is interrupted, it resumes from where it left off. |
| 206 | + |
| 207 | +## Next steps |
| 208 | + |
| 209 | +- [Learn about Apify integrations](/platform/integrations) to chain Actors in automated workflows. |
| 210 | +- [Explore Airtable's API documentation](https://airtable.com/developers/web/api/field-model) for field type details. |
| 211 | +- If you have questions about the Airtable integration, reach out via the support live chat in Console or the [developer community on Discord](https://discord.com/invite/jyEM2PRvMU). |
0 commit comments