---
title: "Check one link, or every link in a message, for known abuse"
method: POST
path: "/v1/link-check"
tags: ["Free checks (no API key)"]
---

# Check one link, or every link in a message, for known abuse

`POST /v1/link-check`

## Request

- Base URL: `https://api.relayshield.net`
- URL: `https://api.relayshield.net/v1/link-check`
- Auth: none required

Keyless and free. Returns the three signals that are immediate and carry no vendor cost: RelayShield's criminal IOC corpus, Google Safe Browsing, and domain registration age. Send `url` for one link, or `urls` for up to 25 at once -- the batch form deduplicates by domain and resolves the whole set in a single Safe Browsing request, so screening a mailbox costs one call rather than one per link.

It never answers `safe`. The best available verdict is `unknown`, meaning nothing is known against the domain, because an absence of evidence is not evidence of absence. Unauthenticated callers share a per-IP daily allowance and a batch counts once per URL; send an API key to lift it.

**Billing.** Free. This endpoint is not metered.

**Note.** No API key required. A key is still accepted and removes the per-IP daily allowance, which is what an integration serving many users should send.

## Request body

- object: Send either `url` or `urls`, not both. `url` returns a single verdict object; `urls` returns a `results` array plus `counts`.
  - `url` string: A single link. Must start with `http://` or `https://`. Omit when sending `urls`.
  - `urls` string[]: Up to 25 links to check in one call. Omit when sending `url`. Results come back in the order submitted, including duplicates.
  - `source` string: Optional integration identifier, recorded so a channel can be measured.

## Response `200`

Success. `ok` is true and `data` holds the result.

- object
  - `ok` true, required
  - `data` object, required
    - `target` string: The link this verdict is about. Single-link form only.
    - `level` 'high' | 'medium' | 'unknown': `high` for an IOC corpus or Safe Browsing hit, `medium` for a domain registered in the last 30 days, `unknown` otherwise. Never `safe`. Single-link form only.
    - `flagged` boolean: True when any signal fired. Single-link form only.
    - `reasons` string[]: Plain-language reasons, one per signal that fired.
    - `signals` object: Structured mirror of `reasons`. The signals are not equivalent and must not be summed: a corpus or Safe Browsing hit is blocklist grade, a young domain is a soft signal every new project also trips. `null` means the signal could not be resolved, which is NOT the same as false.
      - `ioc_corpus` boolean: Domain appears in RelayShield's criminal IOC corpus.
      - `safe_browsing` boolean: Google Safe Browsing flags the domain.
      - `domain_age_days` integer: Days since registration, or null if unknown.
    - `results` object[]: Batch form only. One entry per submitted URL, in order, each carrying `target`, `level`, `flagged`, `reasons` and `signals`.
    - `counts` object: Batch form only.
      - `submitted` integer: URLs received.
      - `checked` integer: URLs actually resolved.
      - `flagged` integer: URLs with at least one signal.
      - `incomplete` integer: URLs NOT resolved in time. Read this before presenting the batch as clean.
    - `incomplete_urls` string[]: Batch form, present only when something was not resolved. These were not checked and are NOT a clean result. Send them again.
    - `note` string: Standing caveat on what a heuristic verdict does and does not mean.

## Other responses

- `405`: Wrong HTTP method. Every endpoint is POST.

---

[API](https://skmtc.dev/relayshield/apis/relayshield-api.md) · [All operations](https://skmtc.dev/relayshield/apis/relayshield-api/llms.txt) · [OpenAPI document](https://skmtc.dev/relayshield/apis/relayshield-api/revisions/4d4721b936dd?raw)
