Integration Guide
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.
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.
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:
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.
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.
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.
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.
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.
Create a new base in Airtable and add a table called Leads with these fields:
| Field Name | Airtable Field Type |
|---|---|
| Name | Single line text |
| Phone | Phone number |
| Title | Single line text |
| Company | Single line text |
| URL | |
| Source | Single select (Apollo, Maps, Lead Database, etc.) |
| Status | Single 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.
Airtable uses Personal Access Tokens (PATs) for API authentication. Legacy API keys are no longer supported. To create a PAT:
data.records:read and data.records:write.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"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.
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 requestsThen 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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
All ScraperCity plans include full API access to all 25 scrapers. There is no per-endpoint unlock and no per-seat charge.
| Plan | Monthly | Includes |
|---|---|---|
| Starter | $49/mo | API access, all scrapers except Lead Database |
| Pro | $149/mo | API access, all scrapers except Lead Database, higher credit volume |
| Scale | $649/mo | All 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.