ContactSearch API · v1

← back to app

Overview

The ContactSearch v1 API exposes read-only access to your tenant's contacts, companies, and jobs. It is designed for automation platforms (n8n / Make / Zapier / custom scripts) that want to PULL data on their own schedule rather than receive WEBHOOK pushes from us.

All endpoints are tenant-scoped — every API key resolves to exactly one tenant, and cross-tenant data is never returned even if a filter would technically match another tenant's row.

Authentication

Every endpoint requires an API key. Keys are minted in Settings → Integrations → API Keys and look like:

cs_live_a1b2c3d4e5f6...

Pass the key one of three ways:

# Preferred — Authorization header
curl -H "Authorization: Bearer cs_live_..." \
     https://contactsearch.ai/api/v1/contacts

# Or, header without scheme
curl -H "Authorization: cs_live_..." \
     https://contactsearch.ai/api/v1/contacts

# Or, query string (less secure — gets logged in access logs)
curl "https://contactsearch.ai/api/v1/contacts?api_key=cs_live_..."
Treat your API key like a password. Anyone with the key has read access to your entire contact + company + jobs database. Revoke compromised keys immediately under Settings.

Rate Limits

Each API key gets 60 requests per minute by default (configurable per key, max 600/min). Exceeding the limit returns:

HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json

{"error":"Rate limit of 60 requests/min exceeded.","code":"rate_limit_exceeded","retry_after_sec":60}

Error Format

Every error response is JSON with a stable code field your retry logic can branch on:

{"error":"API key not recognized.","code":"unknown_api_key"}

Codes you'll see:

Endpoints

Contacts

GET/api/v1/contacts
Paginated list. Filters: company, title, city, state. Pagination: page, per_page (max 100).
curl -H "Authorization: Bearer cs_live_..." \
  "https://contactsearch.ai/api/v1/contacts?company=Ace&state=IL&per_page=25"
GET/api/v1/contacts?id=<contact_id>
Single contact with all JSON columns decoded (work_history, education, skills, etc).

Companies

GET/api/v1/companies
Paginated list. Filter: q (name LIKE). Pagination: page, per_page.
GET/api/v1/companies?id=<company_id>
Single company row.

Jobs

GET/api/v1/jobs
Paginated list. Filters: company, since (YYYY-MM-DD). Ordered by posted_at DESC.

Resume PDF

GET/api/v1/resume?id=<contact_id>
Returns a generated PDF resume for the contact as application/pdf.
curl -H "Authorization: Bearer cs_live_..." \
  -o resume.pdf \
  "https://contactsearch.ai/api/v1/resume?id=12345"
URL note: the original spec listed /api/v1/contacts/<id>/resume.pdf. Our router serves the same content at /api/v1/resume?id=<id> for now. Future router upgrades may add the path-parameter form; the query-string form will keep working.

Response Shapes

List response

{
  "contacts": [ { ...row... }, ... ],
  "page": 1,
  "per_page": 50,
  "total": 1247,
  "has_more": true
}

Detail response

{
  "contact": { ...full row with JSON columns decoded... }
}

Best practices