GoliathDevelopersGet an API key
Integrations / Skip Trace API

Skip Trace API

Run skip traces programmatically from your own systems using the Goliath developer API

About

The Skip Trace API lets you run skip traces from your own code — any server, script, or third-party tool that can make HTTPS requests. Send an address (and optionally a person or entity name), receive the people we can match to that property — names, phones, and emails — in the same response. Each person is labelled with how they are connected to the property, so you can tell a deed owner from a relative before you dial. Each call consumes one skip-trace credit from your organization’s balance, the same way an in-app skip trace does.

Generating an API key

  1. Open the Integrations page

    From the left sidebar, click Integrations under the Automations section.

  2. Find the Skip Trace API card

    It lives under the Developer section. The card is only visible to organization admins — if you do not see it, ask an admin to generate a key for you.

  3. Click Generate API key

    Goliath shows your new key once, in a dialog. Copy it now and store it in your secrets manager — the full key is never shown again. After you close the dialog, only the first and last characters are visible in the UI.

  4. Use the key

    Pass it as a Bearer token on every request:

    Authorization: Bearer gd_...

Making a request

Endpoint

POST https://server.goliathdata.com/api/v1/skiptrace

Headers

Header Value
Authorization Bearer gd_...
Content-Type application/json

Example

curl -X POST https://server.goliathdata.com/api/v1/skiptrace \
  -H "Authorization: Bearer gd_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "address": {
      "line1": "123 Main St",
      "city": "Austin",
      "state": "TX",
      "zip": "78701"
    }
  }'
const res = await fetch("https://server.goliathdata.com/api/v1/skiptrace", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.GOLIATH_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    address: {
      line1: "123 Main St",
      city: "Austin",
      state: "TX",
      zip: "78701",
    },
  }),
})
const data = await res.json()

Request body

addressobjectrequired

The property address to look up.

address properties
line1stringrequired

Street address.

line2string

Apartment, unit, suite.

citystringrequired

City name.

statestringrequired

Two-letter USPS state abbreviation (e.g. TX, CA).

zipstringrequired

ZIP code. Five or nine digits.

actorobject

Narrow the trace to a specific person or entity. If omitted, the API traces against whichever owner records are attached to the property.

actor properties
typestringrequired

Either person or entity.

firstNamestring

First name. Required when type is person; person only.

lastNamestring

Last name. Required when type is person; person only.

namestring

Entity or company name. Required when type is entity; entity only.

Response

{
  "id": "3f21d6e0-a8a4-4a0e-8f25-9e9a74c37c2e",
  "status": "records_found",
  "persons": [
    {
      "firstName": "Jane",
      "lastName": "Doe",
      "middleName": null,
      "age": 47,
      "relationship": "owner",
      "deceased": false,
      "phones": [
        { "number": "5125551234", "type": "mobile" },
        { "number": "5125559999", "type": "residential" }
      ],
      "emails": ["jane@example.com"]
    },
    {
      "firstName": "Robert",
      "lastName": "Doe",
      "middleName": null,
      "age": 51,
      "relationship": "relative",
      "deceased": false,
      "phones": [{ "number": "5125557777", "type": "mobile" }],
      "emails": []
    }
  ]
}
idstring

Skip-trace request ID.

statusstring

Either records_found or no_records_found.

personsarray

The people we can match to the property. Not all of them are owners — see relationship below.

Sorted by how likely each person is to be the one you are trying to reach: owners first, then parties to a filing on the property, then people the data provider ties to the address, then relatives, then anyone flagged deceased, then procedural participants. If you only dial the first one or two entries, this ordering is what you are relying on — but reading relationship is more reliable than reading position.

person properties
firstNamestring | null

First name, or null if unknown.

lastNamestring | null

Last name, or null if unknown.

middleNamestring | null

Middle name, or null if unknown.

agenumber | null

Approximate age, or null if unknown.

relationshipstring

How this person is connected to the property. One of:

  • owner — on the deed. The name the county has for this property.
  • related_party — carries a role in a filing on the property (personal representative, executor, heir, beneficiary, petitioner). Not an owner, but has standing in the matter.
  • associate — tied to the address by the data provider, but not on the deed and carrying no filing role. Often a current occupant or an unrecorded owner; sometimes a same-name match elsewhere.
  • relative — reported as a relative of one of the people above. Their connection is to a person, not to the property, so they are the least likely to answer as the owner.
  • procedural_participant — named on a filing only for attesting to its execution (witness, notary, deputy clerk). No stake in the property.

Attorneys and counsel are never returned.

deceasedboolean

true when the data provider flags this person as deceased. Deceased people are still returned — a probate workflow needs the decedent’s record — but they are sorted behind every living person. Independent of relationship: a deceased person can still be the owner on the deed.

phonesarray

Array of { number, type } objects. type is mobile, residential, or unknown.

emailsstring[]

Array of email addresses.

Errors

All errors return a JSON body of the shape:

{ "error": { "code": "insufficient_credits", "message": "..." } }
HTTP Code Meaning
401 invalid_key Missing, malformed, or revoked Bearer key.
402 insufficient_credits Your organization has no skip-trace credits. Buy more in the Goliath app.
403 feature_unavailable Your organization’s plan or trial does not currently permit skip tracing.
422 invalid_request Request body failed validation, or the address could not be resolved.
429 rate_limited Too many requests per minute. Back off for Retry-After seconds.
429 concurrency_limit Too many in-flight requests from this key at once.
504 timeout Skip trace did not complete within 30 seconds. Retry the request.
500 internal_error Unexpected server error. Retry; contact support if it persists.

Rate limits

  • 1,000 requests per minute per API key.
  • 10 concurrent requests per API key per server instance.

Every response includes:

Header Meaning
X-RateLimit-Limit Requests allowed per window (1000).
X-RateLimit-Remaining Requests remaining in the current window.
X-RateLimit-Reset Unix timestamp when the window resets.
Retry-After Seconds to wait. Only set when you receive a 429.

Credits and billing

Each successful skip-trace call consumes one SKIPTRACE credit from your organization’s balance. This is the same credit pool as in-app skip traces — running either path debits the same bucket.

When your balance hits zero, subsequent calls return 402 insufficient_credits. Buy more credits in the Goliath app under Billing → Credits and then retry the request.

Frequently asked questions

How long does a request take?

Most calls complete in a few seconds. The server will wait up to 30 seconds for the pipeline to terminate before returning 504 timeout. Retrying a timed-out request is safe and will return the cached result if one has since been produced.

Is the result cached?

Yes. Repeat calls for the same address + actor combination return the cached result from the previous successful trace, without triggering another provider call. Credits are still consumed for each call.

Can I use this from the browser?

No. API keys are org-level secrets. Anyone with the key can run traces and burn your credits. Keep it on your server and never expose it to client code.

What happens when I rotate the key?

The old secret stops working immediately. Any systems still using it start receiving 401 invalid_key on their next request. Update them with the new secret before rotating if you can coordinate the switch.

Do you support property IDs as input?

No. The public API only accepts street addresses. Property IDs are internal identifiers that change over time and aren’t safe to expose on a stable public surface.