# Airtable Base Documentation & ER Diagram Generator (`mediocre_interest/airtable-base-documentation`) Actor

Document any Airtable base from a read-only token: a Markdown data dictionary for every table and field, plus a Mermaid ER diagram of the relationships, as a shareable HTML page. Formulas render with real field names instead of field IDs.

- **URL**: https://apify.com/mediocre\_interest/airtable-base-documentation.md
- **Developed by:** [Mediocre\_Interest](https://apify.com/mediocre_interest) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / base documented

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Airtable Base Documentation & ER Diagram Generator

**Turn any Airtable base into handover documentation — a data dictionary for every table and field, plus an entity relationship diagram — from a read-only token, in seconds.** Leave the token empty and the Actor documents a bundled sample base for free, so you can see exactly what you get before connecting anything. Every other Airtable Actor on Apify Store moves *records* in or out; this one reads the **schema** and never touches a single row of your data.

- **One scope, read-only.** `schema.bases:read` is all it needs. It cannot read your records, and the scope is available on every Airtable plan, including Free.
- **Formulas you can actually read.** Airtable's API returns formulas as `DATETIME_DIFF({fldCLSO8JcfeAnf5Q}, TODAY(), 'days')`. This Actor prints `DATETIME_DIFF({Deadline}, TODAY(), 'days')`.
- **Relationships counted once.** Airtable stores one link as two fields; the Actor collapses them, so 8 link fields become the 4 relationships they really are.
- **Three files per base**, on shareable links: a Markdown data dictionary, an HTML page, and Mermaid diagram source that GitHub, GitLab, Notion and Obsidian render natively.

### What does Airtable Base Documentation & ER Diagram Generator do?

An Airtable base grows by accretion. Someone adds a lookup, someone else adds a rollup that depends on it, a formula references a field that has since been renamed, and a year later nobody can say which tables link to which — or which of the dozens of fields in `Projects` anything still uses. Airtable's own interface shows you one table at a time and offers no schema export, so the usual answer is a screenshot and a hopeful Loom.

This Actor reads your base's **structure** through Airtable's metadata API and writes the document you would otherwise make by hand. For each base it produces a **Markdown data dictionary** — every table, every field, its type, its configuration and its description — a **Mermaid ER diagram** of how the tables link, and a **shareable HTML page** carrying both. It also writes one dataset row per table, so the whole estate is a spreadsheet you can sort, filter and export.

It reads no records. The metadata API returns names, types, options and descriptions; it never returns a cell value. One base is one HTTP request, so documenting a 40-table base takes about as long as documenting a 4-table one.

#### Which parts of an Airtable base does it document?

| What it reads            | What you get                                                                                                                                          |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| Tables                   | Name, description, table ID, field count, view count, primary field                                                                                   |
| Fields                   | Name, human-readable type, description, and the configuration that matters — select options, currency symbol and precision, rating scale, date format |
| Formulas                 | The expression with **field IDs substituted back to field names**, and a loud flag when Airtable reports the formula as broken                        |
| Rollups, lookups, counts | The far table and the field they reach through, derived from the link field rather than from Airtable's empty `referencedFieldIds`                    |
| Links between tables     | One relationship per pair, not one per field, with many-to-many, one-to-many and self-referential links distinguished                                 |
| One-way and self links   | Named as such — a self-link is drawn as a single loop, not two tables                                                                                 |
| Views (optional)         | Each view, its type, and the fields it shows                                                                                                          |

### Why use this Airtable base documentation Actor?

- **Hand a client or a new hire something real.** A signed link to an HTML page beats a screen share. The page renders the ER diagram in the browser and lists every field beneath it.
- **Nothing to install, and a free way to see the output.** Run it with an empty input and it documents a bundled 4-table sample base at no charge — no Airtable account, no token, no credit spent.
- **Read-only by construction.** The only scope it asks for is `schema.bases:read`, which cannot read records, cannot write anything, and exists on every Airtable billing plan.
- **Deterministic — no AI model anywhere.** Every number in the output is counted from one API response. Run it twice on an unchanged base and you get the same document twice.
- **Built for agencies and ops teams** who inherit other people's bases: point it at a token with ten bases and it documents all ten in one run.
- **It is an Apify Actor**, so you also get scheduling, a JSON API, exports to JSON, CSV and Excel, run monitoring, and the Make, n8n and Zapier integrations.

### How to document an Airtable base

Try it with nothing at all:

1. Click **Start** with the input left exactly as it is.
2. Wait a few seconds. The run documents a bundled sample base — 4 tables, 40 fields, 4 relationships.
3. Open the **Output** tab for the table rows, or the **Storage** tab for the page, the data dictionary and the diagram source.

Then document your own base:

1. Go to [airtable.com/create/tokens](https://airtable.com/create/tokens) and create a personal access token.
2. Give it the single scope **`schema.bases:read`**.
3. Under **Access**, add the bases you want documented. A base must be listed here or the Actor cannot see it.
4. Paste the token into **Airtable personal access token** and click **Start**.
5. Leave **Bases to document** empty to document every base the token can see, or paste base IDs — or whole base URLs — one per line.

#### Which Airtable token scope do you need?

Exactly one: **`schema.bases:read`**. It is a read-only scope, it is available on the Free plan, and a collaborator with read-only access to a base can create a token that covers it. The Actor never requests `data.records:read` and cannot read your records with the token you give it. This was verified for this listing with a token holding `schema.bases:read` and nothing else: it produced the same documentation as a fully privileged token.

### Input

| Field                              | What it does                                                                                                                                                                                   |
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Airtable personal access token** | Your Airtable PAT, needing only `schema.bases:read`. **Leave it empty to document the bundled sample base for free.** Stored as a secret.                                                      |
| **Bases to document**              | Base IDs, one per line — or paste whole Airtable URLs and the ID is taken out of them. Empty means every base the token can see.                                                               |
| **What to produce**                | `both`, `markdown` or `html`. The Mermaid diagram source is written as its own `.mmd` file in every case, unless you switch the diagram off below.                                             |
| **Include views**                  | List each table's views and the fields each one shows. On by default.                                                                                                                          |
| **Maximum tables in the diagram**  | Stop drawing the ER diagram after this many tables, keeping the most connected ones. Default 40; `0` skips the diagram entirely. The data dictionary and the dataset always cover every table. |

See the sample base, free, with no token:

```json
{}
```

Document two named bases and produce both files:

```json
{
    "airtableToken": "<your Airtable personal access token>",
    "baseIds": ["appXXXXXXXXXXXXXX", "https://airtable.com/appYYYYYYYYYYYYYY/tblZZZ"],
    "format": "both",
    "includeViews": true,
    "maxTablesInDiagram": 40
}
```

### Output

Every run writes **one row per table** to the default dataset, and three files per base to the key-value store. You can download the dataset in JSON, CSV, Excel, HTML or XML from the Output tab, or pull it from the API.

This is a real row from the free sample run — the `{}` input above:

```json
{
    "source": "sample",
    "collectedAt": "2026-09-28T12:03:00.998Z",
    "baseId": "appSAMPLEdemo0001",
    "baseName": "Sample Agency CRM (demo)",
    "basePermissionLevel": "read",
    "tableId": "tblkkmzN0BZxKqCDp",
    "tableName": "Projects",
    "tableDescription": "Billable engagements. One record per statement of work.",
    "primaryFieldName": "Project Name",
    "primaryFieldType": "singleLineText",
    "fieldCount": 18,
    "viewCount": 1,
    "linkFieldCount": 4,
    "relationshipCount": 3,
    "relatedTables": ["Clients", "Projects", "Tasks"],
    "oneWayLinkCount": 0,
    "selfLinkCount": 1,
    "formulaFieldCount": 1,
    "rollupFieldCount": 1,
    "lookupFieldCount": 1,
    "countFieldCount": 1,
    "invalidComputedFieldCount": 0,
    "tableHasDescription": true,
    "fieldsWithDescription": 1,
    "markdownUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/documentation-appSAMPLEdemo0001.md?signature=...",
    "htmlUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/documentation-appSAMPLEdemo0001.html?signature=...",
    "diagramUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/diagram-appSAMPLEdemo0001.mmd?signature=..."
}
```

#### What does each table row contain?

| Columns                                                                        | What they mean                                                                                           |
| ------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------- |
| `baseId`, `baseName`, `basePermissionLevel`                                    | The base this table belongs to and the access level the token has on it                                  |
| `tableId`, `tableName`, `tableDescription`                                     | The table, and the description someone wrote in Airtable — `null` if nobody did                          |
| `fieldCount`, `viewCount`                                                      | How many fields and views the table has                                                                  |
| `primaryFieldName`, `primaryFieldType`                                         | Airtable's primary field, which cannot be a computed type                                                |
| `relationshipCount`, `relatedTables`                                           | Distinct relationships this table takes part in, and the tables on the other side                        |
| `linkFieldCount`, `oneWayLinkCount`, `selfLinkCount`                           | Link fields on the table, how many links have no reciprocal field, and how many point back at this table |
| `formulaFieldCount`, `rollupFieldCount`, `lookupFieldCount`, `countFieldCount` | Computed fields by kind                                                                                  |
| `invalidComputedFieldCount`                                                    | Computed fields Airtable itself reports as broken — usually a formula referencing a deleted field        |
| `tableHasDescription`, `fieldsWithDescription`                                 | Documentation coverage, so you can see which tables nobody has described                                 |
| `markdownUrl`, `htmlUrl`, `diagramUrl`                                         | Signed links to this base's three files. Hand these out — they are the shareable URLs                    |
| `source`, `collectedAt`                                                        | `airtable` or `sample`, and when the schema was read                                                     |

#### What files does each base produce?

| File                          | Content type       | What it is                                                                                                                                      |
| ----------------------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `documentation-{baseId}.md`   | `text/markdown`    | The data dictionary: contents list, the ER diagram as a fenced Mermaid block, then a section per table with its fields, relationships and views |
| `documentation-{baseId}.html` | `text/html`        | The same document as a self-contained page that renders the diagram in the browser. This is the link to send a client                           |
| `diagram-{baseId}.mmd`        | `text/plain`       | The Mermaid `erDiagram` source on its own, for pasting into GitHub, GitLab, Notion or Obsidian                                                  |
| `summary-run`                 | `application/json` | Run totals: bases and tables documented, relationships found, anything skipped and why                                                          |

Use the `markdownUrl`, `htmlUrl` and `diagramUrl` from the dataset row to reach these. Those links are signed; a key-value store URL you assemble by hand will be rejected.

#### How are relationships counted?

Airtable stores one relationship between two tables as **two link fields**, one in each table, and its API returns both. Counting fields would double every edge, so the Actor collapses each pair into a single relationship and reports both numbers: `linkFieldCount` is the fields on the table, `relationshipCount` is the distinct relationships it takes part in. In the sample base, 8 link fields are 4 relationships. A link with no field on the other side is counted as one-way, and a link from a table to itself is drawn as a single self-loop rather than two entities.

### How much does it cost to document an Airtable base?

The Actor is **pay per event**, at the prices shown on this page. You are charged for output, never for time.

| Event                | Charged                                                              | When                                                           |
| -------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------- |
| **Base documented**  | $0.50 per base                                                       | After the base's data dictionary, page and diagram are written |
| **Table documented** | $0.03 per table on the Free plan, falling to $0.02 on Gold and above | After each table's row reaches the dataset                     |

So a base costs **$0.50 plus $0.03 a table**: a 5-table base is $0.65, a 12-table base $0.86, and two bases totalling six tables come to $1.18. Platform compute is negligible beside that — a run at the default 1 GB finishes in a few seconds and costs well under a cent.

Two things are free. **The sample base is free**: an empty token documents it with no charge at all. And a base is **documented whole or not charged at all** — if what is left of your budget cannot cover a base and all of its tables, that base is skipped entirely and named in the run's status message, because half a data dictionary is not worth paying for. Set **Maximum total charge** on the run if you are pointing a many-base token at it for the first time.

### How to run Airtable base documentation from the API, Make, n8n or a schedule

Call the Actor and get the table rows back in one request:

```bash
curl -X POST "https://api.apify.com/v2/acts/mediocre_interest~airtable-base-documentation/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
  -H 'Content-Type: application/json' \
  -d '{ "airtableToken": "<your Airtable personal access token>", "format": "both" }'
```

That returns the dataset as JSON — one object per table, exactly like the row above. Add `&format=csv` for a spreadsheet, or `&fields=baseName,tableName,fieldCount,relationshipCount,htmlUrl` to trim it to the columns you want. Send `{}` as the body to get the free sample rows back instead.

From there it is an ordinary Apify Actor: schedule it monthly so the documentation never goes stale, connect it to **Make**, **n8n** or **Zapier** through their Apify apps, or trigger it from a webhook and post the `htmlUrl` into Slack when the run finishes.

### Tips for better results

- **Write descriptions in Airtable first.** The Actor copies table and field descriptions straight through, and `fieldsWithDescription` tells you where the gaps are. Ten minutes of descriptions turns a field list into documentation.
- **Cap the diagram on a large base.** A 200-table ER diagram is unreadable. Leave **Maximum tables in the diagram** at 40 and the Actor keeps the most connected tables; the data dictionary still covers every one of them.
- **Name the bases you want.** On a token that can see twenty bases, an empty **Bases to document** documents all twenty and charges for all twenty. Paste the IDs you actually need.
- **Turn views off for a shorter document.** Views are on by default and add a table per view; switch **Include views** off when you only want the field-level reference.
- **Sort by `invalidComputedFieldCount`** in the Output tab to find broken formulas across every base in one go.

### FAQ

#### Does this Actor read my Airtable records?

No. It uses Airtable's metadata API, which returns table names, field names, field types, options and descriptions — never a cell value. The token you give it needs only `schema.bases:read`, and that scope cannot read records even if the Actor asked it to.

#### Can I document an Airtable base without an Airtable token?

Yes. Leave the token field empty and the Actor documents a bundled sample base — 4 tables, 40 fields, 4 relationships, including a self-referential link, a formula, a rollup, a lookup and a count. It costs nothing and every row it writes is marked `source: "sample"`.

#### Which Airtable plan do I need?

Any of them. `schema.bases:read` is available on Free, Team, Business and Enterprise Scale. You do not need an Enterprise plan and you do not need to be a base owner — read-only access to a base is enough to create a token for it.

#### Does it document automations, interfaces, scripts or record data?

No, and it cannot. Airtable's metadata API exposes tables, fields and views only. Automations, interfaces, extensions and scripts are not available to any API client, so no Actor can document them.

#### Who can see the page the Actor produces?

Anyone you give the link to. The `markdownUrl`, `htmlUrl` and `diagramUrl` in each row are signed URLs to your run's key-value store — they are not indexed or listed anywhere, but they need no login, which is exactly what makes them easy to send to a client. Treat them as you would a shared document link.

#### Does the documentation include anything I typed into Airtable?

Yes — and it is worth knowing before you share it. Table names, field names, descriptions, formula expressions and AI field prompts are all reproduced in the output, because they are the documentation. If someone has typed a credential or a client's private note into a field description, it appears in the page too. Read the document before you pass the link on.

#### Why does my ER diagram show fewer tables than my base has?

Because of **Maximum tables in the diagram**, which defaults to 40. Above that the Actor draws the most connected tables and says how many it left out at the foot of the page. Raise the limit if you want the whole thing, or set it to `0` to skip the diagram and keep only the written data dictionary.

#### Why did a run document some of my bases but not all of them?

The budget ran out. A base is documented whole or not at all, so rather than part-writing one the Actor skips it and names it in the run's status message. Raise **Maximum total charge** on the run, or list fewer bases, and run it again.

#### Does it use an AI model?

No. Every field in the output is read or counted directly from one Airtable API response per base. There is no model, no estimate and no sampling, so the same base always produces the same document.

### Support

Found something wrong, or documented a base that came out looking odd? Open the **Issues** tab on this Actor and include the run ID and the smallest input that reproduces it — that is usually enough to fix it quickly. Custom work on top of this Actor, such as a different document layout or pushing the output straight into a wiki, is available on request through the same tab.

# Actor input Schema

## `airtableToken` (type: `string`):

Your Airtable personal access token. It needs one scope, <code>schema.bases:read</code>, which is read-only, is available on every Airtable plan, and does not let the Actor read any of your records. Under <b>Access</b> on the token, add the bases you want documented. Create one at <a href='https://airtable.com/create/tokens' target='_blank'>airtable.com/create/tokens</a>.<br><br><b>Leave this empty to try the Actor on a bundled sample base</b> — it documents a four-table example instead of your own data, free of charge, and every row it writes is marked <code>source="sample"</code>.

## `baseIds` (type: `array`):

The bases to document, one per line. Find a base ID in its URL — <code>https://airtable.com/appXXXXXXXXXXXXXX/...</code> — or paste the whole URL and the Actor will take the ID out of it.<br><br>Leave empty to document <b>every base your token can see</b>. Each base is charged for, so on a token with many bases either list the ones you want or set <b>Maximum total charge</b> on the run.

## `format` (type: `string`):

Which files to write for each base. The Mermaid diagram source is written as a separate <code>.mmd</code> file whichever you pick — unless you switch the diagram off with <b>Maximum tables in the diagram</b> — because GitHub, GitLab, Notion and Obsidian all render it directly.

## `includeViews` (type: `boolean`):

List each table's views, and which fields each view shows.

## `maxTablesInDiagram` (type: `integer`):

Stop drawing the ER diagram after this many tables, keeping the most connected ones. A base with hundreds of tables produces a diagram nobody can read, so this keeps it legible; the data dictionary and the dataset always cover every table. Set to 0 to skip the diagram.

## Actor input object example

```json
{
  "baseIds": [],
  "format": "both",
  "includeViews": true,
  "maxTablesInDiagram": 40
}
```

# Actor output Schema

## `tables` (type: `string`):

No description

## `files` (type: `string`):

Every file this run wrote. Each base gets a Markdown data dictionary, a shareable HTML page and the Mermaid ER diagram source, named after its base ID.

## `runSummary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("mediocre_interest/airtable-base-documentation").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("mediocre_interest/airtable-base-documentation").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call mediocre_interest/airtable-base-documentation --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mediocre_interest/airtable-base-documentation"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/5Eq8ll8j5igQdwo6i/builds/32GbdZAc9qG4PH7wp/openapi.json
