Skip to content

List a brand's domains

GET
/core/api/brands/{id}/domains/
curl --request GET \
--url 'https://app.nebty.de/core/api/brands/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/domains/?group_by=apex&http_status_filter=all&risk_score_filter=all&sort_direction=asc&sort_field=domain&triage_filter=all&verdict_filter=all&visual_match_filter=all' \
--header 'Authorization: <Authorization>'

Returns the domains associated with a brand, with filtering and sorting. risk_score and verdict describe the pairing of this brand with the domain, not the domain on its own. The default order is highest risk first.

Paginated: page and page_size (default 50, maximum 100). See the pagination note in the API description.

Use compact=true for a short row per domain (id, name, source, a compact latest_scan and open_takedown). Without it every row embeds scan history and is much larger; fetch a single domain when you need the evidence.

Filters: domain_filter (case-insensitive substring of the name), http_status_filter (online, offline, all), risk_score_filter (low, medium, high, all), verdict_filter (red, amber, green, unknown, all) and triage_filter. visual_match_filter is deprecated; use verdict_filter. Sort with sort_field and sort_direction. group_by=apex returns one row per registrable domain, with its subdomains collapsed, instead of one row per domain. An unsupported group_by or triage_filter value is rejected with 400.

id
required
string format: uuid

UUID of the brand, as returned in the id field of the brand list.

compact
boolean

Return compact list summaries without embedded history. Use domain detail for evidence.

domain_filter
string

Filter domains by name (case-insensitive substring match)

group_by
string
Allowed values: apex none

Collapse the list to one row per registrable apex (eTLD+1) so a third-party domain and all its subdomains take one decision. Applied after every filter. With ‘apex’ the response items are BrandDomainApexGroup objects, not Domain objects, and the paginated count is the number of apexes — summing domain_count over all pages reproduces the ungrouped total for the same filter. Default ‘none’.

http_status_filter
string
Allowed values: all offline online

Filter by HTTP status: ‘online’, ‘offline’, or ‘all’

page
integer

Page number within the paginated result set.

page_size
integer

Rows per page. Defaults to 50; clamped to 100 above that.

risk_score_filter
string
Allowed values: all high low medium

Filter by risk score level: ‘low’, ‘medium’, ‘high’, or ‘all’

sort_direction
string
Allowed values: asc desc

Sort direction

sort_field
string
Allowed values: domain http_status review_priority risk_score visual_match

Field to sort by. ‘review_priority’ is the layer-2 pre-ranking of what a human still has to review; unranked pairs sort below every ranked one, with risk breaking ties. Under group_by=apex only ‘domain’ (apex name) and ‘risk_score’ (the group’s worst host) apply.

triage_filter
string
Allowed values: all escalated filed queue

Filter by the common-word triage outcome, for brands where it is enabled. ‘queue’ hides the pairs the machine filed as an unrelated entity or a name collision — the review worklist. ‘filed’ shows only those, for auditing what was hidden. ‘escalated’ shows only pairs carrying a brand-affinity token. Pairs a human has judged are never hidden by this filter and never listed as machine decisions. Default ‘all’, which returns every row. Note that the brand statistics endpoints (/stats/ and ?include_stats=true) always describe the ‘queue’ population, so verdict_stats matches the row count under ‘queue’ rather than under ‘all’.

verdict_filter
string
Allowed values: all amber green red unknown

Filter by the merged customer-facing verdict for this brand’s pair with the domain. Accepts the same values as BrandStatsSerializer.verdict_stats’ keys, plus ‘all’ — a stat and its filtered list can never disagree.

visual_match_filter
deprecated
string
Allowed values: all no unknown yes

DEPRECATED — use verdict_filter. Filter by visual match: ‘yes’, ‘no’, ‘unknown’, or ‘all’. Kept so the frontend does not break on deploy; the visual scan is now one of two inputs to the merged verdict, not the customer-facing answer.

Media typeapplication/json
object
count
required
integer
next
string format: uri
nullable
previous
string format: uri
nullable
results
required
Array
Any of:

Serializer for Domain model.

object
id
required
string format: uuid
name
required
string
<= 255 characters
source

How this domain was discovered/added to the system

  • VARIATION - Generated from Variation
  • CERTSTREAM - CertStream API
  • SUBDOMAIN_SCAN - Subdomain Scan
  • MANUAL - Manual Entry
  • AD_LIBRARY - Meta Ad Library
string
Allowed values: VARIATION CERTSTREAM SUBDOMAIN_SCAN MANUAL AD_LIBRARY
parent
string format: uuid
nullable
associated_brands
required
Array<string>
latest_scan
required
object
id
required
string format: uuid
domain
required
string format: uuid
mx_records
string
nullable
a_records
string
nullable
cname_records
string
nullable
visual_match
required
boolean
nullable
verdict
required

The single customer-facing verdict, resolved for the requesting brand.

Unlike risk_score/visual_match there is no shared DomainScan column to fall back to: the merged verdict has only ever lived on the pair (BrandDomainAssociation.refresh_merged_verdict), so a caller with no brand in context — or a historical scan row, which carries no pair-specific verdict of its own — gets “unknown” rather than another brand’s answer.

string
verdict_reasoning
required
string
nullable
requires_human_review
required
boolean
visual_scan_at
required

When this brand last had the domain visually scanned.

The shared timestamp is whichever brand scanned the domain most recently, so on a shared hostname it is somebody else’s. NULL on the pair means this brand has not scanned it yet, which is exactly what the cooldown treats it as.

string format: date-time
nullable
screenshot_url

URL to the screenshot of the domain stored in Hetzner S3-compatible Object Storage

string format: uri
nullable <= 2000 characters
https_head_request_response_code
integer
nullable
certificate
string
nullable
scans_executed
integer
is_alerting_event
required

Whether this brand still has an unsent alert for the domain.

The shared flag means “somebody has one”, so after this brand’s alert was sent it would keep reporting true while another brand’s is pending.

boolean
nullable
completeness
required

A completeness of 0 means that the domain has no records set. The higher this number, the more likely the domain is actually used.

integer
risk_score
required
integer
scan_history
required

The domain’s scan timeline, most recent first.

On a domain that has only ever had single-brand history, the whole history is this brand’s own and renders untouched. Once branded rows exist, the domain has had a multi-brand period: the requesting brand then sees only its own rows, and NULL-brand rows are redacted because current association count can no longer prove which brand they belonged to.

Array<object>
object
key
additional properties
user_feedback_type
required
One of:
  • false_positive - False Positive
  • comment - Comment
string
Allowed values: false_positive comment
user_feedback_comment
required

Free text comment from the user.

string
user_feedback_by
required

User who submitted the feedback.

string format: uuid
nullable
user_feedback_at
required

When the feedback was submitted.

string format: date-time
nullable
created_at
required
string format: date-time
updated_at
required
string format: date-time
scans
required
Array<object>

Serializer for DomainScan model.

risk_score and visual_match are answers about a brand↔domain pair, not about the domain (#954). When the caller puts a brand in the serializer context — every brand-scoped endpoint does — both are rendered from that brand’s association, so a domain two customers monitor shows each of them the verdict computed against their own brand.

Without a brand in context the shared columns are rendered unchanged, which is what the domain-global endpoints and the admin want. That column now means “the worst score across every brand monitoring this domain” rather than “the longest-monitored brand’s score” — see DomainScan.get_risk_score.

verdict/verdict_reasoning/requires_human_review are the single customer-facing verdict merged from the visual and content detectors (BrandDomainAssociation.refresh_merged_verdict) and are what the frontend should read going forward. visual_match is kept in the payload as deprecated — do not remove it — because it still governs code paths mid-migration; new consumers should read verdict instead.

object
id
required
string format: uuid
domain
required
string format: uuid
mx_records
string
nullable
a_records
string
nullable
cname_records
string
nullable
visual_match
required
boolean
nullable
verdict
required

The single customer-facing verdict, resolved for the requesting brand.

Unlike risk_score/visual_match there is no shared DomainScan column to fall back to: the merged verdict has only ever lived on the pair (BrandDomainAssociation.refresh_merged_verdict), so a caller with no brand in context — or a historical scan row, which carries no pair-specific verdict of its own — gets “unknown” rather than another brand’s answer.

string
verdict_reasoning
required
string
nullable
requires_human_review
required
boolean
visual_scan_at
required

When this brand last had the domain visually scanned.

The shared timestamp is whichever brand scanned the domain most recently, so on a shared hostname it is somebody else’s. NULL on the pair means this brand has not scanned it yet, which is exactly what the cooldown treats it as.

string format: date-time
nullable
screenshot_url

URL to the screenshot of the domain stored in Hetzner S3-compatible Object Storage

string format: uri
nullable <= 2000 characters
https_head_request_response_code
integer
nullable
certificate
string
nullable
scans_executed
integer
is_alerting_event
required

Whether this brand still has an unsent alert for the domain.

The shared flag means “somebody has one”, so after this brand’s alert was sent it would keep reporting true while another brand’s is pending.

boolean
nullable
completeness
required

A completeness of 0 means that the domain has no records set. The higher this number, the more likely the domain is actually used.

integer
risk_score
required
integer
scan_history
required

The domain’s scan timeline, most recent first.

On a domain that has only ever had single-brand history, the whole history is this brand’s own and renders untouched. Once branded rows exist, the domain has had a multi-brand period: the requesting brand then sees only its own rows, and NULL-brand rows are redacted because current association count can no longer prove which brand they belonged to.

Array<object>
object
key
additional properties
user_feedback_type
required
One of:
  • false_positive - False Positive
  • comment - Comment
string
Allowed values: false_positive comment
user_feedback_comment
required

Free text comment from the user.

string
user_feedback_by
required

User who submitted the feedback.

string format: uuid
nullable
user_feedback_at
required

When the feedback was submitted.

string format: date-time
nullable
created_at
required
string format: date-time
updated_at
required
string format: date-time
open_takedown
required
object
id
required

Takedown id; fetch the case at GET /core/api/takedowns/{id}/.

string format: uuid
status
required

Current status of the running case.

  • REQUESTED - Requested (Pending Approval)
  • OPEN - Open
  • IN_PROGRESS - In Progress
  • PENDING_RESPONSE - Pending Response
  • CLOSED - Closed
  • FAILED - Failed
  • REJECTED - Rejected
string
Allowed values: REQUESTED OPEN IN_PROGRESS PENDING_RESPONSE CLOSED FAILED REJECTED
last_closed_takedown
required
object
id
required

Takedown id; fetch the case at GET /core/api/takedowns/{id}/.

string format: uuid
status
required

How the case ended: CLOSED (worked to a conclusion, see resolution) or FAILED (abandoned, no resolution).

  • CLOSED - Closed
  • FAILED - Failed
string
Allowed values: CLOSED FAILED
resolution
required
One of:
  • DOMAIN_SUSPENDED - Domain Suspended
  • CONTENT_REMOVED - Content Removed
  • SERVER_HOLD - Server Hold
  • VOLUNTARY_TAKEDOWN - Voluntary Takedown
  • UNRESOLVED - Unresolved
  • FALSE_POSITIVE - False Positive
string
Allowed values: DOMAIN_SUSPENDED CONTENT_REMOVED SERVER_HOLD VOLUNTARY_TAKEDOWN UNRESOLVED FALSE_POSITIVE
closed_at
required

When the case finished. For CLOSED this is the case’s closed_at; FAILED and undated legacy cases use a stable result timestamp.

string format: date-time
created_at
required
string format: date-time
updated_at
required
string format: date-time
Example
{
"count": 123,
"next": "http://api.example.org/accounts/?page=4",
"previous": "http://api.example.org/accounts/?page=2",
"results": [
{
"source": "VARIATION",
"latest_scan": {
"user_feedback_type": "false_positive"
},
"scans": [
{
"user_feedback_type": "false_positive"
}
],
"open_takedown": {
"status": "REQUESTED"
},
"last_closed_takedown": {
"status": "CLOSED",
"resolution": "DOMAIN_SUSPENDED"
}
}
]
}

Invalid query parameters or request data. Field errors appear directly on the response; malformed JSON has a detail message.

Media typeapplication/json
Any of:
object
detail
required
string
Example
{
"target_website": [
"Target host is not a public address."
]
}

The API key is missing, invalid, revoked or belongs to an inactive user.

Media typeapplication/json
object
detail
required
string
Examplegenerated
{
"detail": "example"
}

The requested page or record does not exist or cannot be accessed.

Media typeapplication/json
object
detail
required
string
Examplegenerated
{
"detail": "example"
}

Too many requests or failed authentication attempts. Try again later.

Media typeapplication/json
object
detail
required
string
Examplegenerated
{
"detail": "example"
}