Ask ChatGPT Ask Claude

Phishing link checker API

`GET /v1/url` checks where a link lands after shorteners and redirects. It checks the destination domain's age and brand imitation, plus phishing and malware URL feeds. It returns a `verdict` of scam, suspicious, safe or unknown with `reason_codes`. When the host is a top site, a business's own site, a URL shortener or a platform anyone can publish on, Bewary lists the exact link instead of the whole host. A call costs 1 credit (30 with deep=true), a free key gets 1,000 credits a month, no card, and it answers in median 477 ms.

Endpoint
GEThttps://api.bewary.app/v1/url
Price
1 credit, deep=true 30
Free key
1,000 credits a month, no card
Speed
Median 477 ms, measured
Returns
verdict, p_scam, entities with typed facts, reason codes
MCP tool
lookup_url

Try it

Free from this page: 10 lookups a day
GET/v1/url
Recorded answer, October 31, 2026
Scamp_scam0.9116
Reason codes
High
Fake domain (brand + suspicious TLD)DOMAIN_BRAND_ON_ABUSED_TLD
Medium
Lookalike domainDOMAIN_LOOKALIKE_BRAND_WORD
Entities
urlhttp://paypal-login-secure.top/verify?account=updateScam
domainpaypal-login-secure.topScam

Call it

curl "https://api.bewary.app/v1/url?u=http%3A%2F%2Fpaypal-login-secure.top%2Fverify%3Faccount%3Dupdate"

Act on the result

Copy-paste code that turns the answer into a decision. It runs with or without BEWARY_API_KEY. TypeScript uses bewary.d.ts.

import os, requests

def link_action(url):
    """block, warn or allow, for a link preview or before an agent opens a page"""
    key = os.environ["BEWARY_API_KEY"]          # a free key: bewary.app/account
    headers = {"Authorization": f"Bearer {key}"}
    try:
        r = requests.get("https://api.bewary.app/v1/url", params={"u": url},
                         headers=headers, timeout=10)
        r.raise_for_status()
    except requests.RequestException:
        return "warn"                      # never fail open
    verdict = r.json()["verdict"]
    return {"scam": "block", "safe": "allow"}.get(verdict, "warn")

print(link_action("http://paypal-login-secure.top/verify"))   # block

Response

The fields you will use

verdict
The link's verdict, judged together with its domain.
entities[0].facts.final_url
Where the link really lands.
entities[1].facts.lookalike
The brand its domain imitates, and how.
entities[1].facts.age_days
How old the domain is.
reason_codes
Why, strongest first.
{  "schema_version": "1.7.0",  "request_id": "req_01M69WHC5ZP5H7M54R0HKK97QD",  "checked_at": "2026-10-31T21:06:05Z",  "engine": "bewary-engine/3.0.0",  "models": {    "calibration": "interim-band-v0"  },  "input": {    "kind": "lookup",    "channel": "unknown",    "language": null,    "region": null  },  "verdict": "scam",  "p_scam": 0.9116,  "risk_score": 91,  "confidence": 1,  "classification": null,  "reason_codes": [    "DOMAIN_BRAND_ON_ABUSED_TLD",    "DOMAIN_LOOKALIKE_BRAND_WORD"  ],  "entities": [    {      "id": "e1",      "type": "url",      "value": "http://paypal-login-secure.top/verify?account=update",      "normalized": "http://paypal-login-secure.top/verify?account=update",      "role": "link_target",      "verdict": "scam",      "p_scam": 0.9116,      "risk_score": 91,      "reason_codes": [        "DOMAIN_BRAND_ON_ABUSED_TLD",        "DOMAIN_LOOKALIKE_BRAND_WORD"      ],      "facts": {        "scheme": "http",        "host": "e2",        "final_url": "http://paypal-login-secure.top/verify?account=update",        "redirect_chain": null,        "shortener": false,        "redirect_resolved": true,        "short_link": null,        "page": null,        "target_countries": null,        "list_count": null,        "platform_status": null      },      "related": [        "e2"      ],      "datasets": [        {          "dataset": "threat_feeds",          "coverage": "WW",          "in_coverage": true        },        {          "dataset": "threat_aggregates",          "coverage": "WW",          "in_coverage": true        },        {          "dataset": "web_pages",          "coverage": "WW",          "in_coverage": true        },        {          "dataset": "short_links",          "coverage": "WW",          "in_coverage": false        },        {          "dataset": "platform_pages",          "coverage": "WW",          "in_coverage": true        },        {          "dataset": "community_reports",          "coverage": "WW",          "in_coverage": true        }      ]    },    {      "id": "e2",      "type": "domain",      "value": "paypal-login-secure.top",      "normalized": "paypal-login-secure.top",      "role": "link_target",      "verdict": "scam",      "p_scam": 0.9116,      "risk_score": 91,      "reason_codes": [        "DOMAIN_BRAND_ON_ABUSED_TLD",        "DOMAIN_LOOKALIKE_BRAND_WORD"      ],      "facts": {        "registrable_domain": "paypal-login-secure.top",        "registered_domain": "paypal-login-secure.top",        "tld": "top",        "tld_abuse": "high",        "registered_at": null,        "expires_at": null,        "age_days": null,        "age_bucket": "unknown",        "registrar": null,        "dns": {          "resolves": false,          "a": null,          "mx": null,          "ns": null        },        "status": "gone",        "cert_first_seen": null,        "lookalike": {          "brand_id": "paypal",          "technique": "brand_word"        },        "platform": "none",        "allowlisted": false,        "listed": false,        "seen_in_checks_7d": null,        "country": null,        "target_countries": null,        "list_count": null,        "site": null,        "platform_status": null,        "registry_status": null,        "hold": null,        "registered": false,        "registration_checked_at": "2026-10-30T19:18:58Z",        "archive": {          "checked_at": "2026-10-30T19:20:08Z",          "captured": false,          "first_capture_at": null,          "last_capture_at": null,          "captured_before_registration": null,          "months_before_registration": null,          "last_capture_before_registration_at": null        },        "first_seen_by_bewary": null,        "first_listed_publicly": null      },      "related": [],      "datasets": [        {          "dataset": "registration",          "coverage": "WW",          "in_coverage": true        },        {          "dataset": "dns",          "coverage": "WW",          "in_coverage": true        },        {          "dataset": "certificates",          "coverage": "WW",          "in_coverage": true        },        {          "dataset": "web_archive",          "coverage": "WW",          "in_coverage": true        },        {          "dataset": "threat_feeds",          "coverage": "WW",          "in_coverage": true        },        {          "dataset": "threat_aggregates",          "coverage": "WW",          "in_coverage": true        },        {          "dataset": "brand_registry",          "coverage": "WW",          "in_coverage": true        },        {          "dataset": "platform_pages",          "coverage": "WW",          "in_coverage": true        },        {          "dataset": "community_reports",          "coverage": "WW",          "in_coverage": true        }      ]    }  ],  "signals": [    {      "code": "DOMAIN_BRAND_ON_ABUSED_TLD",      "entity": "e2",      "severity": "high",      "weight": 55,      "source_class": "heuristic",      "p": null,      "dataset": "brand_registry",      "coverage": "WW",      "evidence": {        "brand_id": "paypal",        "tld": "top"      }    },    {      "code": "DOMAIN_LOOKALIKE_BRAND_WORD",      "entity": "e2",      "severity": "medium",      "weight": 35,      "source_class": "heuristic",      "p": null,      "dataset": "brand_registry",      "coverage": "WW",      "evidence": {}    }  ],  "degraded": false,  "degraded_reasons": [],  "cached": false,  "explanation": null}

Real responses, captured from the live API on 2026-10-31. Every response has this shape: see Response format.

Link facts

Every key is in every response, null when unknown. A key marked Coming stays null until its source ships, so you can code against it now.

KeyTypeMeaning
scheme http | https | other or null The link's scheme.
host entity id or null The domain entity this link points to.
final_url string or null Where the link lands after shorteners and redirects.
shortener boolean or null The link uses a URL shortener.
redirect_resolved boolean or null A shortened link was followed to its destination.
page object or null With deep=true: whether the page was read (analyzed) and which brand it shows (brand_shown). Its has_login_form and has_payment_form keys stay null for now.
redirect_chain array or null The short links read on the way, in order.
short_link object or null On a shortener: the link's state as the shortener answers it, such as disabled for abuse.
platform_status object or null A form or link-in-bio platform's own answer about this URL, such as a takedown.
target_countries array or null Countries whose people it is aimed at, from the national lists that name it (PL, TW, DE, US).
list_count integer or null How many lists name it, ours and others'.

Parameters

NameTypeDescription
uquery, required string The full URL, URL-encoded. Shorteners are followed.
deepquery boolean Also read the destination page with a model (30 credits, API key required). Default false.

Questions

Can I check a link before my app or agent opens it?

Yes, and the check never opens the suspicious site. Without deep=true, Bewary contacts only known link shorteners such as bit.ly, t.co and tinyurl.com to see where they point, and reads open-redirect targets from the URL itself. deep=true reads the destination page from our side, never from your app.

Does it expand shortened links?

Yes, GET /v1/url follows shorteners and redirects and returns the domain the link points to, with that domain's facts.

What makes a link a scam?

Mostly its domain: a brand name on a cheap, abused TLD (paypal-login-secure.top), a lookalike spelling, a domain registered days ago, or a listing on a threat list or our scam list. The reason codes name which.

How fast is it?

Measured median 477 ms for a link seen for the first time. Following a slow shortener can take a few seconds.

Used for

Reason codes it can return

CodeMeaningSeverity
LISTED_BLOCKLISTOn scam blocklistCritical
DOMAIN_ALLOWLISTEDEstablished domain (allowlist)Info
DOMAIN_AGE_GT_1YEstablished domainInfo
DOMAIN_AGE_90D_1YEstablished domain (<1y)Info
DOMAIN_AGE_30_90DRecently-registered domainMedium
DOMAIN_AGE_LT_30DBrand-new domainMedium
DOMAIN_MULTITENANT_PLATFORMHosted on a public platformInfo
DOMAIN_AGE_UNKNOWNDomain age unknownInfo
DOMAIN_LOOKALIKE_BRAND_WORDLookalike domainMedium
DOMAIN_MIXED_SCRIPTFake domain (mixed alphabets)High
DOMAIN_HOMOGLYPH_BRANDFake domain (homoglyph)High
DOMAIN_BRAND_ON_ABUSED_TLDFake domain (brand + suspicious TLD)High
DOMAIN_TYPO_BRANDPossible fake domain (typo)Medium
DOMAIN_BRAND_SCAM_WORDPossible fake domain (brand + scam word)Medium
TLD_HIGH_ABUSERisky domainMedium
URL_LISTED_THREAT_INTELKnown dangerous linkCritical
URL_HIGH_RISK_SCOREHigh-risk linkHigh
URL_SUSPICIOUS_SCORESuspicious linkMedium
PAGE_MALICIOUSDangerous pageHigh
DOMAIN_DECEPTIVE_STRUCTUREA brand name placed before an @ so the real host hides after itHigh
DOMAIN_CERT_LT_48HFirst TLS certificate issued in the last 48 hoursMedium
URL_SHORTENERUses a link shortenerInfo
URL_REDIRECT_CROSS_DOMAINRedirects to a different domainMedium
LISTED_CAUTIONOn a broad threat list that also holds spam and malware hostsMedium
PROTECTIVE_DNS_BLOCKEDBlocked by public protective DNS resolversMedium
PLATFORM_TAKEDOWNThe hosting platform took it downHigh
PLATFORM_WARNINGThe platform shows a phishing or malware warning in front of itHigh
DOMAIN_REGISTRY_HOLDThe registry holds the domain out of the DNSHigh
DOMAIN_REGISTRAR_HOLDThe registrar suspended the domainMedium
SHORTLINK_DISABLEDThe link shortener disabled the linkHigh
SHORTLINK_WARNINGThe link shortener warns before the destinationMedium
PAGE_CREDENTIAL_HARVESTThe page asks for a login it should notHigh
PAGE_PAYMENT_HARVESTThe page asks for card or bank details it should notHigh

Reserved codes are in the contract, not emitted yet. All reason codes

Updated · This page as Markdown · Changelog