# method_lookup API

Two APIs share one base URL and one API-key pool. Each service has its
own LLM Markdown document below.

# Credit Score API

Returns an applicant's FICO score from a soft credit inquiry.

- Transport: JSON over HTTPS
- Typical duration: 95-140 seconds
- One application per person. Re-running the same identity shortly after a
  completed application is rejected.
- Service prefix: `/api/apply`

## LLM documentation endpoints

- `GET /api/apply/docs.md` — this document (`text/markdown`)
- `GET /api/apply/llms.txt` — alias of this document
- `GET /docs.md` and `GET /llms.txt` — combined documentation for both APIs
- `GET /swagger` — interactive OpenAPI UI
- `GET /openapi.json` — OpenAPI schema

TruePeopleSearch skip-tracing documentation is at `/search/docs.md` and
`/search/llms.txt`.

The Swagger UI groups this route under **Credit Score**. It uses the shared
`ApiKeyAuth` scheme (`X-API-Key`) in the **Authorize** dialog; the endpoint
also accepts `Authorization: Bearer <key>` for compatibility.

## Authentication

Every `POST /api/apply` request must send the API key, either as:

- `x-api-key: <key>`
- `Authorization: Bearer <key>`

Missing or wrong key:

```json
401 { "success": false, "error": "Unauthorized" }
```

If the service itself has no key configured, it refuses to run:

```json
503 { "success": false, "error": "API authentication is not configured" }
```

Public routes (no key needed): `GET /`, `GET /docs`, `GET /docs.md`,
`GET /llms.txt`, `GET /health`, `GET /swagger`, `GET /openapi.json`,
`GET /api/apply/docs.md` and `GET /api/apply/llms.txt`.

## POST /api/apply

Runs one application and returns the score.

### Request body

Required:

- `firstName` (string)
- `lastName` (string)
- `dob` (string, `MM/DD/YYYY`)
- `street` (string)
- `city` (string)
- `state` (string, 2-letter code or full name)
- `zip` (string; a numeric JSON value is also accepted)

Optional:

- `ssn` (string, 9 digits, with or without dashes) — submitted only if the
  application asks for it
- `email` (string) — generated from the name if omitted
- `phone` (string, 10 digits) — area code matched to the state if omitted
- `flow` (string, `fast` or `ui`) — overrides the server's `FLOW_MODE`

### Response

A score was retrieved (`score` is an integer, 300-850; approval is a separate
matter):

```json
200 { "success": true, "score": 764 }
```

The credit file is frozen, so nothing could be evaluated:

```json
200 { "success": true, "isFrozen": true }
```

The application failed without a score:

```json
502 { "success": false, "error": "Application declined" }
```

The time limit was exceeded:

```json
504 { "success": false, "error": "Run exceeded 170000ms at phase \"esign\"" }
```

Invalid input:

```json
400 { "success": false, "error": "Missing required fields: firstName, lastName, street, city, state, zip." }
```

Unexpected server failure:

```json
500 { "success": false, "error": "Internal Server Error" }
```

### Status codes

| Status | Meaning |
| --- | --- |
| `200` | Score retrieved, or the credit file is frozen |
| `400` | Invalid JSON, missing fields, invalid DOB, state or SSN |
| `401` | Missing or wrong API key |
| `502` | Application failed without a score |
| `503` | No API key is configured on the server |
| `504` | Run exceeded `RUN_DEADLINE_MS` |

The response never contains the applicant's name, address, date of birth, SSN,
email or phone. Error text is sanitized (`[email]`, `[phone]`, `[ssn]`,
`[date]`) and capped at 200 characters.

### Example

```bash
curl -s -o /tmp/apply.json -w 'HTTP %{http_code} in %{time_total}s\n' \
  -X POST "$BASE_URL/api/apply" \
  -H "x-api-key: $API_KEY" \
  -H 'Content-Type: application/json' \
  --max-time 190 \
  -d '{
    "firstName": "<first name>",
    "lastName": "<last name>",
    "dob": "<MM/DD/YYYY>",
    "street": "<street>",
    "city": "<city>",
    "state": "<2-letter state>",
    "zip": "<ZIP>",
    "ssn": "<9 digits>"
  }'

jq . /tmp/apply.json
```

Trailing slash is accepted directly: `POST /api/apply/`.

## GET /health

```json
200 { "status": "ok" }
```

## Notes

- `success: true` means a score was retrieved. A declined application with a
  score is still `success: true`.
- Declines without a score return `502`. Frozen files return `isFrozen: true`.
- A fresh identity is required for each run.
- The flow runs through a NodeMaven Scraping Browser session and closes it when
  the run ends, including on failure or timeout.
- `RECORD_RUN=true` writes per-run artifacts under `logs/`.


# TruePeopleSearch Skip-Tracing API

Public-records search endpoints under `/search/*`, backed by TruePeopleSearch
through the NodeMaven Scraping Browser.

- Transport: JSON over HTTPS
- Authentication: `X-API-Key`
- Default response format: `competitor`
- Upstream: TruePeopleSearch
- Parsed responses are cached for `CACHE_TTL_SECONDS` (default 900 seconds).
  Identical concurrent requests are collapsed into one upstream fetch.

## LLM documentation endpoints

- `GET /search/docs.md` — this document (`text/markdown`)
- `GET /search/llms.txt` — alias of this document
- `GET /docs.md` and `GET /llms.txt` — combined documentation for both APIs
- `GET /swagger` — interactive OpenAPI UI
- `GET /openapi.json` — OpenAPI schema

Credit-score documentation is at `/api/apply/docs.md` and
`/api/apply/llms.txt`.

The Swagger UI groups these routes under **TruePeopleSearch**. They use the
shared `ApiKeyAuth` scheme (`X-API-Key`) in the **Authorize** dialog.

## Authentication

Every `/search/*` data endpoint requires the API key in the `X-API-Key` header:

```bash
curl -H "X-API-Key: $API_KEY" \
  "$BASE_URL/search/byname?name=John%20Smith&page=1"
```

`Authorization: Bearer <key>` is accepted by `POST /api/apply`, not by the
`/search/*` endpoints.

Missing or wrong key:

```json
401 { "detail": "Invalid or missing API key" }
```

The response includes `WWW-Authenticate: ApiKey`.

If the service itself has no key configured:

```json
503 { "detail": "API authentication is not configured" }
```

## Common query parameters

Every data endpoint accepts:

- `format=competitor` (default) — competitor-compatible envelope
- `format=native` — native TruePeopleSearch envelope

Endpoints with pagination accept `page` (integer, minimum `1`, default `1`).
Invalid `format` or `page` values are rejected by request validation with
HTTP `422`.

Trailing slashes are accepted directly, for example `/search/byname/` and
`/search/detailsbyID/`.

## Endpoints

| Endpoint | Required parameters | Pagination |
| --- | --- | --- |
| `GET /search/byname` | `name` | `page` |
| `GET /search/byaddress` | `street`, `citystatezip` | `page` |
| `GET /search/bynameaddress` | `name`, `citystatezip` | `page` |
| `GET /search/byphone` | `phoneno` | `page` |
| `GET /search/byemail` | `email` | no |
| `GET /search/detailsbyID` | `peo_id` | no |

### Search by name

```bash
curl -H "X-API-Key: $API_KEY" \
  "$BASE_URL/search/byname?name=John%20Smith&page=1"
```

### Search by address

```bash
curl -H "X-API-Key: $API_KEY" \
  "$BASE_URL/search/byaddress?street=1%20Main%20St&citystatezip=Austin%2C%20TX&page=1"
```

### Search by name and city/state/ZIP

```bash
curl -H "X-API-Key: $API_KEY" \
  "$BASE_URL/search/bynameaddress?name=John%20Smith&citystatezip=Austin%2C%20TX&page=1"
```

### Search by phone

```bash
curl -H "X-API-Key: $API_KEY" \
  "$BASE_URL/search/byphone?phoneno=5125550100&page=1"
```

### Search by email

```bash
curl -H "X-API-Key: $API_KEY" \
  "$BASE_URL/search/byemail?email=jane%40example.com"
```

### Person details by ID

```bash
curl -H "X-API-Key: $API_KEY" \
  "$BASE_URL/search/detailsbyID?peo_id=p123"
```

## Missing parameters

Missing required query parameters return **HTTP 200** with the legacy error
envelope, not `400`:

```json
{"error": "name parameter Not found"}
```

| Endpoint | Competitor `error` | Native `Message` |
| --- | --- | --- |
| `/search/byname` | `name parameter Not found` | `name parameter Not found` |
| `/search/byaddress` | `street and citystatezip parameter Not found` | `street, citystatezip parameter Not found` |
| `/search/bynameaddress` | `name and address parameter Not found` | `name, citystatezip parameter Not found` |
| `/search/byphone` | `phone parameter Not found` | `phoneno parameter Not found` |
| `/search/byemail` | `email parameter Not found` | `email parameter Not found` |
| `/search/detailsbyID` | `peo_id parameter Not found` | `peo_id parameter Not found` |

The native missing-parameter envelope is:

```json
{
  "Status": 200,
  "Source": "_cch_dch",
  "Message": "name parameter Not found",
  "Records": 0,
  "Page": 1,
  "PropertyDetails": {},
  "PeopleDetails": []
}
```

## Competitor response format (default)

### Search

```json
{
  "Status": 200,
  "Message": "state_1; Use the /person_details_by_ID endpoint to gather more info!",
  "Source": "ncch_dch",
  "Records": 1,
  "Page": 1,
  "PropertyDetails": {},
  "PeopleDetails": [
    {
      "Name": "Jane Q Example",
      "Link": "https://www.truepeoplesearch.com/find/person/p123",
      "Person ID": "p123",
      "Age": "42",
      "Lives in": "Austin, TX",
      "Used to live in": "1 Old St, Austin, TX 78701, 2 Older St, Austin, TX 78702",
      "Related to": "Alex Example, Sam Example",
      "peo_id": "p123",
      "Used_Names": ["J Q Example"],
      "Previous_Addresses": [
        "1 Old St, Austin, TX 78701",
        "2 Older St, Austin, TX 78702"
      ],
      "Related": ["Alex Example", "Sam Example"]
    }
  ]
}
```

When no records are found:

```json
{
  "Status": 200,
  "Message": "state_1; We could not find any records for that search criteria",
  "Source": "ncch_dch",
  "Records": 0,
  "Page": 1,
  "PropertyDetails": {},
  "PeopleDetails": []
}
```

`Records` is the total record count reported by TruePeopleSearch when
available, otherwise the number of returned records. `Page` echoes the
requested page.

### Details

```json
{
  "Status": 200,
  "Message": "state_1",
  "Source": "ncch_dch",
  "Person Details": [
    {
      "Person_name": "Jane Q Example",
      "Age": "42",
      "Born": "January 1980",
      "Lives in": "Austin, TX",
      "Telephone": "(512) 555-0100"
    }
  ],
  "Current Address Details List": [
    {
      "street_address": "1 Old St",
      "address_locality": "Austin",
      "address_region": "TX",
      "postal_code": "78701",
      "county": "Travis County",
      "date_range": "(Jan 2020 - Jan 2026)",
      "property_summary": "$100,000 | 3 Bed"
    }
  ],
  "All Phone Details": [
    {
      "phone_number": "(512) 555-0100",
      "phone_type": "Wireless",
      "phone_info": "Possible Primary Phone",
      "last_reported": "Last reported Jan 2026",
      "provider": "Example Wireless",
      "status": "Active"
    }
  ],
  "Email Addresses": ["jane@example.com"],
  "Previous Address Details": [],
  "All Relatives": [
    {
      "Name": "Alex Example",
      "Age": "30",
      "Person Link": "https://www.truepeoplesearch.com/find/person/p456",
      "Person ID": "p456",
      "Relationship": "Sibling"
    }
  ],
  "All Associates": [],
  "Records": 1,
  "Page": 1,
  "PropertyDetails": {},
  "PeopleDetails": ["<native detail record>"],
  "peo_id": "p123",
  "AKAs": ["J Q Example"],
  "Phone_Numbers": ["(512) 555-0100"],
  "All_Addresses": ["1 Old St, Austin, TX 78701"],
  "Property_Details": {
    "Bedrooms": "3",
    "Bathrooms": "2",
    "Sq_Ft": "1500",
    "Year_Built": "1985",
    "Estimated_Value": "$100,000",
    "Estimated_Equity": "$50,000",
    "Last_Sale_Amount": "$80,000",
    "Last_Sale_Date": "Jan 2020",
    "Occupancy": "Owner",
    "Ownership": "Individual",
    "Type": "Single Family",
    "Class": "Residential",
    "Subdivision": "Example",
    "Lot_Sq_Ft": "5000",
    "APN": "000-000-000",
    "School_District": "Example ISD",
    "Address": "1 Old St, Austin, TX 78701"
  },
  "Possible_Relatives": ["Alex Example"],
  "Possible_Associates": []
}
```

If the details adapter receives no records, it returns:

```json
{
  "Status": 200,
  "Source": "_cch_dch",
  "Message": "peo_id Not Valid",
  "Records": 0,
  "PeopleDetails": []
}
```

### Competitor key mapping

| Competitor key | Source field |
| --- | --- |
| `Person ID` | `peo_id` |
| `Lives in` | `Lives_In` |
| `Used to live in` | `Previous_Addresses` joined with `, ` |
| `Related to` | `Related` joined with `, ` |
| `Person Details` | `Name`, `Age`, `Born`, `Lives_In`, first phone |
| `Current Address Details List` | `Current_Address_Details` |
| `All Phone Details` | `Phone_Details` |
| `Email Addresses` | `Emails` |
| `Previous Address Details` | `Previous_Address_Details` |
| `All Relatives` | `Relatives_Details` |
| `All Associates` | `Associates_Details` |

Additive extras retained for existing clients: `peo_id`, `Used_Names`,
`Previous_Addresses`, `Related`, `Records`, `Page`, `PropertyDetails`,
`PeopleDetails`, `AKAs`, `Phone_Numbers`, `All_Addresses`, `Property_Details`,
`Possible_Relatives`, `Possible_Associates`, `property_summary`, `status` and
`Relationship`.

## Native response format (`format=native`)

### Search

```json
{
  "Status": 200,
  "Source": "ncch_dch",
  "Message": "Successful Search Result",
  "Records": 1,
  "Page": 1,
  "PropertyDetails": {},
  "PeopleDetails": [
    {
      "peo_id": "p123",
      "Name": "Jane Q Example",
      "Age": "42",
      "Lives_In": "Austin, TX",
      "Previous_Addresses": [],
      "Related": []
    }
  ]
}
```

### Details

The native details response uses the same envelope with one record:

```json
{
  "Status": 200,
  "Source": "ncch_dch",
  "Message": "Successful Search Result",
  "Records": 1,
  "Page": 1,
  "PropertyDetails": {},
  "PeopleDetails": ["<native detail record>"]
}
```

## Errors

| Status | Body | Meaning |
| --- | --- | --- |
| `200` | Legacy missing-parameter envelope | A required query parameter is empty or absent |
| `401` | `{"detail": "Invalid or missing API key"}` | Missing or wrong key |
| `422` | FastAPI validation details | Invalid `format` or `page` |
| `500` | `{"detail": "Unable to retrieve data from the source. Please try again later."}` | Upstream or NodeMaven failure |
| `503` | `{"detail": "API authentication is not configured"}` | No server API key configured |

Upstream details are logged server-side only. The client-facing 500 body never
contains proxy URLs, NodeMaven tokens, attempt counts or upstream HTML.

## Notes

- The `Source` key is preserved for compatibility; its value is an internal
  cache indicator, not a copy of the competitor value.
- The upstream is scraped through a warm, auto-scaling NodeMaven session pool.
  Pool size, session TTL, retries and cache TTL are configured through
  environment variables.
- Response sets are not guarantees of identity; treat public-records matches
  as candidates and verify them.
