# Migrating from another provider

> Move a legacy criminal-records integration onto one normalized envelope: the parameter map, the additive recordType note, and the labelled fields you gain.

- **HTML:** https://offendersearch.app/docs/criminal/migration
- **Base URL:** https://api.offendersearch.app
- **Authentication:** `X-API-Key` request header
- **OpenAPI:** https://offendersearch.app/openapi.json · https://offendersearch.app/openapi.yaml
- **Criminal reference as markdown:** https://offendersearch.app/docs/criminal.md

## One normalized envelope

The Criminal Search API returns one normalized record schema across every jurisdiction, so you stop stitching differing per-state formats together yourself. Moving a legacy criminal-records integration means changing the base URL and key, then adopting the labelled fields the envelope adds.

```plain
- const BASE = "https://api.previous-provider.example";
+ const BASE = "https://api.offendersearch.app";
- headers: { "Authorization": "Bearer " + LEGACY_KEY }
+ headers: { "X-API-Key": process.env.OFFENDERSEARCH_KEY }
```

## Parameter map

| Concern | Criminal Search API |
| --- | --- |
| Search predicate | `query.lastName` **required**; `firstName`, `state`, `county`, `dob`/`birthYear`/`age` narrow it. |
| Pagination | `page` / `perPage` at the **top level** (not inside `query`); `perPage` clamped to 200. |
| Name widening | `nameStrategy: "prefix"` (default) or `"exact"`. **No fuzzy surname matching, ever.** |
| Live re-check | a `live` block — `{ "jurisdictions": [...] }` or `{ "scope": "matched" }` — billed per completed source to a $2.00 ceiling. |
| Location filter | `query.state`/`query.county` (jurisdiction) + `query.city`/`query.zipcode`/`query.address` (residence, where published). No lat/lng radius. |
| Errors | always `{ "error": { "code", "message" } }` — branch on `code`. |

## What you gain

- **Labelled match strength.** `matchState`, `matchConfidence` and `matchBasis` on every record, so you set your own auto-accept threshold rather than inherit one.
- **A completeness signal.** On a live search, `counts.sourcesIncomplete` separates *no match* from *not determined*.
- **Neutral jurisdiction codes.** Every place is a `<ST>-<COUNTY>` or `<ST>-<RECORD-KIND>` code — a state and a place, and nothing else.
- **Stored history and CSV batch.** Prior searches are recorded, and up to 1000 cached lookups run in one call.

## The additive-recordType note

Criminal records are a distinct product on the same account and key as the Sex Offender API, returned as their own `recordType` (`criminal_record`). New data lands as a new `recordType`; existing keys and their meanings do not change under you. A caller who knows one API knows the other.

> This data is not a consumer report and may not be used for any FCRA-covered purpose — the notice is present in the `legal` block of every response.

---

## Related

- Previous: [Verification reports](https://offendersearch.app/docs/criminal/verification-reports.md)
- Index: [Criminal Search API reference](https://offendersearch.app/docs/criminal.md)
