---
title: "Submit domain for analysis"
method: POST
path: "/analyze"
tags: ["analysis"]
---

# Submit domain for analysis

`POST /analyze`

## Request

- Base URL: `https://api.whoisgeni.us` (the document declares no server; this is the origin it was published from)
- URL: `https://api.whoisgeni.us/analyze`
- Auth: none declared

Submit a single domain for analysis.

Creates an analysis job and returns 202 with a job_id for polling.
Deduplicates against active jobs unless force_refresh is True.
An audit log entry is written in the background.

Set ``wait=true`` for a one-call flow: the request is held briefly and
returns the result inline (HTTP 200) when the analysis resolves, or HTTP 202
with the job_id to poll if it is still running when the wait budget elapses.

Set ``depth="quick"`` for the honest fast path: the fast collector subset
(dns/whois/http_probe/asn/rdns) runs inline under a hard deadline and the
terminal result comes back as HTTP 200 (same body shape as GET /jobs/{id},
plus job_id). Quick never runs vision/LLM/reverse-WHOIS/metered third-party
enrichment -- it is the fast honest answer; deep is the full one. Quick
cannot be combined with wait=true or force_refresh (400), costs the same
1 credit as deep, and refunds on a failed outcome.

Quota enforcement: each query increments a monthly Redis counter.
Free tier users are hard-blocked at their monthly quota
(``settings.quota_free``). Pro/Team users exceeding quota are billed
overage via Stripe metered billing. Falls back to credit deduction if
overage billing unavailable.

## Query parameters

- `wait` boolean: When true, hold the response for up to ~20 seconds and return the result inline once ready (same body as GET /jobs/{job_id}) with HTTP 200. If the analysis is still running when the wait budget is spent, return HTTP 202 with the job_id so you can poll GET /jobs/{job_id}. Default false returns 202 immediately.

## Headers

- `X-API-Key` string, nullable
- `X-LLM-API-Key` string, nullable
- `X-LLM-Provider` string, nullable

## Request body

- SubmitRequest: Request body for submitting a single domain for analysis. Attributes: domain: Fully qualified domain name to analyze. force_refresh: Skip dedup and force a fresh analysis run.
  - `domain` string, required: Domain name to analyze (ASCII or internationalized).
  - `display_domain` string: Original IDN form of the domain (populated internally).
  - `depth` 'deep' | 'quick': Analysis depth. 'deep' (default) queues the full async pipeline (all collectors, vision, LLM enrichment, metered third-party enrichment) and returns 202 + job_id for polling. 'quick' runs the fast collector subset (dns/whois/http_probe/asn/rdns) inline under a hard deadline and returns 200 with the terminal result plus job_id; it never runs vision/LLM/reverse-WHOIS/metered enrichment. 'quick' cannot be combined with force_refresh or wait=true (400).
  - `force_refresh` boolean
  - `callback_url` string, nullable: Optional URL to receive a webhook POST when analysis completes
  - `output_schema` string, uuid, nullable: Template ID for response projection (mutually exclusive with output_fields)
  - `output_fields` OutputFieldsSpec: Inline field projection spec for response customization. Specifies a mode (include or exclude) and a list of dot-notation field paths to apply as a filter on the scoring_result dict. Attributes: mode: ``"include"`` to whitelist fields, ``"exclude"`` to blacklist. fields: List of dot-notation field paths (min 1 required).
    - `mode` 'include' | 'exclude', required
    - `fields` string[], required

## Response `200`

wait=true or depth=quick: analysis resolved inline within the request (depth=quick always answers 200 with the terminal result; wait=true answers 200 only when the job resolved inside the wait budget)

- ApiResponseJobStatusResponse
  - `data` JobStatusResponse: Response for polling a job's current status. When the job is completed, ``scoring_result`` contains the full ScoringResult dict from the scoring engine. When LLM enrichment is enabled and the ambiguity gate triggers, the scoring_result dict includes additional fields: enriched_conclusion, entity_resolution_notes, arbitration_reasoning. These fields are null/absent when LLM was not invoked. Attributes: job_id: Unique job identifier. status: Current job status (pending, processing, completed, failed). domain: Domain being analyzed. collectors_done: Number of collectors that have finished. collectors_total: Total number of collectors in the pipeline. analysis_run_id: Database analysis run ID. started_at: ISO 8601 timestamp when the job started processing. completed_at: ISO 8601 timestamp when the job finished. scoring_result: Full scoring result dict when completed. May include enriched_conclusion, entity_resolution_notes, and arbitration_reasoning when LLM enrichment was invoked.
    - `job_id` string, required
    - `status` 'pending' | 'processing' | 'completed' | 'failed' | 'partial', required
    - `domain` string, nullable
    - `phase` string, nullable
    - `collectors_done` integer, nullable
    - `collectors_total` integer, nullable
    - `analysis_run_id` string, nullable
    - `started_at` string, nullable
    - `completed_at` string, nullable
    - `scoring_result` object, nullable
  - `error` ErrorDetail: Structured error information for API error responses. Attributes: code: Machine-readable error code (e.g. "JOB_NOT_FOUND"). message: Human-readable error description. details: Optional additional context for debugging.
    - `code` string, required
    - `message` string, required
    - `details` object, nullable
  - `meta` object, nullable

## Other responses

- `202`: Analysis job accepted
- `400`: Invalid depth combination (quick + wait/force_refresh)
- `401`: Unauthorized — missing or invalid API key/JWT.
- `422`: Invalid domain format

---

[API](https://skmtc.dev/whoisgeni/apis/whoisgenius.md) · [All operations](https://skmtc.dev/whoisgeni/apis/whoisgenius/llms.txt) · [OpenAPI document](https://skmtc.dev/whoisgeni/apis/whoisgenius/revisions/707f1c34f6b3?raw)
