A phishing domain does not arrive on a social platform once. It arrives in a caption, in forty bios, in a burst of replies under a trending post, in direct messages sent from accounts that were themselves taken over last night, and in ad creative submitted from six freshly funded accounts. Screening it means holding the whole list locally and checking at submission — because at post volume, asking a remote service about every link is not an architecture, it is a bill.
/batch request for queue and appeal workTeams tend to build link screening for the feed, then discover the same domain has been sitting in a bio for three weeks and moving through direct messages the whole time.
The first thing worth doing on a platform of any size is enumerating every place a user can put a destination, because the list is always longer than the trust and safety roadmap assumes. Captions and post bodies are obvious. Comments and replies are usually covered too. What tends to be missed is everything that is not a piece of content in the ordinary sense: profile bios and the link fields attached to them, pinned link collections, display names that contain a bare hostname, image alt text, the description field on a live broadcast, event listings, group and community descriptions, and the destination on an ad creative, which lives in an entirely separate submission pipeline built by a different team.
A caption is written once and read many times, so a check at submission covers the whole lifetime of the post. A bio link is written once, edited freely afterwards, and points at a destination that can change ownership long after the check passed. A direct message is high-trust, low-volume per sender and enormously sensitive to how you handle content. An ad destination is deliberately amplified, carries an implicit endorsement, and is the only surface where somebody paid you for the distribution.
It is entirely normal for an attacker to test each surface in turn, find the one that is not screened, and industrialise it. If comments are checked and bios are not, the bios fill up. If the main feed is checked at post time and the edit path is not, everything gets posted clean and edited afterwards. Attackers probe systematically because the cost of probing is close to zero, and a partial system that everyone believes is complete generates a false sense of safety.
Screening after publication leaves a window in which the link is live and being clicked, and on a platform with velocity that window is where most of the harm occurs. Screening client-side means the check can be bypassed by anybody willing to talk to your API directly, which is precisely the population you are trying to stop. The API key is the username chosen at registration, it is a server-side secret, and it must never appear in a mobile bundle or in page source.
A domain that was clean when a bio link was set can be compromised, can expire and be re-registered, or can simply have been dormant infrastructure not yet activated. Because you hold the whole database locally and it rebuilds daily, sweeping every stored destination against the current list is a local set intersection rather than an API workload — cheap enough to run nightly across the entire corpus of profile links, pinned collections and active ad destinations.
Highest volume, simplest lifecycle. Check at submission and again on edit; the destination almost never changes after publication, so one good check covers the whole post.
Low volume, long lifetime, frequently ignored. These need daily re-screening far more than posts do, because the destination outlives the check by months.
The highest-trust surface and the most privacy-sensitive. Extract the hostname only, check locally, and never move message content off the platform.
Where campaign amplification concentrates, because reply threads under trending posts inherit the reach of the original without inheriting its scrutiny.
Paid amplification with an implied endorsement. The one surface where a synchronous check inside the review pipeline is unambiguously worth the latency.
Broadcast descriptions, event pages and community about-sections are link surfaces with real reach and almost no moderation attention. Cover them explicitly.
Page administrators and creators are targeted with the two things a platform can take away from them, and both lures arrive dressed as the platform itself.
The most productive social phishing lure in circulation is a message that appears to come from the platform, about the platform, threatening something the recipient genuinely cares about. Two pretexts dominate because they map onto real processes that page administrators already fear. The first is verification: an offer of a badge, a warning that an existing badge is about to lapse, or a request to "confirm eligibility" before a review deadline. The second is a copyright or community-standards strike, usually with a countdown and an appeal link, because an unappealed strike sounds like it ends a business.
They arrive inside the platform, or in mail closely imitating platform notifications, and they concern administrative processes that are genuinely obscure to the people affected. A creator with two hundred thousand followers has probably never seen the real appeals interface, has no mental model of what it should look like, and is under immediate commercial pressure to act. The message does not need to be sophisticated — it needs to be plausible for ninety seconds.
A compromised personal account costs the individual their contacts and their embarrassment. A compromised business page or creator account comes with reach, an established posting cadence that makes new posts look normal, existing advertiser relationships, frequently a linked payment method and ad account that can buy amplification for whatever the attacker posts next — and a direct message inbox full of people who trust it.
The attacker is not hand-crafting a page for each victim; they stand up one or a small cluster of hostnames, run them until the destinations stop working, and move on. So once a hostname is observed and verified, blocking it removes the destination from every simultaneous attempt using it — across direct messages, comment replies and tagged mentions. The brand protection page covers imitations of your own domain.
After an account is compromised the owner searches for help, and the results and replies they encounter are heavily seeded with fake support and recovery services. Somebody who has already lost an account and is desperate to get it back is the least sceptical audience there is, and the second compromise frequently does more damage than the first. Screening replies to distress posts is unglamorous, targeted work with a very high hit rate.
The engineering constraint on a social platform is not accuracy. It is that whatever you build has to run inside a request path that is already tight.
Post submission is a latency-sensitive path with a hard budget, and anything you add to it competes with everything else that already lives there — media processing, spam scoring, ranking signals, notification fan-out. That constraint shapes the whole design. A network call to an external service inside the submission path is the wrong shape at any meaningful volume: it adds a dependency whose failure mode is either blocking every post or silently failing open, and it adds a round trip to a path where you are counting milliseconds.
The feed downloads as CSV with domain, category and dns_status columns, or as JSON, and several hundred thousand hostnames load comfortably into an in-memory set or a small embedded key-value store on every application host. A lookup is then a hash comparison measured in microseconds, with no network involved, no failure mode depending on somebody else's uptime, and no per-check cost. Feed downloads are unlimited on a subscription, so refreshing often costs nothing beyond bandwidth.
Take the destination, follow it to a canonical form, and reduce it to the registrable hostname before comparing. Handle the awkward cases explicitly: internationalised domain names normalised to their encoded form, mixed-case input, trailing dots, embedded credentials in the authority section, and shorteners — which need resolving to a final destination before the check means anything, and which have their own page on shortener abuse. A layer that compares literal strings will be bypassed within days of launch.
Users reasonably expect private messages to be private, and any scanning must be justifiable to them in a sentence. The design that survives is narrow: extract only the hostname from a link, compare it against a local list, and never transmit, store or derive anything from the surrounding text. Because the comparison happens on your own infrastructure against a list you downloaded, no third party learns that a message was sent, let alone what it contained — a claim that is architecturally rather than contractually true.
Moderation queues sweeping a backlog. Appeal review, where somebody needs a current verdict on one hostname. Ad creative review, where the pipeline is already asynchronous. Investigations pivoting on a cluster of accounts. All batch or single-item workloads where /batch at a hundred domains per request, one lookup each, with checked, phishing_found and credits_used returned, is the right tool — and where ten requests per second is a comfortable ceiling.
Moderating individual pieces of content against a coordinated campaign is a losing shape. Removing the destination is not.
Content moderation is built around the post: something is reported, reviewed, actioned, and the account accumulates a record. That model works reasonably well for the things it was designed for, and it works very poorly against link-based phishing, because the economics are asymmetric in the attacker's favour. Producing another post costs nothing. Producing another account costs slightly more than nothing. Reviewing a post costs a reviewer's attention, which is the scarcest resource the platform has. A campaign that generates content faster than you can review it wins by arithmetic alone, regardless of how good your reviewers are.
It has to be registered, hosted, given working DNS, and — if it imitates a login page — fitted with a plausible certificate. Everything upstream of it is disposable. Blocking one verified hostname therefore neutralises every piece of content pointing at it at once: the posts you found, the posts you did not, the ones in bios, the ones sitting unopened in direct message inboxes, and the ones that have not been posted yet.
Counting removed posts measures your reviewers' throughput. Counting distinct hostnames blocked, and the volume of content pointing at each, measures the campaign — and the shape of that distribution is informative. A few hostnames carrying most of the blocked content is a coordinated operation and should be escalated as one. A long flat tail is background noise, and treating it as a crisis wastes attention the concentrated cluster deserves.
Domains added on the same day, imitating the same brand or the same platform process, are usually one operation being stood up. Watching a cluster appear and disappear a week later tells you its lifecycle — and because entries only exist while they have an active A record verified through rotating proxy infrastructure, a removal genuinely means the host stopped resolving rather than that somebody's reporting window closed.
0.0 for anything not on the listAny automated action at platform volume produces mistakes in absolute numbers. The design question is what the mistake costs and how fast it is fixed.
Silently deleting content is the response users hate most and the one platforms reach for most often. It produces no explanation, no route to challenge, and a strong impression of arbitrary enforcement — which is corrosive well beyond the individual case, because the affected user tells other people and the story that circulates is never the accurate one. A domain-based block is unusually easy to explain, and that advantage is worth using rather than discarding.
An interstitial warning that names what was found and lets the user proceed suits a link in a direct message between two people who know each other. Blocking the destination while leaving the post visible works where the content has value beyond the link. Full removal fits paid ad creative, where the platform is actively amplifying. Whichever you pick, tell the user something specific and truthful — this destination is on a list of domains verified as currently resolving and confirmed as credential harvesting.
There is no score to interpret and no threshold to argue about: either the hostname is in the database or it is not. A reviewer handling an appeal runs it through /check, reads is_phishing, category, dns_status and last_checked, and has a defensible answer in seconds. When a domain has since been remediated and dropped out, that is visible immediately and the content can be reinstated without a debate.
Record what the verdict was when you acted, not just what it is now, because a domain leaving the database later does not mean the original block was wrong — it means the host stopped resolving. A reviewer who cannot see the original evidence will reinstate correctly-actioned content and, worse, will start to distrust the signal. Storing the check response alongside the enforcement record costs almost nothing and settles a whole category of internal argument.
Platforms operating under proactive-measures obligations are increasingly asked to evidence not just what they removed but what mechanism found it and how current that mechanism was. A dated daily feed, an archived changelog, and per-action records showing the verdict at action time constitute an unusually clean audit trail — and one that does not depend on retaining user content in order to prove anything.
Paid destinations are already reviewed asynchronously, so a call adding tens of milliseconds is invisible in a pipeline measured in minutes. Check the destination at submission, again at approval, and again on a schedule for the life of the campaign — advertisers can and do change landing pages after approval.
Extract the hostname, compare against the local list, act on the link rather than the message. No content leaves your infrastructure, nothing is stored beyond the enforcement record, and no third party learns that a message existed.
Bio links, pinned collections, event pages and live campaign destinations were checked once, possibly a long time ago. Because the whole database sits locally and rebuilds daily, re-screening the entire stored corpus is a set intersection you can run overnight rather than an API workload you have to budget for.
Users who have just lost an account are the softest target on the platform, and the replies to their public distress posts are reliably seeded with fake recovery services. Screen links in replies to accounts recently flagged as compromised, and in unsolicited messages sent to them.
This is a known-bad lookup and nothing more. A hostname that is not in the database returns 0.0, and that means "not on this list" — never "safe". A domain registered this morning and used for the first time this afternoon will not be present, because the DNS verification that makes the data trustworthy also means an entry cannot exist before the host has been observed and confirmed as resolving. On a platform with real velocity, that window is genuinely exploited.
0.0 response is a design error, and it is the kind that gets quoted back at you.Including the volume objection, the privacy objection and the one about who is accountable when it gets something wrong.
No, and it is not supposed to. The rate limit is ten requests per second per API key, which is a sensible ceiling for investigation and queue work and nowhere near submission-path volume on a platform of any size. Anyone who tells you a remote per-link call scales to social media volume is describing a bill rather than an architecture.The shape that works is the feed: download the full database daily, hold it in memory or in an embedded store on every application host, and check locally in microseconds with no network call at all. Feed downloads are unlimited on a subscription, so the cost does not move with your traffic. Keep the API for the workloads where volume is small and freshness matters — appeals, investigations, moderation queues and ad review.
By keeping the scope genuinely narrow and then describing it accurately. What this requires is extracting the hostname from a link and comparing it against a list you already hold locally. It does not require reading, storing, transmitting or deriving anything from the message text, and it does not tell any third party that a message was sent.That is a materially different proposition from content scanning and it should be described as such, in plain words, in the policy. The strongest version of the claim is architectural: because the comparison happens on your own infrastructure against a downloaded list, there is no outbound request per message that anyone could log, subpoena or correlate. Resist the temptation to broaden the scope later — the narrowness is the entire justification.
Plan for it and it is a support interaction rather than an incident. Verification via an active DNS record keeps the false-positive rate low, but at platform scale a low rate still produces absolute numbers, and pretending otherwise is how you end up with an unstaffed appeals queue.Three things make the difference. Keep a local allow-list applied after every daily import, so an override survives the next reload. Make the notice specific about what was found and offer a route to challenge it. And store the check response as it was at action time, so a reviewer can distinguish "this was never on the list" from "this was on the list and has since been remediated" — the second is not a false positive and should not be recorded as one.
It defeats a naive implementation, yes. If you check the shortener's own hostname you have learned nothing, because that hostname is legitimate and shared by millions of benign links. The check has to happen against the final destination, which means resolving the chain first.That is standard work for a platform of any size and you probably already do it for other reasons — preview cards, spam scoring, and link attribution all need the destination. Follow redirects with a hop limit, apply the check at each hop and at the end, and be aware that some hostile chains serve different destinations depending on the requesting address or user agent. The URL shorteners page goes into the evasion patterns in more detail.
This is a legal question with a jurisdiction-dependent answer and you should ask your own counsel rather than a vendor page. What can be said factually is that the tool makes a narrow, verifiable claim — a hostname was observed and confirmed as currently resolving credential-harvesting infrastructure — and does not claim to identify everything hostile.In practice, platforms subject to proactive-measures obligations tend to find that a dated, verifiable screening mechanism with an archived changelog and per-action evidence is easier to explain to a regulator than an entirely manual process, precisely because it can be evidenced. Documenting the known limitation honestly is part of that, not a weakness in it. Overstating coverage internally is the failure mode that actually creates exposure.
The feed is the relevant product, because submission-time screening has to be local. The Daily Threat Feed is $499 per month, with unlimited downloads, and all plans include priority support — which comfortably covers appeals and investigation work without a separate purchase.Note that the cost does not scale with your post volume, which is the property that matters here: whether you screen a million links a day or a hundred million, the subscription is the same because the checks happen on your hardware. Credit packages exist for the API side alone and start at Growth at $99/month for 25,000 lookups; the full table is on the pricing page. , and enterprise delivery over SFTP or S3 is quoted rather than listed.
The architecture is the same at any size — a daily download and a local set membership test is not a big-company technique, and it runs perfectly well on a single application server. If anything, a smaller platform benefits more, because it has no trust and safety headcount to absorb the moderation load that a coordinated campaign generates.Where scale changes the answer is in the appeals path. A large platform needs a self-service appeal flow; a small community can handle it in a support inbox. Do not build the large-platform machinery before you need it, but do decide from day one what a user is told when a link is blocked, because that message is the entire user-facing surface of this system.
Mostly, yes, and the trade-off is smaller than it first appears. Attackers already know whether their destination works, because they can test it themselves from an ordinary account in seconds. Withholding the reason from a legitimate user therefore costs you goodwill without denying an adversary anything they could not trivially discover.What is worth withholding is timing and mechanism detail — do not tell anybody when your list refreshed, how many entries it holds, or which specific check fired. Say what was found and offer the appeal route. Keep the operational specifics in your own logs, where they inform your enforcement analysis rather than somebody else's evasion testing.
Read the current database size from the open statistics endpoint, pull one day's file, and intersect it with a week of destinations your own users have posted. The number that decides this is not how large the list is — it is how many links already in your corpus are on it right now.