ScraperCity logo

Integration Guide

ScraperCity + Airtable

Build a searchable, filterable B2B lead database inside Airtable. Scrape contacts from Apollo, Google Maps, or the Lead Database and push them into any Airtable base automatically - with name, email, phone, title, company, and LinkedIn URL for every record.

How It Works

ScraperCity returns structured B2B contact data from any of its scrapers. Airtable accepts new records via its REST API. Connect the two and every lead ScraperCity pulls automatically becomes a row in your Airtable base - complete with email, phone, title, company, and LinkedIn URL.

The simplest path is Zapier or n8n: trigger a ScraperCity API call, loop through results, and create an Airtable record for each contact. For bulk loads, a Python or Node script works better since the Airtable REST API accepts batches of 10 records per request, letting you write up to 50 records per second within the rate limit.

ScraperCity exposes 25 different data sources under one API key - the same workflow you build for Apollo leads works for Google Maps businesses, Yelp listings, Shopify stores, or any other scraper. You pick a source, define your filters, and the output lands in Airtable in the same consistent field structure every time.

What You Can Build

Before writing a single line of code, it helps to know exactly what kind of Airtable base you are building. The most common setups are:

ICP Contact Database

A single Airtable base of decision-makers filtered by title, industry, and geography. Sales reps open the base each morning and work through verified leads. Each record has name, email, direct phone, company, LinkedIn URL, and a Status field for tracking outreach.

Local Business Directory

A filterable map of every business in a target city or vertical - built from Google Maps scrapes. Useful for agencies prospecting local clients, franchisors scouting competition, or market researchers benchmarking a geography. Fields include business name, phone, email, website, rating, and review count.

E-commerce Store Tracker

A live inventory of Shopify and WooCommerce stores in your niche, built from ScraperCity store data. Track monthly revenue estimates, contact info, tech stack, and which stores have not been contacted yet. Useful for app developers, wholesalers, and agencies selling to e-commerce operators.

Enrichment Pipeline

Start with a raw list of company domains in Airtable. Run the Email Finder ($0.05/contact) and Mobile Finder ($0.25/input) against each row to append decision-maker contact details automatically. Write enriched results back to the same record using the Airtable upsert endpoint.

Airtable REST API Tutorial: Setup

This section walks through the full Airtable API integration from scratch - creating the base, generating credentials, writing leads with curl, and then graduating to a full Python script using pyairtable.

1

Create your Airtable base and table

Create a new base in Airtable and add a table called Leads with these fields:

Field NameAirtable Field Type
NameSingle line text
EmailEmail
PhonePhone number
TitleSingle line text
CompanySingle line text
LinkedInURL
SourceSingle select (Apollo, Maps, Lead Database, etc.)
StatusSingle select (New, Contacted, Replied, Closed)

The Source field helps you track which ScraperCity scraper produced each lead. The Status field gives your sales team a simple workflow to manage outreach directly in Airtable.

2

Get your Airtable Personal Access Token

Airtable uses Personal Access Tokens (PATs) for API authentication. Legacy API keys are no longer supported. To create a PAT:

  1. Go to airtable.com/create/tokens (the Airtable Developer Hub).
  2. Click + Create new token and give it a name like "ScraperCity integration".
  3. Under Scopes, add data.records:read and data.records:write.
  4. Under Access, select the specific base you just created.
  5. Click Create token and copy it immediately - Airtable will only show it once.

Your Base ID is the string starting with app found in the URL when you open the base: airtable.com/appXXXXXXXX/...

Store both values as environment variables - never hard-code credentials in scripts.

export AIRTABLE_TOKEN="patXXXXXXXXXXXXXX"
export AIRTABLE_BASE_ID="appXXXXXXXXXXXXXX"
3

Pull leads from ScraperCity and push to Airtable with curl

Here is the complete two-step flow using curl - query ScraperCity, then batch-create Airtable records:

# Step 1 - Pull leads from ScraperCity (any endpoint works the same way)
curl -X GET "https://app.scrapercity.com/api/v1/database/leads?title=CTO&country=United%20States&hasEmail=true&limit=10" \
  -H "Authorization: Bearer YOUR_SCRAPERCITY_KEY"

# Step 2 - Push a batch of up to 10 records to Airtable
curl -X POST "https://api.airtable.com/v0/YOUR_BASE_ID/Leads" \
  -H "Authorization: Bearer YOUR_AIRTABLE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "records": [
      {
        "fields": {
          "Name": "Jane Smith",
          "Email": "[email protected]",
          "Phone": "+15551234567",
          "Title": "CTO",
          "Company": "Acme Corp",
          "LinkedIn": "https://linkedin.com/in/janesmith",
          "Source": "Lead Database",
          "Status": "New"
        }
      }
    ]
  }'

The Airtable REST API returns a 200 with the created record IDs on success and a 422 if a required field is missing or formatted incorrectly.

4

Airtable API Python script - full bulk loader

For loading hundreds or thousands of leads, use pyairtable - the recommended Python client for the Airtable API. It handles batch chunking, rate-limit retries, and pagination automatically.

pip install pyairtable requests

Then create a script that pulls from ScraperCity and writes to Airtable in batches:

import os
import math
import time
import requests
from pyairtable import Api

# --- Credentials (set as environment variables) ---
SCRAPERCITY_KEY = os.environ["SCRAPERCITY_KEY"]
AIRTABLE_TOKEN  = os.environ["AIRTABLE_TOKEN"]
AIRTABLE_BASE   = os.environ["AIRTABLE_BASE_ID"]
AIRTABLE_TABLE  = "Leads"

# --- 1. Pull leads from ScraperCity Lead Database ---
def fetch_scrapercity_leads(title: str, country: str, limit: int = 100) -> list[dict]:
    url = "https://app.scrapercity.com/api/v1/database/leads"
    headers = {"Authorization": f"Bearer {SCRAPERCITY_KEY}"}
    params  = {"title": title, "country": country, "hasEmail": "true", "limit": limit}
    resp = requests.get(url, headers=headers, params=params)
    resp.raise_for_status()
    return resp.json().get("leads", [])

# --- 2. Map ScraperCity fields to Airtable fields ---
def map_lead(lead: dict) -> dict:
    return {
        "Name":     lead.get("name", ""),
        "Email":    lead.get("email", ""),
        "Phone":    lead.get("phone", ""),
        "Title":    lead.get("title", ""),
        "Company":  lead.get("company", ""),
        "LinkedIn": lead.get("linkedinUrl", ""),
        "Source":   "Lead Database",
        "Status":   "New",
    }

# --- 3. Batch-create records in Airtable (max 10 per call) ---
def push_to_airtable(leads: list[dict]) -> None:
    api   = Api(AIRTABLE_TOKEN)
    table = api.table(AIRTABLE_BASE, AIRTABLE_TABLE)
    records = [map_lead(l) for l in leads]

    chunk_size = 10
    total_chunks = math.ceil(len(records) / chunk_size)

    for i in range(0, len(records), chunk_size):
        chunk = records[i : i + chunk_size]
        table.batch_create(chunk)
        print(f"Pushed chunk {i // chunk_size + 1}/{total_chunks} ({len(chunk)} records)")
        # pyairtable retries 429s automatically, but a small sleep keeps
        # you well under the 5 req/sec per-base rate limit
        time.sleep(0.25)

# --- Run ---
if __name__ == "__main__":
    leads = fetch_scrapercity_leads(title="VP of Sales", country="United States", limit=500)
    print(f"Fetched {len(leads)} leads from ScraperCity")
    push_to_airtable(leads)
    print("Done.")

The batch_create() method accepts a list of field dictionaries and handles the 10-record-per-request limit internally. pyairtable automatically retries requests when it receives a 429 rate-limit response from Airtable.

5

Upsert to prevent duplicate records

If you run the same scrape multiple times, you will create duplicate rows unless you use Airtable's upsert feature. The Airtable REST API supports a performUpsert option on the PATCH endpoint that updates the matching record if one is found, or creates a new one if not. Use Email as the merge key since it is unique per contact.

# Upsert via raw REST (PATCH instead of POST)
curl -X PATCH "https://api.airtable.com/v0/YOUR_BASE_ID/Leads" \
  -H "Authorization: Bearer YOUR_AIRTABLE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "performUpsert": {
      "fieldsToMergeOn": ["Email"]
    },
    "records": [
      {
        "fields": {
          "Name": "Jane Smith",
          "Email": "[email protected]",
          "Title": "CTO",
          "Company": "Acme Corp",
          "Source": "Lead Database"
        }
      }
    ]
  }'

With pyairtable, use table.batch_upsert(records, key_fields=["Email"]) instead of batch_create(). The response includes separate createdRecords and updatedRecords arrays so you can log exactly what changed.

Common Automation Patterns

Daily lead pipeline

Schedule an n8n or Zapier workflow to pull fresh leads every morning and add them to your Airtable base. Filter by title, industry, or location to match your ICP. New rows appear automatically - no manual exports or CSV uploads.

Account-based prospecting

Start with a list of target company domains in Airtable. Use ScraperCity Email Finder ($0.05/contact) to find decision-maker emails for each company and write them back to the same row using the upsert endpoint with domain as the merge key.

Local business directory

Scrape Google Maps for businesses in your target market ($0.01/place) and build a filterable Airtable directory with business name, phone, email, website, rating, and review count. Share the base with your team as a territory management tool.

Sales team handoff

Scrape leads, validate emails ($0.0036/email), then push only verified contacts to Airtable with Status = New. Share the base with your sales team as their daily calling list. Reps update Status as they work through the queue.

Competitor store tracking

Scrape Shopify and WooCommerce stores in your niche ($0.0039/lead) into an Airtable base. Track tech stack, monthly revenue estimates, and contact details. Filter by estimated revenue to prioritize outreach to the highest-value stores.

Real estate lead farming

Pull agent listings from Zillow ($0.01/listing) or commercial listings from Crexi ($0.029/listing) into Airtable. Build a base your team can sort by location, listing price, and days on market for targeted outreach.

No-Code Setup: Zapier and n8n

If you prefer no-code automation, both Zapier and n8n have native Airtable integrations. The pattern is identical in both tools: use an HTTP Request node to call the ScraperCity API, then loop through the results and use the Airtable "Create Record" action for each one.

Zapier setup: Create a Zap with a Schedule trigger (daily or weekly). Add an HTTP step that calls the ScraperCity endpoint with your API key. Add a "Looping by Zapier" step to iterate over the results array. Inside the loop, add an Airtable "Create Record" action and map the ScraperCity fields to your Airtable columns.

n8n setup: Create a workflow with a Schedule trigger. Add an HTTP Request node pointing to the ScraperCity API. Add a SplitInBatches node set to 10 items per batch. Then add an Airtable node in "Create" mode and map the fields. n8n handles pagination natively - add a loop back to the HTTP node with an offset parameter to pull multiple pages.

For large volumes (10,000+ leads), the Python script approach in Step 4 above is faster and cheaper than running through Zapier task limits. Use no-code for recurring small batches and scripted imports for one-time bulk loads.

Performance Tips and Rate Limits

The Airtable REST API enforces a rate limit of 5 requests per second per base. Each create or update request accepts a maximum of 10 records, so the theoretical write ceiling is 50 records per second. In practice, stay under that ceiling by sleeping 200-250 ms between batches.

Always batch to 10 records per request

A single POST to the Airtable API can create up to 10 records at once. Never send one record per request - that is 10x slower and will hit the rate limit immediately on any load over a few hundred records.

Use pyairtable for automatic 429 handling

pyairtable automatically retries requests up to five times when the Airtable API returns a 429 rate-limit error. You do not need to write your own retry logic. If you are using raw HTTP requests, implement exponential backoff starting at 1 second and doubling up to 30 seconds.

Use upsert instead of check-then-create

If you need to avoid duplicates, use the performUpsert option on the PATCH endpoint rather than fetching all existing records and comparing. Fetching uses up your rate-limit budget and is slower. The upsert handles the deduplication server-side in a single call.

Paginate ScraperCity results before writing

The ScraperCity Lead Database returns up to 100 leads per page. Fetch all pages first, build the full list in memory, then write to Airtable in batches. This keeps your Airtable write loop clean and predictable rather than interleaving API calls between two services.

Empty fields are omitted from Airtable responses

The Airtable API does not return fields with empty or null values. When reading records back, always use .get() with a fallback rather than direct key access - otherwise a record with no phone number will raise a KeyError.

Troubleshooting Common Errors

401 Unauthorized

Cause: Your Personal Access Token is missing, expired, or was not scoped to the base you are writing to.

Fix: Make sure the Authorization header is "Bearer YOUR_TOKEN" (not "Token" or "API-Key"). Check that data.records:write is in the token scopes and that the target base is listed under Access.

403 Forbidden

Cause: The token has the right scopes but the authenticated user does not have write permission on the base.

Fix: Open the base in Airtable and check your collaborator role. You need at least Editor access to create records via the API. Creator access is required to modify the schema.

404 Not Found

Cause: The base ID or table name in your URL is wrong.

Fix: Double-check the base ID (starts with "app") from the URL bar. Table names are case-sensitive and must match exactly. You can also reference tables by their table ID (starts with "tbl") which is stable even if you rename the table.

422 Unprocessable Entity

Cause: A field value does not match the Airtable field type, or a required field is missing.

Fix: Check that Email fields contain a valid email address, URL fields contain a valid URL (including https://), and Phone fields contain a valid phone string. If a field is empty, omit it from the payload entirely rather than passing null or an empty string.

429 Too Many Requests

Cause: You exceeded 5 requests per second for the base.

Fix: Add a sleep of at least 200 ms between batch requests. If using pyairtable, automatic retries are enabled by default. If using raw HTTP, implement exponential backoff: wait 1s, then 2s, then 4s before retrying.

ScraperCity returns 0 leads

Cause: Your query filters are too narrow or the scraper job is still processing.

Fix: Apollo scrapes take 11-48+ hours. Check the job status via GET /api/v1/status/{runId} before downloading results. For the Lead Database, try broadening your filters - remove hasEmail=true first to see if unfiltered results return data.

Pricing

All ScraperCity plans include full API access to all 25 scrapers. There is no per-endpoint unlock and no per-seat charge.

PlanMonthlyIncludes
Starter$49/moAPI access, all scrapers except Lead Database
Pro$149/moAPI access, all scrapers except Lead Database, higher credit volume
Scale$649/moAll scrapers including the Lead Database (3M+ B2B contacts, instant query)

Usage is pay-per-result on top of the plan. Apollo leads cost $0.0039 each. Google Maps businesses cost $0.01 each. Email validation costs $0.0036 per address. A credit card is required to activate your plan.

FAQ

Get API access to ScraperCity: