> ## Documentation Index
> Fetch the complete documentation index at: https://wiki.darknetsearch.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Check credentials at sign-up, login and password change

> Check a password, an email address, or an email and password pair against known leaks from your sign-up, login and password-change flows, and tell the user to change a leaked password.

Credential Check tells your application whether the credential a user just typed appears in a known leak. You call it from your own sign-up, login and password-change flows. Your backend sends what the user typed, and the answer comes back in the same response, so you can refuse a leaked password or ask for a change on the next screen.

Each check is one synchronous `POST` with a JSON body. There is no job to start and nothing to poll. Each call answers one question:

* Has this password appeared in a known leak, under any login?
* Has this exact email and password pair leaked together?
* Has this pair leaked for one website, such as yours?
* Has this email address appeared in a known leak?

Your users' addresses can be on any domain, public mail providers included. The checks are not limited to domains your organization owns.

**Availability:** every plan with a license in force.

**Cost:** no metered allowance and no per-check daily cap. Each check made with an API key uses one unit of your monthly API Calls allowance, refunded when the response is not `2xx`, and counts toward your organization's per-second [rate limit](https://wiki.darknetsearch.com/api/guides/rate-limits.md). Every call appears in your [API call usage](https://client-api.leak.center/scalar-docs/#tag/general-endpoints/GET/api/service/get_api_call_usage/).

### Pick a check for each moment

| Moment in your flow | Check | When the credential is found, tell the user |
| - | - | - |
| A user picks a password at sign-up | [Password](#check-a-new-password) | "This password has appeared in a data leak. Choose a different one." Refuse it, or warn before you save it. |
| A user picks a new password at change or reset | [Password](#check-a-new-password) | The same message. Refuse the password before you store its hash. |
| A user logs in with the correct password | [Email and password pair](#check-the-pair-after-a-successful-login) | "Your password for this account appeared in a data leak. Change it now." Finish the sign-in and prompt the change. |
| The same login, after the pair check finds a match | [Pair on your site](#check-a-credential-for-your-own-site) | The same message. The credential leaked for your site, so require the change before the session continues. |
| A signed-in user views account or security settings | [Email](#check-an-email-address) | "This email address appears in known data leaks. Use a password you use nowhere else." A notice, not a block. |

The checks nest, so each narrower check only adds information when the broader one found something:

* A pair match is always a password match. The pair check flags only this user's own leaked pair, not every password someone else leaked.
* A site match is always a pair match. Run the site check only after the pair check returns `found: true`.
* An email match says the address leaked. It does not say the user's current password did. Use it for a notice, and let the pair check decide whether to prompt a change.

The pair, site and email checks need an email address. If your users sign in with a username, or with an AD-style login such as `alice@acme.local`, use the password check.

### Check a new password

Call [`credential_check_password_compromised`](https://client-api.leak.center/scalar-docs/#tag/credential-check/POST/api/service/credential_check_password_compromised/) when a user picks a password, before you hash and store it. Send the password as `password`, exactly as the user typed it. Do not hash or trim it.

At sign-up the address is not verified yet, so check the password alone. A pair or email result shown to someone who has not proved they own the address tells them whether that address leaked. At a password change the user is signed in. At a reset, the reset link has proven they control the address. In both cases the pair check with their email and the new password is safe too, if you only want to refuse their own leaked pairs.

```bash theme={"dark"}
curl https://client-api.leak.center/api/service/credential_check_password_compromised/ \
  -X POST \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"password": "Summer2026!"}'
```

```json theme={"dark"}
{
  "found": true,
  "leakCount": 7
}
```

* `found`: `true` when at least one leak record holds this password. This is the value your flow acts on.
* `leakCount`: the number of leak records that hold this password, under any login. It counts records, not distinct leaks, and stops at 10,000. A value of `10000` means 10,000 or more.

The rules for the value:

* **1 to 128 characters.** An empty string or a longer value is refused with `400`. A password longer than 128 characters cannot be checked. Skip the check for it.
* **Whitespace is kept.** `" hunter2 "` and `"hunter2"` are two different passwords. A single space is a valid password.
* **Matching is case-sensitive.** The value is matched as sent, with no wildcards.

### Check the pair after a successful login

Login is the one moment your backend holds an existing user's password in plain text. Your database holds only hashes. That makes login the moment to run the pair check for accounts that already exist.

**1. Verify the password as usual**

Run your own password verification first. On a failed attempt, call nothing.

**2. Run the pair check**

Send the `email` and `password` the user typed. On `found: false`, finish the sign-in.

**3. Act on a match**

Finish the sign-in and prompt a password change. To require the change when the leak names your site, run the [site check](#check-a-credential-for-your-own-site) next.

**4. Let the user through if a check cannot answer**

On an error or a timeout, finish the sign-in without the prompt. See [Let the user through when a check cannot answer](#let-the-user-through-when-a-check-cannot-answer).

Call [`credential_check_credentials_compromised`](https://client-api.leak.center/scalar-docs/#tag/credential-check/POST/api/service/credential_check_credentials_compromised/) with both fields. A request missing either one is refused with `400`.

```bash theme={"dark"}
curl https://client-api.leak.center/api/service/credential_check_credentials_compromised/ \
  -X POST \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email": "alice@acme.com", "password": "Summer2026!"}'
```

```json theme={"dark"}
{
  "found": true,
  "leakCount": 3
}
```

A record matches only when it holds both values: the address, without regard to case, and the password, case-sensitively. `leakCount` stops at 10,000, like the password check. `found: true` means this user's current password for this address sits in a leak, next to the address.

> [!WARNING]
>
> **Run the pair check only after the password verifies.** A check on a failed attempt tells whoever is typing whether their guess is a leaked pair. It also still answers, so a credential-stuffing run against your form would use up your organization's rate limit and push your real checks into `429`.

### Check a credential for your own site

The password, pair and email checks take no website. The pair check matches the email and password on any site. To ask whether the pair leaked for your site, call [`credential_check_leak_search`](https://client-api.leak.center/scalar-docs/#tag/credential-check/POST/api/service/credential_check_leak_search/) with `email`, `password` and `website` in the body.

```bash theme={"dark"}
curl https://client-api.leak.center/api/service/credential_check_leak_search/ \
  -X POST \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email": "alice@acme.com", "password": "Summer2026!", "website": "acme.com"}'
```

The response is the leak search envelope: `totalElements`, `page`, `size` and `results`, in the shape shown under [What comes back](#what-comes-back). Read `totalElements`. A value greater than `0` means the credential leaked for that site. All three filters must match the same record. A record with no site recorded never matches, so a pair can be found by the pair check and still return `0` here. `totalElements` stops at 10,000.

* **Send each host your users sign in on,** such as `acme.com` or `login.acme.com`. `website` takes one value of up to 255 characters. It is matched, without regard to case and with no wildcards, against the site recorded on each record. Run one search per host.
* **Confirm the value matches before you rely on it.** Outside the sign-in path, search once with `website` alone and read the `website` values that come back. A value in a form your records do not use answers `0`, with no error.
* **A `0` does not clear the user.** The pair check already matched, so keep the prompt to change the password.
* **Leave out `password`** to ask whether any credential for this address leaked for your site. Every field you send narrows the match.
* **Send no query string.** The default page of 10 records is the smallest one. A `size` below `10` is refused with `400`.
* **Keep `results` on your server.** It carries up to 10 matching records, with the leaked passwords in plain text. Your flow needs only `totalElements`. Never log or store the response.

### Check an email address

Call [`credential_check_email_compromised`](https://client-api.leak.center/scalar-docs/#tag/credential-check/POST/api/service/credential_check_email_compromised/) for a signed-in user's verified address, for example on an account or security settings page. Send the address as `email`.

```bash theme={"dark"}
curl https://client-api.leak.center/api/service/credential_check_email_compromised/ \
  -X POST \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email": "alice@acme.com"}'
```

```json theme={"dark"}
{
  "found": true,
  "leakCount": 42,
  "firstSeen": "2019-03-15 10:20:30",
  "lastSeen": "2025-01-10 08:15:00"
}
```

* `found`: `true` when at least one leak record holds this address.
* `leakCount`: the number of matching leak records. It stops at 10,000.
* `firstSeen` / `lastSeen`: when the oldest and the newest matching records were recorded, as `YYYY-MM-DD HH:MM:SS`. These are recording dates, not breach dates. Both are `null` when `found` is `false`.

```json theme={"dark"}
{
  "found": false,
  "leakCount": 0,
  "firstSeen": null,
  "lastSeen": null
}
```

The address is matched without regard to case, so `Alice@Acme.com` and `alice@acme.com` get the same answer. Surrounding whitespace is trimmed. The field must be a valid email address. A bare domain or a partial address is refused with `400`. An address on a reserved domain such as `.local`, `.test` or `localhost` is refused with `422`. An address with an IP-address domain is refused with `400` or `422`. AD-style logins such as `alice@acme.local` fall into this case. Skip the check for those addresses.

A found address does not mean your user's password leaked. Read it next to the pair check: if the email is found but the pair is not, the address leaked, but never with this password.

### Act on the answer

* **Act on `found`.** Any `true` is enough to refuse a new password or ask for a change. `leakCount` tells you how widely a value circulates. It is context, not a threshold.
* **Show results only to the account owner.** Run the pair and site checks after a successful login. Show an email result only to the signed-in owner of a verified address. Otherwise your form tells strangers which addresses and passwords are leaked.
* **Store the outcome, not the input.** Keep a flag such as "password change required" on the account. Never store the password you checked or the records that came back.

> [!NOTE]
>
> **`found: false` is a complete answer.** If the leak index cannot answer from all of its data, the call fails with `502`. You never get a smaller count, or `found: false`, in its place. An error means unknown, never clean.

### Wire the checks into your backend

Call Credential Check from your server, inside the request that handles the form. Your API key authorizes calls as your organization. Anyone who reads it can make the same calls, so it never goes to a browser or a mobile app. Your form posts to your backend as it does today, and your backend calls the API.

Keep the API key server-side. Read it from your secret store and send it in the `X-API-Key` header on every check. [Authentication](https://wiki.darknetsearch.com/api/guides/authentication.md) covers API keys. A `401` means the key is invalid, expired or revoked. Alert your team, and do not retry with the same key.

**1. Send the credential in the JSON body**

The checks read `password` from the request body, never from the query string. Send JSON and build it with a JSON encoder, not string concatenation. Quotes, backslashes and non-ASCII characters in a password then reach the API intact.

**2. Set your own timeout**

The API waits up to 30 seconds by default for the lookup service before it answers `504`. A slow lookup usually ends as a `502` before that. Either way, it is longer than a sign-up or login form should hang. Give the call the wait your form can afford, and treat a timeout as no answer.

**3. Fail open**

Decide up front what happens when a check gives no answer. At sign-up, login and password change, let the user continue. An outage on the check then never blocks a sign-up or a sign-in.

**4. Log the status, never the body**

Log the status code of a failed check so you can alert on it. Keep the request body and any leak search response out of your HTTP client logs, error trackers and traces. Both hold credentials.

**5. Send the browser only the outcome**

The browser needs to know whether to show the warning. It does not need the count or the records.

> [!WARNING]
>
> **Never put a password in a URL.** A password in the query string is never checked, and URLs are logged far more widely than request bodies.

The helpers below wire the login flow: the pair check, then the site check on a match. Pass in the API key your backend reads from its secret store.

```python Python theme={"dark"}
import logging

import requests

API = "https://client-api.leak.center/api/service"
TIMEOUT_SECONDS = 3  # example value. Set the wait your form can afford.

logger = logging.getLogger(__name__)


def credential_check(api_key, endpoint, body):
    """The JSON answer, or None when the check cannot answer."""
    try:
        response = requests.post(
            f"{API}/{endpoint}/",
            json=body,
            headers={"X-API-Key": api_key},
            timeout=TIMEOUT_SECONDS,
        )
        if response.status_code != 200:
            logger.warning("credential check failed: %s %s", endpoint, response.status_code)
            return None
        return response.json()
    except (requests.RequestException, ValueError):
        logger.warning("credential check did not answer: %s", endpoint)
        return None


def leak_status_at_sign_in(api_key, email, password, site="acme.com"):
    """'site', 'pair', 'none', or None when the pair check cannot answer."""
    pair = credential_check(
        api_key,
        "credential_check_credentials_compromised",
        {"email": email, "password": password},
    )
    if pair is None:
        return None
    if not pair.get("found"):
        return "none"
    on_site = credential_check(
        api_key,
        "credential_check_leak_search",
        {"email": email, "password": password, "website": site},
    )
    if on_site is not None and on_site.get("totalElements", 0) > 0:
        return "site"
    return "pair"
```

```javascript Node.js theme={"dark"}
const API = "https://client-api.leak.center/api/service";
const TIMEOUT_MS = 3000; // example value. Set the wait your form can afford.

// The JSON answer, or null when the check cannot answer.
async function credentialCheck(apiKey, endpoint, body) {
  try {
    const response = await fetch(`${API}/${endpoint}/`, {
      method: "POST",
      headers: {
        "X-API-Key": apiKey,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(body),
      signal: AbortSignal.timeout(TIMEOUT_MS),
    });
    if (response.status !== 200) {
      console.warn(`credential check failed: ${endpoint} ${response.status}`);
      return null;
    }
    return await response.json();
  } catch {
    console.warn(`credential check did not answer: ${endpoint}`);
    return null;
  }
}

// "site", "pair", "none", or null when the pair check cannot answer.
async function leakStatusAtSignIn(apiKey, email, password, site = "acme.com") {
  const pair = await credentialCheck(
    apiKey,
    "credential_check_credentials_compromised",
    { email, password },
  );
  if (pair === null) return null;
  if (!pair.found) return "none";
  const onSite = await credentialCheck(
    apiKey,
    "credential_check_leak_search",
    { email, password, website: site },
  );
  if (onSite !== null && onSite.totalElements > 0) return "site";
  return "pair";
}
```

In your login handler, after the password verifies: on `"site"`, require a change before the session continues. On `"pair"`, finish the sign-in and prompt a change. On `"none"`, finish the sign-in. On no answer, finish the sign-in without the prompt. The answer is unknown, not clean.

The helpers log the endpoint and the status only. They never log the body, which holds the password.

#### Let the user through when a check cannot answer

A leak check adds a warning to your flow. It should never be the reason a user cannot sign up, sign in or change a password. When a check fails, finish the flow without the warning and log the failure for your team.

| Response | What it means | What your flow does |
| - | - | - |
| `200` | The check answered from all of its data. | Act on `found`, or on `totalElements` for the site check. |
| `400` or `422` | The value cannot be checked as sent: an invalid address, a reserved or IP-address domain, or a password over 128 characters. | Let the user continue. Skip the check for this input. A `400` on every call means your request is malformed. |
| `401` | Your API key is missing, invalid, expired or revoked. If the body says `Invalid or missing service token.`, the API could not authenticate to the lookup service, and your API key is fine. | Let the user continue. Alert your team. Do not retry with the same key. For `Invalid or missing service token.`, treat it like a `502`. |
| `403` | Your organization has no license in force. | Let the user continue. Alert your team. |
| `429` | You sent more requests per second than your organization's rate limit allows. | Let the user continue. Skip checks until `Retry-After` seconds have passed. |
| `502` | The lookup service or the leak index failed. A slow lookup usually ends here too. | Let the user continue. Do not retry while the user waits. Check again at the next login. |
| `504` | The API's wait for the lookup service ran out, 30 seconds by default. | Your own timeout should fire first. Treat it like a `502`. |
| Your own timeout, or any other error status | No answer. | Treat it like a `502`. |

#### Plan for your volume

Every check is one API call. A sign-in that finds a pair match and then runs the site check makes two. Every call that passes the license check counts toward your organization's per-second rate limit, failed calls included. That limit is shared with every other call your organization makes. Each of those calls returns `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` headers. They show your current window, usually one second. Read them to space out bursts. [Rate limits](https://wiki.darknetsearch.com/api/guides/rate-limits.md) covers the limits.

> [!TIP]
>
> You do not need to check on every sign-in. Record when you last checked an account, and check again on a schedule that fits your traffic. Always check a new password at sign-up, password change and reset.

### Read the leak records behind an answer

Support and investigation work sometimes needs the records, not just the yes/no. `credential_check_leak_search` returns the records a check counted. Read and page the records outside the sign-in path. Send the same values you checked, and `totalElements` matches the `leakCount` you got, unless new records were indexed in between.

> [!WARNING]
>
> **The records hold plaintext passwords.** Show them only to the people who investigate, and keep them out of logs.

**1. Run a check**

Call the email, password, or pair check. Move on when `found` is `true`.

**2. Search with the same values**

Send the same `email`, `password`, or both in the body. Add `website` to narrow to one site, and `dateFrom` or `dateTo` to narrow by date.

**3. Page through the records**

Raise `page` until you have read `totalElements` records.

The filters go in the JSON body. Paging goes in the query string.

```bash theme={"dark"}
curl "https://client-api.leak.center/api/service/credential_check_leak_search/?page=0&size=50&sort=createdAt,desc" \
  -X POST \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email": "alice@acme.com", "dateFrom": "2024-01-01"}'
```

#### Body filters

| Field | Notes |
| - | - |
| `email` | A valid email address, matched as in the email check. |
| `password` | 1 to 128 characters, matched as in the password check. |
| `website` | Up to 255 characters, such as `acme.com`. Matched without regard to case against the site recorded on each record. |
| `dateFrom` | `YYYY-MM-DD`. Earliest date a record was recorded. |
| `dateTo` | `YYYY-MM-DD`. Latest date a record was recorded. |

You need at least one of `email`, `password` or `website`. Dates alone are refused with `400`. Every filter you send narrows the result. `dateFrom` and `dateTo` each work on their own. Send dates as `YYYY-MM-DD`. A value longer than 10 characters is refused with `400`. An impossible date such as `2024-02-30` is refused with `422`.

#### Query parameters

| Parameter | Default | Notes |
| - | - | - |
| `page` | `0` | Zero-based page number. |
| `size` | `10` | Records per page, from 10 to 100. |
| `sort` | `createdAt,desc` | `field,direction`. Direction is `asc` or `desc`. |

Records are ordered by `createdAt`, newest first by default. `sort=createdAt,asc` returns the oldest first. The API also accepts `leakPublishDate` and `leakDiscoverDate` as the field name, but the order still follows `createdAt`. Only the direction changes it.

#### What comes back

```json theme={"dark"}
{
  "totalElements": 2,
  "page": 0,
  "size": 50,
  "results": [
    {
      "id": "7f3e9a12-4b6c-4d1e-9a85-2c0f6b8d1e47",
      "createdAt": "2025-01-10 08:15:00",
      "email": "alice@acme.com",
      "password": "Summer2026!",
      "fileName": null,
      "leakId": "b2f4e833-2e12-3219-a2b1-84e39d59e194",
      "leakName": "url_login_pass_combolist_2025",
      "leakPublishDate": "2025-01-06",
      "leakDiscoverDate": "2025-01-09",
      "website": "https://acme.com/login"
    },
    {
      "id": "3c81d0e5-92af-4f7b-b6d2-7a4e1f09c3b8",
      "createdAt": "2024-06-02 14:31:09",
      "email": "alice@acme.com",
      "password": "Spring2024!",
      "fileName": null,
      "leakId": "d71c5a60-0b9e-3f42-8e17-5c2a9b3f4d06",
      "leakName": "mixed_combolist_2024_05",
      "leakPublishDate": "2024-05-28",
      "leakDiscoverDate": "2024-06-01",
      "website": "https://mail.acme.com/"
    }
  ]
}
```

The envelope:

* `totalElements`: the number of matching records. It stops at 10,000, like `leakCount`.
* `page` / `size`: the page you asked for and its size.
* `results`: the records on this page.

There is no page count in the response. Divide `totalElements` by `size` and round up.

Each record:

* `id`: the record's unique ID.
* `createdAt`: when the record was recorded, as `YYYY-MM-DD HH:MM:SS`. The date filters and the sort use this field.
* `email`: the leaked login, as an email address or another login name.
* `password`: the leaked password.
* `website`: the site the credential was used on.
* `leakId` / `leakName`: the leak the record came from. When the leak was published inside a larger collection, these name the collection.
* `leakPublishDate` / `leakDiscoverDate`: when that leak or collection was published and when it was discovered, as `YYYY-MM-DD`.
* `fileName`: not populated. It is always `null`.

`leakName` and the two leak dates come from the leak catalogue. When the catalogue has no entry for the leak, all three are `null` and `leakId` is the record's own leak ID.

> [!NOTE]
>
> **Paging stops at record 10,000.** A page whose last record would pass 10,000 is refused with `422`. At `size=100` the last page you can read is `99`. At `size=10` it is `999`. To reach the rest, flip the sort direction or narrow the date range.

### Errors

The API validates your request first, then hands it to the lookup service that reads the leak index. A `400` never reaches the leak index. For what a live flow does with each status, see [Let the user through when a check cannot answer](#let-the-user-through-when-a-check-cannot-answer).

| Status | When | Body |
| - | - | - |
| `400` | A field is missing, malformed, or out of range. | One entry per field, for example `{"email": ["Enter a valid email address."]}` or `{"size": ["Ensure this value is less than or equal to 100."]}`. A leak search with no `email`, `password` or `website` returns `{"non_field_errors": ["At least one of email, password or website is required."]}`. |
| `401` | The API key is missing, invalid, expired, or revoked. | `{"detail": "Authentication credentials were not provided."}` when the `X-API-Key` header is missing. `{"detail": "Invalid or expired API key."}` when the key is invalid, expired, or revoked. `{"detail": "Invalid or missing service token."}` means the API could not authenticate to the lookup service. Your API key is fine, so treat it like a `502`. |
| `403` | Your organization has no license in force (none, suspended or expired), or its parent organization has none. | `{"detail": "Your license is not active."}` |
| `422` | The lookup service refused a value the first validation let through: an email address on a reserved domain (`.local`, `.test`, `localhost`) or an IP-address domain such as `alice@[192.0.2.1]`, an impossible date such as `2024-02-30`, a `sort` value it does not accept, or a page past record 10,000. | `{"detail": ...}`. For example, `{"detail": "Invalid sort parameter. Use format: field,direction (e.g. createdAt,desc)"}` or `{"detail": "Page is beyond the searchable range. No more than 10000 records are reachable through pagination."}`. A refused email address or date returns `detail` as a list of field errors. |
| `429` | You sent more requests per second than your organization's rate limit allows. | `{"message": "Rate limit exceeded", "retry_after_seconds": 1}`. The `Retry-After` header gives the same wait in seconds. |
| `502` | The lookup service or the leak index failed or answered with something unusable. You also get it when the lookup service is at capacity or times out on its side, which is how a slow lookup usually ends. Back off before you retry. | `{"detail": "Leak search backend unavailable. Please retry."}` or `{"detail": "The requested service is temporarily unavailable."}` |
| `504` | The API's wait for the lookup service ran out. The wait is 30 seconds by default. | `{"detail": "The requested service timed out."}` |

Any other error status from the lookup service reaches you unchanged, with its body. Treat it like a `502`.

### Other credential searches

Credential Check reads the account index, which holds the same parsed `login` / `password` / `website` records the [ULP feed](https://wiki.darknetsearch.com/api/guides/credentials-ulp-feed.md) searches. You pass the raw value, not a Lucene query. The API escapes it for you, so wildcards, quotes and operators match literally.

| You want | Use |
| - | - |
| A yes/no for one email address, password, or pair inside your own app | **Credential Check** (this page) |
| Free-form Lucene over the account index, with `OR`, `NOT` and field pivots | [The ULP feed](https://wiki.darknetsearch.com/api/guides/credentials-ulp-feed.md) |
| Every exposed account on a domain you own, deduplicated and risk-scored | [Filtered credentials](https://wiki.darknetsearch.com/api/guides/credentials-filtered.md) |
| The original leaked dump around a credential | [Raw Data](https://wiki.darknetsearch.com/api/guides/credentials-raw-sweep.md) |
| The whole capture from an infected machine | [Stealer logs](https://wiki.darknetsearch.com/api/guides/credentials-stealer-logs.md) |

Start from [Find leaked credentials](https://wiki.darknetsearch.com/api/guides/credential-exposure.md) when you need more than a yes/no.

### Endpoints

| Action | Endpoint |
| - | - |
| Check a new password | [`credential_check_password_compromised`](https://client-api.leak.center/scalar-docs/#tag/credential-check/POST/api/service/credential_check_password_compromised/) |
| Check an email and password pair | [`credential_check_credentials_compromised`](https://client-api.leak.center/scalar-docs/#tag/credential-check/POST/api/service/credential_check_credentials_compromised/) |
| Check a pair on one site, or read the leak records behind a check | [`credential_check_leak_search`](https://client-api.leak.center/scalar-docs/#tag/credential-check/POST/api/service/credential_check_leak_search/) |
| Check an email address | [`credential_check_email_compromised`](https://client-api.leak.center/scalar-docs/#tag/credential-check/POST/api/service/credential_check_email_compromised/) |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.