URL & Phishing Scanner API
The URL & phishing scanner scores domains/URLs, captures redirect flows, pulls WHOIS/DNS/SSL data, and emits persona-ready EvidencePacks (Dralvia's exportable evidence reports). Use the REST endpoints below to automate scans from CI/CD, ticketing, or cron jobs.
Authentication & headers
- Use a workspace API key (generate it via
https://dralvia.tech/#/api-keys). PreferAuthorization: Api-Key <key>(legacy:X-API-KEY). - Signed-in browser sessions are supported for portal workflows. Workspace API keys are preferred for automation.
X-Actor: optional string that tags jobs (shown in audit logs and EvidencePacks).
All endpoints accept and return JSON.
Base URL
- Workspace persona:
https://dralvia.tech/api/tenant
Examples below show the path only. Use the versioned path for the core scan request, for example POST /v1/scan, and prefix it with the base URL.
POST /v1/scan
Runs a synchronous scan and waits for the final verdict.
{
"domain": "example.com",
"force_scan": false
}
Response (abbreviated):
{
"domain": "example.com",
"score": 21,
"risk_level": "medium",
"flags": ["ssl:missing_hsts", "content:brand_impersonation"],
"campaign_tagging_degraded": false,
"campaign_tagging": {
"feature_flag_enabled": false,
"shadow_mode": true,
"degraded": false,
"suppressed_reason": "feature_flag_disabled",
"autonomous_tags": []
},
"detailed_results": { "...": "..." },
"tabs": {
"detection": { "evidencepack": { "...": "..." } },
"redirect_timeline": { "...": "..." }
}
}
Use force_scan: true when you need to bypass caching (counts against the force-scan quota exposed by licensing).
OAuth/OpenID consent detection notes:
- URL scans can now persist
detailed_results.oauth_scopewhen the target looks like an OAuth or OpenID authorize flow. - The scanner promotes both classic high-risk scope abuse and fake-host authorize flows that reference a known provider brand on a different domain.
- New flags include
oauth:provider_mismatchandoauth:encoded_auth_payloadwhen the URL hides a nested authorize target inside an encoded payload. Packed nested targets can now be decoded from hex, standard base64, and URL-safe base64 forms. - New flags also include
oauth:malformed_authorize_flowwhen the URL uses a broken callback or inconsistent response-mode pattern, andoauth:generic_auth_handoffwhen a fake consent/login handoff avoids explicit scopes but still behaves like an authorize flow on a non-provider host. - Content-page auth-flow detection now also recognizes broader provider-family wording such as Apple, GitHub, Slack, Zoom, and Dropbox-style sign-in prompts in addition to the earlier Google/Microsoft/Okta set.
- The persisted detail uses the shape
detailed_results.oauth_scope.{data,flags,score}and includes fields such asprovider_hints,provider_mismatch,redirect_host,decoded_targets,authorize_flow_anomalies,generic_auth_terms, andrecommended_action.
Certificate Transparency notes:
- URL and domain scans read the public Certificate Transparency history for the
domain and persist it under
detailed_results.ssl.data.ctwith fieldsentry_count,newest_age_days,oldest_age_days,issuers,sibling_domains,has_wildcard, andnew_footprint. Human-readable lines are added todetailed_results.ssl.data.ct_evidence. - New flag
ct:new_certificate_footprintis raised when the domain's entire certificate history only just appeared, which indicates newly stood-up infrastructure rather than a routine renewal. It carries a modest weight. - New flag
ct:freshly_issued_certificaterecords that the newest certificate was issued in the last few days. It is evidence only and carries no weight on its own, because legitimate sites renew certificates frequently. - The lookup is best-effort. If the Certificate Transparency source is slow or
unreachable the scan still completes and the
ctfields are omitted.
Public web history notes:
- URL and domain scans read the public Internet Archive history for the domain
and persist it under
detailed_results.whois.data.domain_historywith fieldsfirst_seen_days_ago,capture_days,no_public_history, andthin_public_footprint. Human-readable lines are added todetailed_results.whois.data.domain_history_evidence. - New flag
history:thin_public_footprintis raised when the domain has little or no established public-web presence, a corroborating signal of newly stood-up or recently repurposed infrastructure. It carries a modest weight and stacks with the WHOIS registration-age signals. - New flag
history:recent_first_seenrecords that the earliest archived capture is recent. It is evidence only and carries no weight on its own. - The lookup is best-effort. If the archive is slow or unreachable the scan
still completes and the
domain_historyfields are omitted.
Hosting provider and ASN notes:
- URL and domain scans classify the resolved host's hosting provider / ASN and
persist it under
detailed_results.geolocation.data.hosting_classificationwith fieldshosting_provider,asn,abuse_prone, andmatched_marker. Human-readable lines are added todetailed_results.geolocation.data.hosting_evidence. - New flag
infrastructure:hosting_provider_identifiedrecords the provider as evidence and carries no weight on its own. - New flag
infrastructure:abuse_prone_hostingis raised when the provider or ASN matches an abuse-associated marker (such as bulletproof or offshore hosting). It carries a modest weight and names the matched marker in the evidence.
Host and infrastructure context notes:
- URL and domain scans record a host fingerprint under
detailed_results.ssl.data.host_contextwith fieldscert_fingerprint_sha256,favicon_hash,ip,vendor,queried_vendor,asn_org,open_ports, andservices. Human-readable lines are added todetailed_results.ssl.data.host_context_evidence. - The certificate fingerprint and favicon hash are recorded with no setup. Open
ports, services, and the ASN organization are populated only when the
workspace has configured a Censys or Shodan key; without a key
queried_vendorisfalseand no vendor lookup is made. - The vendor lookup is best-effort. If the vendor is slow or unreachable the scan still completes and the vendor fields stay empty.
Brand-impersonation (Certificate Transparency siblings) notes:
- The names found on a host's TLS certificates (via Certificate Transparency) are
compared against the trusted-brand catalogue. A name that closely imitates a
brand without being that brand raises
brand:ct_sibling_impersonation_<brand>. Matches are persisted underdetailed_results.brand.data.ct_sibling_impersonation(a list of{sibling, brand, distance}). - This recovers brand-impersonation detections where the scanned apex domain itself does not resemble the brand but a sibling name on the same certificate does.
Evidence and scoring transparency notes:
-
The plain-language findings behind the enrichment flags (Certificate Transparency, public web history, host context, hosting provider) are consolidated into
detailed_results.infrastructure.data.enrichment_evidence, a list shown in the scan view as "What we found". -
Every flag contributes to the score through its published weight, and the
score_breakdownfield reconciles the total by category (the scan view shows this as "Why this score?"). The new enrichment signals appear there like any other signal, so the score and its explanation always stay in sync. -
score_breakdownreconciles to the score actually served, which is not always the sum of the categories:Field Meaning categoriesPoints per category raw_totalSum of the categories, before any reduction applied_scoreThe score actually served — equals scoreandphishing_scorecappedtruefor any reduction, not only the 100 ceilingadjustmentsOrdered reductions, each with reason,detail,from,toreconcilesfalseif points were removed without a recorded reasonCurrent
reasonvalues aretrusted_brand,established_domain, andhygiene_only; see when points are removed again. Integrations that want the unreduced picture should readraw_totalandadjustmentsand apply their own policy, rather than treatingapplied_scoreas a measurement of the page.reconciles: falsemeans the served score cannot be explained by the parts we published — treat it as a defect on our side and report it.
Reused-infrastructure notes:
- Dralvia records the TLS certificate and favicon fingerprints of scanned hosts
and, on a new scan, checks whether any previously-flagged host shared the same
fingerprint. A match raises
infrastructure:reused_known_bad_fingerprintand the matching detail is persisted underdetailed_results.infrastructure.data.reused_fingerprint_match(fingerprint_type,fingerprint_value,distinct_sources). - This is a strong signal because shared infrastructure with known-bad hosts often outlives a single throwaway domain. The match is learned only from your own scan history, and the host under test is excluded so it cannot reinforce its own past verdict.
Content lure notes:
- Credential-style document-share phishing can now raise
content:document_share_lure. - The signal requires both document-share wording such as
shared document,secure share,view document, ormailbox documentand credential-oriented behavior such as password forms, fake-auth flow indicators, external credential post targets, or redirect-driven auth prompts. - Credential-oriented behavior no longer has to be on the same page. A page published on a shared multi-tenant platform whose call to action links to an unrelated registrable domain also satisfies the requirement, because that is the same lure with the credential form moved one hop away.
- Matched phrases are exposed as
detailed_results.content.data.document_share_lure_phrases. - A page on a shared multi-tenant platform (for example
*.freshdesk.com,*.zendesk.com,*.github.io,*.notion.site) whose action link — "View document", "Sign in", "Verify" — points at an unrelated registrable domain can raisecontent:offsite_credential_cta. The matched links are exposed asdetailed_results.content.data.offsite_credential_ctas, each withtext,url,hostandregistrable_domain, and the platform is reported asdetailed_results.content.data.offsite_cta_page_surface. The signal does not apply to a site on its own domain, and the platform's own hosts are not treated as tenants of themselves. - A site that blocks the scan and returns a short custom holding page while
writing browser storage, driving a redirect, or prompting for a permission can
raise
content:analysis_evasion_gate. It requires that no recognized vendor challenge marker (Cloudflare, Akamai, Imperva and similar) was present and that the response carried no real page content;detailed_results.content.datareportsanalysis_evasion_gatewith thestatus, thevisible_textthat was served, and thebehaviorsobserved, plusscanner_block_vendor_challengeandvisible_text_length. A recognized vendor challenge continues to raise onlycontent:scanner_blocked. - Credential-style parcel or shipping phishing can now raise
content:parcel_shipping_lure. - The signal requires both parcel / package / delivery wording such as
track package,parcel tracking,delivery update,missed delivery, ordpdand stronger credential-oriented behavior such as an external credential post target, fake-auth flow, active credential-capture posture, or a redirect-driven auth prompt. - Matched phrases are exposed as
detailed_results.content.data.parcel_shipping_lure_phrases. - Credential-style streaming or subscription phishing can now raise
content:streaming_clone_lure. - The signal requires streaming/subscription wording such as
watch now,continue watching,subscription expired,account on hold, orupdate paymentplus credential-funnel behavior such as off-host credential posts, active credential-capture posture, or a mismatched throwaway host branding itself as a streaming service. - Matched phrases and brand hits are exposed as
detailed_results.content.data.streaming_clone_lure_phrasesanddetailed_results.content.data.streaming_clone_brand_hits. - Credential-style marketplace-brand phishing can now raise
content:marketplace_brand_lure. - The signal requires marketplace/order/account/payment wording plus credential-funnel behavior, and supports conservative host-mismatch and path-stuffing handling for throwaway Amazon-style shopping subjects.
- Matched phrases, brand hits, and path hits are exposed as
detailed_results.content.data.marketplace_brand_lure_phrases,detailed_results.content.data.marketplace_brand_hits, anddetailed_results.content.data.marketplace_brand_path_hits.
Domain and TLS age notes:
- WHOIS results include
age_dayswhen registration date is available. - Domains registered within 60 days raise
whois:very_recent_registration; domains registered within 7 days also raisewhois:ultra_recent_registration. - TLS results include normalized issuer fields (
issuer_common_name,issuer_organization),subject_common_name, andcert_age_dayswhen the certificate exposesnotBefore. - Certificates issued within 7 days raise
ssl:very_recent_certificate. This is intentionally a low-weight corroborating signal, not a phishing verdict by itself.
Infrastructure notes:
- Scans can include
detailed_results.infrastructurewith normalizedasn,hosting_provider,hosting_tenancy,shared_hosting_provider,shared_hosting_suffix,free_hosting_provider, host, IP, domain-age, and TLS-age context. - The admin scan result UI shows the same data in Infrastructure
Signals, and the links and websites EvidencePack explanation includes a compact
summary.infrastructureblock plus an infrastructure decision driver when those signals contribute to the verdict. - Multi-workspace free-hosting surfaces can raise
infrastructure:free_hosting_surface. When paired with lure signals such as login forms, brand abuse, OAuth/OpenID evidence, or redirect abuse, the scanner also raisesinfrastructure:free_hosting_lure. - When curated free-hosting also coincides with trusted-brand lure
signals, the scanner raises
infrastructure:brand_free_hosting_lureto make that combined explanation explicit. hosting_tenancy = "shared_multi_tenant"means the target sits on a shared workspace platform and that hosting should be treated as corroborative context.hosting_tenancy = "dedicated_or_unknown"means the scanner did not detect a known shared workspace surface.- Bare IP targets raise
infrastructure:ip_literal_surface. - Fresh domain plus fresh TLS certificate plus lure evidence raises
infrastructure:fresh_domain_certificate_lure.
Campaign-tagging notes:
- Fresh scans no longer receive new legacy heuristic campaign flags (
campaign:active_cluster,campaign:related_cluster). - Historical rows can still carry those legacy tags; they remain queryable and are marked as legacy in the campaign-tagging decoration metadata.
- When autonomous campaign tagging is enabled for the request, autonomous tags (
campaign:autonomous.related,campaign:autonomous.active) are merged intoflagsand contribute to score/explanations. When it is off, thecampaign_taggingblock still records the decision path in shadow mode.
POST /scan_async
Queues a job and returns immediately:
{
"domain": "example.com",
"force_scan": true
}
Response:
{ "scan_id": 1234, "status": "queued" }
Poll GET /scan/jobs/1234 (or GET /scan_status/1234) until the job transitions to done, failed, or dead_letter.
Async worker notes:
- Transient worker faults are retried automatically with bounded backoff before a job is marked terminal.
dead_lettermeans the worker exhausted its retry budget and no further automatic attempts will be made.- Repeated stale-job recovery now also counts against a bounded stale
recovery budget, so a stranded job will eventually move to
dead_letterinstead of being re-dispatched forever. - Queue workers now hard-reset their database session between attempts so one aborted transaction cannot poison the next retry for the same scan.
POST /scan/bulk
Submit up to BULK_SCAN_LIMIT domains per call (defaults to 50). Duplicates inside the same payload are ignored and reported in the summary.
Request:
{
"domains": ["foo.example", "bar.example", "foo.example"],
"force_scan": false
}
Response:
{
"jobs": [
{ "domain": "foo.example", "scan_id": 101, "status": "queued", "queued": true },
{ "domain": "bar.example", "scan_id": 102, "status": "queued", "queued": true }
],
"errors": [
{ "input": "foo.example", "error": "duplicate_in_request" }
],
"summary": {
"accepted": 2,
"rejected": 1,
"duplicates": 1,
"limit": 50
}
}
Every job returned by /scan/bulk can be tracked via /scan/jobs/<id>.
GET /scan/jobs/<id>
Returns a single job:
{
"scan_id": 1234,
"status": "done",
"result": {
"domain": "example.com",
"score": 21,
"risk_level": "medium",
"flags": ["ssl:missing_hsts"]
}
}
If the job failed you receive { "error": "...reason..." } instead of result.
GET /scan/jobs
Lists recent jobs for the async queue and /scan/bulk. Query parameters:
limit(default 25, max 200)status(comma-separated:queued,running,done,failed,dead_letter)domain(filters by ASCII domain)force_scan(true/false)
Response:
{
"jobs": [
{
"scan_id": 1234,
"domain": "example.com",
"status": "done",
"force_scan": false,
"queued_at": "2025-11-21T08:15:27Z",
"run_duration_ms": 8320,
"result": { "score": 21, "risk_level": "medium", "flags": ["ssl:missing_hsts"] }
}
],
"meta": {
"limit": 25,
"counts": { "queued": 3, "running": 1, "done": 42, "failed": 2, "dead_letter": 0 }
}
}
Use this endpoint to power dashboards (the admin HUD consumes it to show queue health).
Retention and history notes
- Full nested links and websites scan payloads are retained only for a short evidence window.
- Older duplicate scan rows may be compacted to a lean history payload when a newer scan for the same scoped site already exists.
- History endpoints and scan listings still preserve score, risk, flags, host/target, and compact check summaries after compaction.
- Large inline screenshots, replay-heavy nested blocks, and duplicate debug payloads are not guaranteed to remain available indefinitely.
Visual preview degradation behavior
- Dralvia prefers isolated browser capture for visual impersonation evidence.
- If that isolated capture degrades before it can produce a safe screenshot, the API now still returns a safe remote preview mirror in
detailed_results.visual.data.screenshot_urlwhen policy allows it. - The scan payload continues to preserve the degraded browser state through
mode,degraded,degraded_reason, replay steps, and policy/runtime metadata. For example, a browser failure can still reportfinal_urlasabout:blankwhile the preview shown to analysts is the safe fallback mirror of the requested target.
EvidencePacks & downloads
- Every scan embeds the EvidencePack inside
tabs.detection.evidencepack. GET /evidencepacks/<id>returns the canonical JSON if you need to re-download it later.GET /reports/<id>(from the pentest module) pulls full PDF exports.
CLI helper
Run scans with the helper script (available from the Dralvia SDK examples):
python scan_cli.py example.com --api-key "$DRALVIA_API_KEY" --api-url "https://dralvia.tech/api/tenant"
python scan_cli.py --file domains.txt --async
python scan_cli.py --status 1234
It wraps /v1/scan, /scan_async, /scan/bulk, and /scan/jobs/<id> with basic error handling. Pass --force to bypass caching when you need a fresh scan (counts against your daily force-scan quota).
Error handling
400 BAD REQUEST: Invalid domain, empty payload, or unsupported parameter.402 PAYMENT REQUIRED: Force-scan is disabled for the active workspace plan.429 TOO MANY REQUESTS: Per-route rate limit reached.500 INTERNAL SERVER ERROR: Unexpected downstream failure (DNS/SSL/renderer). Failures are logged viarecord_scan_failurefor observability.
Combine /scan results with /ticketbridge/tickets to escalate suspicious verdicts automatically.