Overview
The external developer API is intentionally separate from the paid-user account API. It gives approved developers API-key protected access to domain authenticity verdicts and security score advisory statements.
External developer keys do not work on the paid-user /api/v1 account API and do not return tracked-domain history, signed-in reports, scan ownership IDs or paid evidence packs.
Two kinds of check
MyDomainRisk runs two different depths of check, and this API gives you the shallower one. The difference is not a limit on how many you may run — it is a difference in how much analysis is done and how much detail comes back.
| Quick check (this API) | A score or a verdict, and the titles of what was found. The same check, the same depth and the same detail a visitor gets on our website — nothing more and nothing less. |
|---|---|
| Full scan (signed in) | A deeper analysis with the detailed findings, the recommended actions, the report and the history. Available in the app, and over the account API for the account's own domains. |
What the quick check does not do
- It does not explain a finding. You get a title and a severity, not the analysis behind it, the remediation steps, or which sources contributed.
- Its score is a floor. It reads a lighter exposure index, so a full scan can return the same score or a higher one, never a lower one. When that applies,
coverage_notesays so in the response — do not present the number without it. - It may report a provisional authenticity verdict. When
enrichment_statusispending, two slower signals are still running and the verdict may still change. Nothing is pushed to you when they finish. Request the same domain again shortly and you will be served the enriched answer, which reportscompleted. Anot_applicableverdict needed no slow signal and will not change however often you ask. This is exactly how the website behaves — a visitor is not notified either. - It may not have read the homepage.
homepage.reviewedtells you whether it did, andhomepage.blocked_bynames what stopped it. “Nothing found” and “the page could not be fetched” are different answers and must not be shown the same way. - Results are cached for up to 20 minutes and there is no force-refresh. A domain that changed in the last few minutes may not show it yet.
Because it is the same check a visitor can run, it carries the same allowance: five per hour. It does not consume the account's daily check allowance, which belongs to full scans.
These are outcome-level answers for vetting and triage. They are not a substitute for a full scan of a domain you are responsible for.
Machine-readable contract
The full API contract is published as an OpenAPI 3.1 document. Validate responses in your CI or generate typed clients from it rather than transcribing the tables below.
curl -sS https://mydomainrisk.com/openapi.jsonThe same document is reachable from the API host, which redirects to the address above:
curl -sSL https://api.mydomainrisk.com/api/developer/v1/openapi.jsonIt carries the exact permitted values for every field with a fixed set of answers — verdicts, grades, score buckets and finding severities — so nothing on this page needs to be inferred from example responses.
Authentication
Developer API keys are available on every plan, including Free. A key carries its owner's account and can do what that account can do — no more. Create a Developer API - authenticity and score advisory key from the account area and send it as a Bearer token.
Authorization: Bearer mdr_live_<id>_<secret>Use developer keys only from server-side code. Do not put them in browser JavaScript, mobile apps, public repositories or client-side logs.
Validate a key before building against it:
GET https://api.mydomainrisk.com/api/developer/v1/me
Authorization: Bearer mdr_live_<id>_<secret>
{
"api": "developer",
"version": "v1",
"scope": "developer_peek",
"tier": "pro",
"capabilities": [
{ "operation_id": "getDeveloperCapabilities", "method": "GET", "path": "/me" },
{ "operation_id": "checkDomainSecurity", "method": "GET", "path": "/peek/security" },
{ "operation_id": "checkDomainAuthenticity", "method": "GET", "path": "/peek/authenticity" }
],
"limits": {
"requests_per_minute_per_key": 120,
"requests_per_minute_per_ip": 60,
"requests_per_hour_per_subnet": 150,
"cache_max_age_seconds": 1200
},
"entitlements": {
"security_score_advisory": true,
"authenticity_verdict": true,
"account_data": false,
"force_refresh": false,
"consumes_account_daily_allowance": false
}
}The capability response deliberately contains no account email or customer data.
If you need API access and do not have an account, or want to discuss a partner arrangement, contact support@mydomainrisk.com.
Security score advisory
GET https://api.mydomainrisk.com/api/developer/v1/peek/security?domain=example.com
Authorization: Bearer mdr_live_<id>_<secret>Example response:
{
"domain": "example.com",
"checked_at": "2026-08-17T12:00:00Z",
"score": 82,
"grade": "B",
"score_summary": {
"bucket": "good",
"label": "Good"
},
"findings": [
{
"title": "DMARC policy could be stronger",
"severity": "medium"
}
],
"total_findings": 1,
"cache": { "hit": false, "max_age_seconds": 1200 },
"scan_time_ms": 842
}Authenticity verdict
GET https://api.mydomainrisk.com/api/developer/v1/peek/authenticity?domain=example.com
Authorization: Bearer mdr_live_<id>_<secret>You can include optional brand context when the expected organisation name is known:
GET https://api.mydomainrisk.com/api/developer/v1/peek/authenticity?domain=example.com&expected_brand=Example%20Ltd
Authorization: Bearer mdr_live_<id>_<secret>Example response:
{
"domain": "example.com",
"checked_at": "2026-08-17T12:00:00Z",
"verdict": "genuine",
"findings": [],
"total_findings": 0,
"factual": {
"resolves": true,
"registrar": "Example Registrar Ltd",
"age_days": 10950,
"has_tls": true
},
"cache": { "hit": false, "max_age_seconds": 1200 },
"scan_time_ms": 936
}Authenticity verdict values:
| genuine | No material authenticity concern visible. Map to pass / green. |
|---|---|
| probablyLegit | Likely legitimate with minor caveats. Map to pass / green. |
| uncertain | Cannot confidently classify. A human should review. Map to review / amber. |
| suspicious | Concerning signals present. Treat with caution. Map to review or fail / amber-red. |
| malicious | Strong malicious or impersonation signals. Do not treat as safe. Map to fail / red. |
For simple traffic-light integrations, map genuine and probablyLegit to pass, uncertain and suspicious to review, and malicious to fail.
Fields
Security score advisory fields
| domain | The normalised domain submitted for checking. |
|---|---|
| checked_at | ISO 8601 check time. May be null for a cached result created before this field was introduced. |
| score | Security score advisory on a 0–100 scale. Higher is better — 0 is the worst result, 100 the best. |
| grade | Display grade derived from the score. One of: A, B, C, D, F. Presentation text — branch on score_summary.bucket instead. |
| score_summary | Object with bucket and label. bucket is one of: strong, goodWithGaps, needsImprovement, atRisk, critical — listed here worst to best in reverse. label is the plain-English wording for the same bucket, and is display text only. |
| findings | Array of current public findings. Each item contains title and severity only. severity is one of: critical, high, medium, low, info. |
| total_findings | Count of current public findings. |
| cache | Object containing hit and max_age_seconds. |
| scan_time_ms | Runtime for the request in milliseconds. |
Branch on score_summary.bucket rather than comparing score against your own thresholds. The bucket is part of the contract; a threshold you pick yourself is not, and will drift from ours without any error to tell you.
Authenticity verdict fields
| domain | The normalised domain or URL host submitted for checking. |
|---|---|
| checked_at | ISO 8601 check time. May be null for a cached result created before this field was introduced. |
| verdict | One of: genuine, probablyLegit, uncertain, suspicious, malicious — listed best to worst. No other value is ever returned. probablyLegit is a POSITIVE verdict; treating it as a failure or an unknown is the most common integration mistake. |
| findings | Array of current public findings. Each item contains title and severity only. |
| total_findings | Count of current public findings. |
| factual | Strict factual context: resolves, age_days, has_tls and registrar; unavailable age or registrar values are null. |
| cache | Object containing hit and max_age_seconds. |
| scan_time_ms | Runtime for the request in milliseconds. |
Results are cached for up to 20 minutes. There is no force-refresh option on the developer API; present results as a recent advisory rather than a permanent decision for domains that may have just changed.
Limits and errors
Developer API requests are limited to 120 requests per minute per key, 60 per minute per IP and 150 per hour per subnet.
The allowance that actually binds a check is five per hour, per IP — the same allowance a visitor gets running the same check on our website. The per-minute figures above are burst guards sitting on top of it. /me reports it as peeks_per_hour_per_ip, and reading /me does not count against it.
These checks do not consume the account's daily check allowance. That allowance belongs to full scans, which are deeper and are a different thing entirely.
| 400/422 | Invalid request body, query string or malformed domain. |
|---|---|
| 401 | Missing, malformed, unknown or revoked API key. |
| 403 | Wrong key type, or account not eligible for API access. |
| 429 | Rate limit or platform guard reached. |
| 503 | Temporary queue, cache or quota-store issue. Retry later. |
| 504 | The upstream check did not finish in time. Retry later. |
Every failure returns the same JSON shape, on every path and at every status. Branch on error.code, not on the message text:
{ "error": { "code": "rate_limited", "message": "...", "retry_after_seconds": 30 } }| invalid_api_key | 401. Missing, malformed, unknown or revoked key. |
|---|---|
| forbidden | 403. Wrong key type, or the account is not eligible. |
| invalid_domain | 422. The domain or URL host could not be parsed. |
| rate_limited | 429. An allowance was reached — the hourly check allowance or a burst guard. Honour Retry-After, or error.retry_after_seconds. |
| temporarily_unavailable | 503. A transient queue, cache or quota-store issue. |
| scan_timeout | 504. The upstream check did not finish in time. |
| request_failed | Any status. The fallback when no more specific code applies. |
Treat this list as the codes worth handling individually, not as a closed set: recognise these and fall back on the HTTP status for anything else, so a new code never breaks your integration.
A 429 carries a Retry-After header as well as the field above. Back off on it rather than on a fixed interval.
Acceptable use
The API is for legitimate business, security, fraud-prevention, customer-protection and compliance workflows. MyDomainRisk performs non-intrusive external analysis only.
Allowed
- Onboarding and signup checks.
- Customer, partner and domain review.
- Internal fraud, risk and security workflows.
Not allowed
- Exploitation, attack simulation or credential testing.
- Bypassing quotas, cooldowns or access controls.
- Using paid-user account keys as external developer keys.
Privacy and data handling
The developer API receives the domain and request metadata needed to provide the service, authenticate the caller, enforce limits and protect the platform. MyDomainRisk does not sell API submission data.
Developer API results may be cached and counted in aggregate telemetry according to the same public domain-check operating model and the MyDomainRisk privacy policy. For privacy questions, contact support@mydomainrisk.com.
Changelog
2026-06-25
Public developer docs updated with key validation, example JSON responses, verdict values, rate limits, and cache guidance for the separate external developer API.