Skip to main content

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). Prefer Authorization: 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_scope when 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_mismatch and oauth:encoded_auth_payload when 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_flow when the URL uses a broken callback or inconsistent response-mode pattern, and oauth:generic_auth_handoff when 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 as provider_hints, provider_mismatch, redirect_host, decoded_targets, authorize_flow_anomalies, generic_auth_terms, and recommended_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.ct with fields entry_count, newest_age_days, oldest_age_days, issuers, sibling_domains, has_wildcard, and new_footprint. Human-readable lines are added to detailed_results.ssl.data.ct_evidence.
  • New flag ct:new_certificate_footprint is 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_certificate records 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 ct fields 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_history with fields first_seen_days_ago, capture_days, no_public_history, and thin_public_footprint. Human-readable lines are added to detailed_results.whois.data.domain_history_evidence.
  • New flag history:thin_public_footprint is 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_seen records 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_history fields 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_classification with fields hosting_provider, asn, abuse_prone, and matched_marker. Human-readable lines are added to detailed_results.geolocation.data.hosting_evidence.
  • New flag infrastructure:hosting_provider_identified records the provider as evidence and carries no weight on its own.
  • New flag infrastructure:abuse_prone_hosting is 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_context with fields cert_fingerprint_sha256, favicon_hash, ip, vendor, queried_vendor, asn_org, open_ports, and services. Human-readable lines are added to detailed_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_vendor is false and 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 under detailed_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_breakdown field 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_breakdown reconciles to the score actually served, which is not always the sum of the categories:

    FieldMeaning
    categoriesPoints per category
    raw_totalSum of the categories, before any reduction
    applied_scoreThe score actually served — equals score and phishing_score
    cappedtrue for any reduction, not only the 100 ceiling
    adjustmentsOrdered reductions, each with reason, detail, from, to
    reconcilesfalse if points were removed without a recorded reason

    Current reason values are trusted_brand, established_domain, and hygiene_only; see when points are removed again. Integrations that want the unreduced picture should read raw_total and adjustments and apply their own policy, rather than treating applied_score as a measurement of the page. reconciles: false means 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_fingerprint and the matching detail is persisted under detailed_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, or mailbox document and 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 raise content:offsite_credential_cta. The matched links are exposed as detailed_results.content.data.offsite_credential_ctas, each with text, url, host and registrable_domain, and the platform is reported as detailed_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.data reports analysis_evasion_gate with the status, the visible_text that was served, and the behaviors observed, plus scanner_block_vendor_challenge and visible_text_length. A recognized vendor challenge continues to raise only content: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, or dpd and 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, or update payment plus 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_phrases and detailed_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, and detailed_results.content.data.marketplace_brand_path_hits.

Domain and TLS age notes:

  • WHOIS results include age_days when registration date is available.
  • Domains registered within 60 days raise whois:very_recent_registration; domains registered within 7 days also raise whois:ultra_recent_registration.
  • TLS results include normalized issuer fields (issuer_common_name, issuer_organization), subject_common_name, and cert_age_days when the certificate exposes notBefore.
  • 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.infrastructure with normalized asn, 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.infrastructure block 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 raises infrastructure:free_hosting_lure.
  • When curated free-hosting also coincides with trusted-brand lure signals, the scanner raises infrastructure:brand_free_hosting_lure to 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 into flags and contribute to score/explanations. When it is off, the campaign_tagging block 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_letter means 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_letter instead 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_url when 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 report final_url as about:blank while 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 via record_scan_failure for observability.

Combine /scan results with /ticketbridge/tickets to escalate suspicious verdicts automatically.