Introduction

The FieldSwap API is a single-shot identity-data API. You send one piece of identity data (email, phone, LinkedIn URL, IP, or full name), and ask for another field back (phone, employment, social, address, etc.). On a cache miss FieldSwap cascades through configured third-party data vendors and persists the result, so the same lookup is free next time within the field's TTL.

Base URL
https://api.fieldswap.founderscale.com
Content type
application/json

All product endpoints live under /v1/. Breaking changes ship under a new major version; backwards-compatible additions (new optional fields, new outputtypes) ship within /v1/.

Need to share these docs with someone outside your workspace? Download the Markdown version — no login required.

Authentication

Every /v1/* request requires an API key. Generate one in the API keys tab. Keys are shown once on creation — store them in a secret manager, not in source.

You can authenticate two ways. They are equivalent; pick whichever your stack prefers:

Authorization: Bearer fs_live_<your_key>
# or
X-Api-Key: fs_live_<your_key>

Keys are scoped to a single workspace (tenant). Treat them like passwords: rotate periodically, revoke any key that may have leaked, and use a separate key per environment or service so you can revoke without coordinated downtime.

Never expose API keys in client-side code. Browser JS, mobile apps, and public repositories are not safe for fs_live_ keys — proxy through your own backend instead.

Lookup

POST/v1/lookup

Resolve a single output field for one identity input. Each call costs 1 credit regardless of whether the result was served from cache or freshly fetched from a vendor. If the cascade returns no data the call still costs 1 credit (this matches the underlying vendor billing model — you pay per query, not per match).

Request body

{
  "input":  { "type": "<field-type>", "value": "<string>" },
  "output": "<field-type>"
}
FieldTypeDescription
input.typeFieldTypeKind of identity supplied. See Field types.
input.valuestring (1–500)Raw value. Emails are lowercased, phones parsed to E.164, LinkedIn normalized to canonical URL.
outputFieldTypeField you want returned. Can equal input.type (useful for canonicalization).

Example

curl -X POST https://api.fieldswap.founderscale.com/v1/lookup \
  -H "Authorization: Bearer fs_live_<your_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "input":  { "type": "email", "value": "ada@example.com" },
    "output": "phone"
  }'

Response

{
  "request_id": "8f1d2c20-…",
  "person_id":  "0a4e5b66-…",
  "input":  { "type": "email", "value": "ada@example.com" },
  "output": {
    "type": "phone",
    "value": { "phone_e164": "+14155551234" },
    "fetched_at": "2026-05-08T17:40:21.118Z",
    "source_vendor": "exampledata",
    "fresh": true
  },
  "hit_source": "vendor",
  "credits_charged": 1,
  "credits_remaining": 9847,
  "status": "ok"
}
FieldDescription
request_idUUID. Echoed in x-request-id response header. Quote when filing support.
person_idStable identifier for the resolved identity in your workspace. null if no person was matched or created.
outputThe resolved field, or null if status is not_found / opt_out.
output.valueField-specific shape. See Field types.
output.freshtrue if served from cache within TTL or freshly fetched.
hit_sourcecache, vendor, or miss. Use to gauge cache effectiveness.
statusok, not_found, or opt_out. 200 OK is returned for all three — check this field.
Status semantics. not_found means we know who the person is but no vendor returned the requested field. opt_out means the person has invoked their right to deletion or opted out of enrichment — your code should treat both identically (no value, no retry needed for some hours).

Field types

Both input.type and output use the same enum. Some fields are only meaningful as inputs (ip) or as outputs (employment, social) — supplying them in the wrong direction returns 400.

TypeDirectionValue shape
emailin / out{ "email": "ada@example.com" }
phonein / out{ "phone_e164": "+14155551234" }
linkedinin / out{ "url": "https://linkedin.com/in/ada" }
first_nameout{ "value": "Ada" }
last_nameout{ "value": "Lovelace" }
full_namein / out{ "value": "Ada Lovelace" }
birthdateout{ "value": "1815-12-10" }
addressout{ "line1": "...", "city": "...", "region": "CA", "postal_code": "94110", "country": "US" }
companyout{ "name": "Analytical Engines Ltd", "domain": "ae.example" }
titleout{ "value": "Mathematician" }
employmentout{ "company": "...", "title": "...", "start_date": "2024-01-01" }
socialout{ "twitter": "@ada", "github": "ada", "linkedin": "..." }
ipin only{ "ip": "203.0.113.42" } → resolves to company / employment

Normalization. Inputs are always normalized before lookup, so Ada@Example.com and ada@example.com hit the same cache row. Phones are parsed to E.164; if parsing fails we return 400 with {"detail":"could not parse phone"}.

Errors

Errors are returned with an HTTP status code and a JSON body of the shape { "detail": "<message>" }. The x-request-id header is set on every response — quote it when contacting support.

StatusCodeWhen
400bad_requestInput could not be parsed (e.g. invalid email or phone number).
401unauthorizedMissing, malformed, or revoked API key.
402payment_requiredInsufficient credits, or workspace daily provider cap reached.
403forbiddenAuthenticated, but not allowed (e.g. dashboard-only endpoint hit with an API key).
404not_foundResource does not exist or is not visible to your tenant.
429rate_limitedPer-key request rate limit exceeded. Retry-After header included.
5xxserver_errorTransient failure. Safe to retry with exponential backoff.

Retry guidance. Lookups are not idempotent (each call costs a credit), but retries on 429 and 5xx are safe — a re-issued lookup within the cache TTL hits the cache and skips vendor work. Always honor Retry-After on 429.

Rate limits

Limits are enforced per API key. The default is 100 requests / minute per key; contact us to raise it. Every response includes:

X-RateLimit-Limit:     100
X-RateLimit-Remaining: 87
X-RateLimit-Reset:     1715192461   # unix seconds
Retry-After:           23           # only on 429

On top of per-key throttling there is a per-workspace daily provider spend cap — once your configured cap is hit, lookups return 402 payment_required until the next UTC day. Configure the cap in Billing.

Logs & usage

Every request is logged with input/output type, hit source, latency, status, and the person it resolved to. View them in the Logs tab, or pull them programmatically:

GET /logs?limit=100&cursor=2026-05-08T00:00:00Z
GET /billing/usage      # current period credits + request count
GET /billing/ledger     # credit ledger entries

These dashboard endpoints require a JWT session, not an API key — they exist for the FieldSwap UI. If you want programmatic access to usage data, request a reporting key (separate scope) via support.

Privacy endpoints

Two unauthenticated endpoints exist so end users (data subjects) can invoke their privacy rights without going through your support team. You do not call these from your backend; you link to them from your privacy policy.

Submit a DSAR

POST/v1/dsar/submit
{
  "requester_email": "ada@example.com",
  "type":            "access" | "delete",
  "identity_type":   "email",
  "identity_value":  "ada@example.com",
  "note":            "optional free text"
}

Opt out

POST/v1/opt-out
{
  "identity_type":  "email",
  "identity_value": "ada@example.com",
  "reason":         "optional"
}

Opt-out is permanent for the identity until reversed by support. Subsequent lookups for an opted-out person return status: opt_out with no output and zero credits charged.

No-code (Zapier & Make)

You don't need to write code to call FieldSwap. Revenue-ops teams can wire /v1/lookup into a Zapier Zap or a Make scenario using each tool's generic HTTP step — no official app required. A typical flow: a new lead lands in your CRM, FieldSwap resolves their email into a phone number, and the result is written back to the record.

Step-by-step guides for each tool:

These cover the enrichment (action) direction only. A FieldSwap-as-a-trigger integration isn't available yet.

SDKs & samples

The API is small enough that an SDK isn't required — any HTTP client works. Official client libraries for Node and Python are on the roadmap.

For interactive testing without writing code, use the Playground. It runs against your live workspace and deducts real credits.

Found a bug or have a question? Email support@fieldswap.io with the failing request_id from x-request-id.