Scam-Check API · for business

Stop scams inside your product with one API call.

Send a link, a customer's SMS or WhatsApp message, or a web page. Get back a verdict — safe, suspicious or dangerous — with the reasons in plain words your support team and customers can read.

  • 1,000 free credits a month
  • No data stored
  • English · 日本語 · Español · हिन्दी
POST /v1/scan/url
$ curl https://api.safehop.app/v1/scan/url \
  -H "Authorization: Bearer sh_live_…" \
  -d '{"url":"paypal-account-verify-center.shop/login"}'

{
  "verdict": "dangerous",
  "score": 100,
  "reasons": [
    { "id": "brand-impersonation", "points": 40,
      "text": "uses the name \"paypal\" but is not
               paypal's official site" },
    { "id": "new-domain", "points": 35,
      "text": "registered only 4 days ago" },
    …
  ],
  "domain": { "ageDays": 4, "registrar": "…" }
}

Who it's for

Built for the places scams reach people.

Banks & fintech

Customers forward "your KYC is pending" texts to support. Check the message and its links instantly and reply with the reason, before they pay.

Marketplaces & classifieds

Scan links sent in buyer–seller chats and listings. Stop fake payment pages and "pay the delivery fee here" links before a user clicks.

Ad networks & publishers

Scan advertisers' landing pages. SafeHop unlocks cloaked pages that show reviewers a shop and victims a fake virus alert.

Messaging, SMS & email

Rate links in messages at delivery time, in English, Japanese, Spanish and Hindi, with the local brands scammers copy.

Why SafeHop

More than a blocklist lookup.

Lists only know scams someone already reported. SafeHop also judges what a link and a page actually do, so brand-new scams are caught on day one.

Reasons, not just a score

Every verdict lists why: lookalike brand, domain registered 4 days ago, hidden redirect, fake-alert page. Show them to users or your fraud team.

Unmasks hidden scam pages

Cloaked pages keep their scam encrypted until a real person moves the mouse. SafeHop finds the key, unlocks it safely and judges what's inside.

Private by design

Links and messages you send are checked and forgotten — never stored, logged or used for ads. Only a credit count per key is kept.

Quick start

Live in three steps.

Your key stays on your server. Your website sends links or messages to your own backend, your backend asks SafeHop, and you decide what to show.

  1. 1

    Get your key

    Sign in with Google or your work email. The key is shown once. 1,000 free credits a month.

    Get a free key
  2. 2

    Store it as a secret

    Put it in your server's environment, not in your code or website. If it ever leaks, sign in again to get a new one.

    .env
    # .env on your server: never commit it, never put the key in browser code
    SAFEHOP_API_KEY=sh_live_your_key_here
  3. 3

    Check links from your server

    Call SafeHop before a link or message is shown to other people, and act on the verdict.

    // Node.js 18+ (built-in fetch). Runs on your server, never in the browser.
    export async function checkLink(url) {
      try {
        const res = await fetch('https://api.safehop.app/v1/scan/url', {
          method: 'POST',
          headers: {
            Authorization: `Bearer ${process.env.SAFEHOP_API_KEY}`,
            'content-type': 'application/json',
          },
          body: JSON.stringify({ url }),
          signal: AbortSignal.timeout(10000),
        });
        return res.ok ? await res.json() : null;
      } catch {
        return null; // SafeHop unreachable: let the content through
      }
    }
    
    const result = await checkLink(linkFromUser);
    if (result?.verdict === 'dangerous') {
      // block it, or show result.reasons[0].text to the user
    }
    <?php
    function safehop_check_link(string $url): ?array {
        $ch = curl_init('https://api.safehop.app/v1/scan/url');
        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 10,
            CURLOPT_HTTPHEADER => [
                'Authorization: Bearer ' . getenv('SAFEHOP_API_KEY'),
                'Content-Type: application/json',
            ],
            CURLOPT_POSTFIELDS => json_encode(['url' => $url]),
        ]);
        $body = curl_exec($ch);
        $ok = curl_getinfo($ch, CURLINFO_HTTP_CODE) === 200;
        curl_close($ch);
        return $ok ? json_decode($body, true) : null; // null: let the content through
    }
    
    $result = safehop_check_link($linkFromUser);
    if ($result && $result['verdict'] === 'dangerous') {
        // block it, or show $result['reasons'][0]['text']
    }
    import os
    import requests
    
    def check_link(url):
        try:
            r = requests.post(
                "https://api.safehop.app/v1/scan/url",
                headers={"Authorization": f"Bearer {os.environ['SAFEHOP_API_KEY']}"},
                json={"url": url},
                timeout=10,
            )
            return r.json() if r.ok else None
        except requests.RequestException:
            return None  # SafeHop unreachable: let the content through
    
    result = check_link(link_from_user)
    if result and result["verdict"] == "dangerous":
        ...  # block it, or show result["reasons"][0]["text"]
    curl https://api.safehop.app/v1/scan/url \
      -H "Authorization: Bearer $SAFEHOP_API_KEY" \
      -H "content-type: application/json" \
      -d '{"url":"paypal-account-verify-center.shop/login"}'
    VerdictWhat to do
    safeLet it through
    suspiciousShow a warning with reasons[0].text
    dangerousBlock it, or ask the user to confirm

    Good practice

    • Never put the key in browser JavaScript or a mobile app; anyone could copy it.
    • If SafeHop does not answer within 10 seconds, let the content through.
    • Cache the result for a link for a few hours to save credits.
    • For whole SMS, chat or email text, use POST /v1/scan/message with {"text": "…"}.

Docs

Four endpoints. JSON in, JSON out.

Base URL https://api.safehop.app. Send your key in the Authorization: Bearer sh_live_… header and a JSON body.

EndpointBodyWhat it doesCredits
POST /v1/scan/url{ "url" }Rates a link: lookalike brands, domain age and registrar, live scam lists, redirects and ad-click destinations.1
POST /v1/scan/batch{ "urls": [ … ] }Up to 100 links in one call.1 per link
POST /v1/scan/message{ "text" }Rates an SMS, WhatsApp message or email and every link in it: fake delivery, KYC, prize, job, investment and police scams.1
POST /v1/scan/page{ "url" }Reads the page (as text, never run): fake logins, fake virus alerts, "can't leave" traps, hidden encrypted loaders.5
GET /v1/accountYour plan, monthly limit and credits used.0

Verdicts

  • safe score under 30: no warning signs found
  • suspicious 30–69: warn the user, ask them to double-check
  • dangerous 70–100: block, or show a strong warning

Errors

  • 400 the body isn't valid
  • 401 missing or wrong key · 403 key disabled
  • 429 per-minute rate or monthly credits used up
POST /v1/scan/message
{ "text": "IndiaPost: your parcel is on hold due to
  incomplete address. Pay Rs 25 to reschedule:
  https://indiapost-track.top/pay" }

{
  "verdict": "dangerous", "score": 85,
  "scamType": "a fake delivery message",
  "advice": "Delivery companies don't ask for fees…",
  "reasons": [ … ],
  "links": [ { "url": "https://indiapost-track.top/pay",
               "verdict": "suspicious", … } ]
}

Pricing

Start free. Pay as you grow.

Trial

Free

1,000 credits / month
30 requests / minute

Get a key

Starter

$49/month

25,000 credits / month
120 requests / minute

Start now
Most popular

Growth

$299/month

250,000 credits / month
600 requests / minute

Start now

Enterprise

Custom

Millions of credits, scam-site feed, support agreement

Talk to us

What a credit buys: checking one link or one message costs 1 credit (a batch costs 1 per link); a full page scan costs 5. Prices are in US dollars.

Get a free trial key.

Create your key in a minute: 1,000 free credits every month, no card needed. Upgrade or cancel any time from your account page.