If your agency runs its client list in Airtable, you already have the perfect home for SEO scores — you're just missing the column that fills them in. This post adds one: an Airtable scripting extension that walks a table of client URLs, audits each one through the SEO Score API, and writes the score, grade, and top fix back into the record. Your base stops being a static list and becomes a live SEO tracker your whole team can read.

It's the self-auditing spreadsheet idea rebuilt for the tool agencies actually run their operations in — with Airtable's views, filters, and grouping on top.

Step 1: set up the table

Create (or reuse) a table — call it Clients — with these fields:

Field Type
Website URL or Single line text
Score Number
Grade Single line text
Top Issue Long text
Last Audited Date

You almost certainly have Website already. The other four are what the script fills.

Step 2: add the scripting extension

Open Extensions → Add an extension → Scripting, then paste this. It reads every record, audits the ones with a URL, and writes the results back:

const API_KEY = "your_seoscoreapi_key"; // from seoscoreapi.com/#signup
const table = base.getTable("Clients");

const query = await table.selectRecordsAsync({
  fields: ["Website", "Score"],
});

for (const record of query.records) {
  const url = record.getCellValueAsString("Website");
  if (!url) continue;

  try {
    const resp = await fetch(
      "https://seoscoreapi.com/audit?url=" + encodeURIComponent(url),
      { headers: { "X-API-Key": API_KEY } }
    );
    const data = await resp.json();

    if (data.score === undefined) {
      output.text(`⚠️ ${url}: ${data.detail || data.error || "audit failed"}`);
      continue;
    }

    const topIssue = (data.priorities && data.priorities[0])
      ? data.priorities[0].issue
      : "Clean — no priority issues";

    await table.updateRecordAsync(record, {
      "Score": data.score,
      "Grade": data.grade,
      "Top Issue": topIssue,
      "Last Audited": new Date().toISOString().slice(0, 10),
    });

    output.text(`✅ ${url}: ${data.score} (${data.grade})`);

    // Pace the loop to respect the rate limit — see below
    await new Promise((r) => setTimeout(r, 2000));
  } catch (e) {
    output.text(`⚠️ ${url}: ${e.message}`);
  }
}

Click Run. The script logs each result as it goes and updates the records in place. Add a client, run it again, and the new row fills in.

Step 3: make the base actually useful

The scores are worth more once Airtable's views organize them:

  • Group by Grade to see your A-clients and your F-clients at a glance.
  • Sort by Score ascending so the work queue — your lowest scorers — floats to the top.
  • Color records red below 70, so a client whose site regressed jumps out.
  • Filter Last Audited is before today to find records that need a refresh.

This is where Airtable beats a plain spreadsheet: the same audit data, sliced into a triage view for the team and a status view for the client, from one script run. It's a lightweight version of the white-label reporting workflow, living inside the CRM you already use.

How do I avoid hitting the rate limit?

That setTimeout(2000) line matters. Every plan has a requests-per-minute cap — 2/min on Free, 10/min on Starter, 30/min on Basic — and without pacing, a 40-client base fires 40 requests in a blink and most come back as 429 Too Many Requests. Two seconds between calls keeps you at ~30/minute, which is safe on Basic. On Starter, bump it to setTimeout(6000); on Free, bulk auditing isn't really viable — that tier is for trying the script on one or two records. Match the delay to your plan and the run just works.

Can I run the audit automatically instead of clicking Run?

Yes — Airtable Automations can call the API without the scripting extension. Trigger on "when a record is created" or "at a scheduled time," add a Run script action with a trimmed version of the loop (audit the single triggering record), and Airtable does it hands-free. A weekly scheduled automation means fresh scores are waiting every Monday. Just remember that automated runs still draw from your quota, so keep the monitoring math in mind when you set the frequency.

Which plan does an agency base need?

Volume, as always, decides. Fifty clients re-audited weekly is ~200 audits/month — right at Starter ($5/mo), or Basic ($15/mo) if you also audit landing pages and competitors. A large roster audited daily climbs into Pro territory. The base doesn't change the cost; the audit cadence does. The ROI breakdown maps client counts to plans if you want the exact line.

Why keep SEO scores in Airtable at all?

Because that's where the client relationship already lives — the contacts, the contracts, the notes, the renewal dates. Bolting the SEO score onto the same record means an account manager sees a client's health without leaving the CRM, and a red score sits right next to the next check-in date. You don't build a separate SEO dashboard nobody opens; you make the tool the team lives in a little smarter. That co-location is the entire benefit: the metric shows up where the decision gets made.

Where to take it next

Two natural extensions. A history table: instead of overwriting Score, append each run to a linked "Audits" table and you've got a trend line per client with zero new infrastructure — a score-history tracker inside Airtable. Client-facing views: share a filtered, read-only view of a single client's record as a live status page they can bookmark. Both build directly on the script you already pasted.

The CRM your agency already runs on, plus one script, turns your client list into an SEO tracker that keeps itself current.