GET/api/v2/bimi
BIMI check API
Whether inboxes show a domain's logo: the BIMI record, the DMARC policy it needs, the SVG checked against SVG Tiny PS, the VMC or CMC with its chain, and what Gmail, Apple Mail, Yahoo and others would do.
- 20 calls a minute per IP
- Same engine as the BIMI Checker tool
{
"query": {
"domain": "cnn.com",
"selector": "default"
},
"verdict": "ready",
"record": {
"status": "pass",
"state": "found",
"name": "default._bimi.cnn.com",
"level": "author",
"text": "v=BIMI1; l=https://amplify.valimail.com/bimi/time-warner/R4Ezf5xK5ac-CNN.svg; a=https://amplify.valimail.com/bimi/time-warner/R4Ezf5xK5ac-CNN.pem",
"ttl": 1800,
"logoUrl": "https://amplify.valimail.com/bimi/time-warner/R4Ezf5xK5ac-CNN.svg",
"evidenceUrl": "https://amplify.valimail.com/bimi/time-warner/R4Ezf5xK5ac-CNN.pem",
"declined": false,
"valid": true,
"avp": "brand",
"lps": null
},
"dmarc": {
"status": "pass",
"meetsRequirement": true,
"domain": {
"domain": "cnn.com",
"foundAt": "cnn.com",
"record": "v=DMARC1; p=reject; rua=mailto:dmarc_agg@vali.email; ruf=mailto:Njk3@ruf.vali.email",
"p": "reject",
"sp": null,
"pct": null,
"testing": false,
"state": "ok"
},
"organizationalDomain": null
},
"logo": {
"status": "pass_with_warnings",
"notChecked": null,
"url": "https://amplify.valimail.com/bimi/time-warner/R4Ezf5xK5ac-CNN.svg",
"finalUrl": null,
"httpStatus": 200,
"contentType": "image/svg+xml",
"bytes": 1725,
"sha256": "215d5054ca31f6d7aea6a316edc8824e45828fc18678fce20c8c886cb4252d22",
"title": "CNN",
"svgTinyPs": true,
"square": true,
"viewBox": [
0,
0,
967.5,
967.5
]
},
"certificate": {
"status": "pass",
"notChecked": null,
"url": "https://amplify.valimail.com/bimi/time-warner/R4Ezf5xK5ac-CNN.pem",
"httpStatus": 200,
"type": "vmc",
"markType": "Registered Mark",
"wordMark": null,
"trademark": {
"office": null,
"country": "US",
"registration": "5817930"
},
"organization": "Cable News Network, Inc.",
"country": "US",
"issuer": "DigiCert Verified Mark RSA4096 SHA256 2021 CA1",
"ca": "digicert",
"serial": "0cb658999c795a3a19c7ee4434a810bd",
"validFrom": "2026-08-28T00:00:00.000Z",
"validTo": "2027-09-01T23:59:59.000Z",
"domains": [
"cnn.com"
],
"coversDomain": true,
"chainTrusted": true,
"root": "DigiCert Verified Mark Root CA",
"logoMatches": true,
"revocation": "good",
"ctLogged": true
},
"providers": [
{
"id": "gmail",
"name": "Gmail",
"shows": "checkmark",
"reasons": []
},
{
"id": "apple",
"name": "Apple Mail",
"shows": "logo",
"reasons": [
{
"code": "undocumented",
"message": "Apple publishes no rules: based on its BIMI partners' documents."
}
]
},
{
"id": "yahoo",
"name": "Yahoo",
"shows": "maybe",
"reasons": [
{
"code": "reputation",
"message": "Also needs bulk volume and a good sender reputation."
}
]
},
{
"id": "aol",
"name": "AOL",
"shows": "maybe",
"reasons": [
{
"code": "reputation",
"message": "Also needs bulk volume and a good sender reputation."
}
]
},
{
"id": "fastmail",
"name": "Fastmail",
"shows": "logo",
"reasons": []
},
{
"id": "laposte",
"name": "La Poste",
"shows": "logo",
"reasons": []
},
{
"id": "zone",
"name": "Zone",
"shows": "checkmark",
"reasons": []
},
{
"id": "zoner",
"name": "Zoner",
"shows": "logo",
"reasons": []
},
{
"id": "onet",
"name": "Onet",
"shows": "maybe",
"reasons": [
{
"code": "approval",
"message": "The provider approves domains first."
}
]
},
{
"id": "outlook",
"name": "Outlook",
"shows": "no",
"reasons": [
{
"code": "not_supported",
"message": "Doesn't show BIMI logos."
}
]
}
],
"nextSteps": [],
"issues": [
{
"severity": "warning",
"code": "svg_colors",
"message": "Only 1 colour is drawn: the profile requires at least two when rendered (a transparent background doesn't count)."
},
{
"severity": "warning",
"code": "svg_background",
"message": "The logo seems to have no solid background: inboxes show it on white or dark grey and crop it to a circle. Add a solid background shape behind the mark."
},
{
"severity": "info",
"code": "svg_desc_missing",
"message": "No <desc>: optional; Gmail recommends one for screen readers."
},
{
"severity": "info",
"code": "certificate_vmc",
"message": "A Verified Mark Certificate (VMC): Registered Mark."
},
{
"severity": "info",
"code": "certificate_chain_root_sent",
"message": "The file includes the root certificate: allowed (Gmail's help lists it), not required."
}
],
"tookMs": 2205
}How it works
Runs the steps a receiving mail server works through (draft-brand-indicators-for-message-identification §7): the assertion record at <selector>._bimi.<domain> (then the organizational domain's), DMARC at quarantine or reject applied to 100% of failing mail, the logo at l= fetched and checked against SVG Tiny PS, and the mark certificate at a= (VMC or CMC): its mark type, the domains it names, its chain to a BIMI root, its embedded logo against the published one, its Certificate Transparency proofs and revocation.
providers then reads each mailbox provider's own published rules: Gmail and Apple Mail need a certificate, Yahoo and Fastmail show self-asserted logos, Outlook shows none, and some also weigh volume and reputation, which no outside check can see (maybe).
The logo and certificate are fetched over HTTPS from public addresses only, size-capped. The logo itself isn't returned: its address, size, hash and title are. A step we couldn't check is error with incomplete, never a failure of the domain.
Query parameters
domainstringrequiredThe domain whose BIMI record to check (the From domain). An address's local part picks a local-part selector (lps=) when the record has them. A URL (its host), an email address (its domain), an IDN in Unicode or a trailing dot are read the way the tool page reads them; the normalised name comes back inquery.domain.selectorstringoptionaldefaultdefaultThe BIMI selector, as a BIMI-Selector header would name it: one or more DNS labels.
Response fields
200 with this JSON object, plus tookMs, and incomplete with warnings when a source failed. Fields marked optional appear only when they apply.
queryobjectalwaysWhat was looked up, normalised (lower case, no trailing dot, IDNs as Punycode).domainstringalwaysThe domain checked (the From domain), lower case, IDNs as Punycode;www.is dropped.selectorstringalwaysThe BIMI selector asked:defaultunlessselectornamed another.
verdictstring or nullalwaysWhether inboxes show the logo:ready(every step a receiver runs passes),partial(some major inboxes show it, others need more, usually a certificate),not_ready,declined(the record opts out),none(no record),unknown(a step couldn't be checked).nullwhen the check stopped early.one of
"ready","partial","not_ready","declined","none","unknown",nullrecordobject or nullalwaysThe BIMI assertion record (draft-brand-indicators-for-message-identification §4.3, §7.2).Show 12 fields
statusstringalwaysThe record step:pass,pass_with_warnings,fail(receivers show no logo because of it),not_checked(seenotChecked),error(we couldn't check: retry).one of
"pass","pass_with_warnings","fail","not_checked","error"statestringalwaysfound,none(no record at the domain or its organizational domain),multiple(two or more: receivers use none),error(DNS failed).one of
"found","none","multiple","error"namestring or nullalwaysWhere the record used lives:<selector>._bimi.<domain>, or the organizational domain's.levelstring or nullalwaysFound at the domain itself (author) or at its organizational domain.one of
"author","organizational",nulltextstring or nullalwaysThe record as published.ttlinteger or nullalwaysIts TTL in seconds.logoUrlstring or nullalwaysl=: the SVG logo's HTTPS address;nullwhen empty or absent.evidenceUrlstring or nullalwaysa=: the mark certificate's (VMC/CMC) PEM address;nullwhen empty or absent.declinedbooleanalwaysl= and a= are both empty: the domain declines BIMI.validboolean or nullalwaysThe record follows the draft's MUSTs;nullwhen there is no record.avpstring or nullalwaysavp=:brand(the default) orpersonal.lpsarray of strings or nullalwayslps=: local-part selectors, when present.
dmarcobject or nullalwaysThe DMARC prerequisite: p=quarantine or reject, applied to 100% of failing mail, at the domain and its organizational domain (§7.1).Show 4 fields
statusstringalwaysThe DMARC step:pass,pass_with_warnings,fail(receivers show no logo because of it),not_checked(seenotChecked),error(we couldn't check: retry).one of
"pass","pass_with_warnings","fail","not_checked","error"meetsRequirementboolean or nullalwaysDMARC is at enforcement as BIMI requires;nullwhen it couldn't be checked.domainobjectalwaysThe DMARC policy at the domain.Show 8 fields
domainstringalwaysThe name.foundAtstring or nullalwaysWhere the record that applies was found.recordstring or nullalwaysThe DMARC record;nullwhen none.pstring or nullalwaysp as published.spstring or nullalwayssp as published.pctinteger or nullalwayspct as published;nullwhen absent (100).testingbooleanalwayst=y (RFC 9989's test mode).statestringalwaysok(quarantine or reject at 100%),none(p=none or below what BIMI needs),missing,invalid,error(the lookup failed).one of
"ok","none","missing","invalid","error"
organizationalDomainobject or nullalwaysThe DMARC policy at the organizational domain, when it differs (nullotherwise).Show 8 fields
domainstringalwaysThe name.foundAtstring or nullalwaysWhere the record that applies was found.recordstring or nullalwaysThe DMARC record;nullwhen none.pstring or nullalwaysp as published.spstring or nullalwayssp as published.pctinteger or nullalwayspct as published;nullwhen absent (100).testingbooleanalwayst=y (RFC 9989's test mode).statestringalwaysok(quarantine or reject at 100%),none(p=none or below what BIMI needs),missing,invalid,error(the lookup failed).one of
"ok","none","missing","invalid","error"
logoobject or nullalwaysThe logo at l=, fetched over HTTPS and checked against SVG Tiny PS (never proxied: fetchurlyourself).Show 12 fields
statusstringalwaysThe logo step:pass,pass_with_warnings,fail(receivers show no logo because of it),not_checked(seenotChecked),error(we couldn't check: retry).one of
"pass","pass_with_warnings","fail","not_checked","error"notCheckedstring or nullalwaysWhy it wasn't checked: no record, declined, no l=, an unusable l= address, out of time, or a non-standard port;nullwhen it was.one of
"no_record","declined","no_logo","no_evidence","bad_uri","deadline","port",nullurlstring or nullalwaysThe address fetched.finalUrlstring or nullalwaysThe address that answered after redirects, when different.httpStatusinteger or nullalwaysThe HTTP status;nullwhen the fetch failed before one.contentTypestring or nullalwaysThe Content-Type served.bytesinteger or nullalwaysThe file's size in bytes.sha256string or nullalwaysThe file's SHA-256, hex.titlestring or nullalwaysThe SVG's <title> (the brand name receivers may show).svgTinyPsboolean or nullalwaysThe file passes SVG Tiny PS (the profile BIMI requires);nullwhen it wasn't checked.squareboolean or nullalwaysThe viewBox is square (inboxes crop logos to a circle).viewBoxarray of numbers or nullalwaysThe viewBox: min-x, min-y, width, height.
certificateobject or nullalwaysThe mark certificate at a= (a VMC or CMC), its chain to a BIMI root, its logo and its revocation status.Show 22 fields
statusstringalwaysThe certificate step:pass,pass_with_warnings,fail(receivers show no logo because of it),not_checked(seenotChecked),error(we couldn't check: retry).one of
"pass","pass_with_warnings","fail","not_checked","error"notCheckedstring or nullalwaysWhy it wasn't checked: no record, declined, no a= (no_evidence), an unusable a= address, out of time, or a non-standard port;nullwhen it was.one of
"no_record","declined","no_logo","no_evidence","bad_uri","deadline","port",nullurlstring or nullalwaysThe PEM address fetched.httpStatusinteger or nullalwaysThe HTTP status.typestring or nullalwaysvmc(Verified Mark Certificate: Gmail's blue checkmark) orcmc(Common Mark Certificate).one of
"vmc","cmc",nullmarkTypestring or nullalwaysRegistered Mark, Government Mark, Prior Use Mark or Modified Registered Mark.wordMarkstring or nullalwaysThe word mark, when the certificate carries one.trademarkobject or nullalwaysThe trademark registration it rests on.Show 3 fields
officestring or nullalwaysThe trademark office.countrystring or nullalwaysIts country.registrationstring or nullalwaysThe registration number.
organizationstring or nullalwaysThe subject's organization.countrystring or nullalwaysThe subject's country.issuerstring or nullalwaysThe issuing CA's name.castring or nullalwaysOur id for the CA brand (digicert, globalsign, ssl-com, sectigo…).serialstring or nullalwaysThe serial number, hex.validFromstring or nullalwaysnotBefore, ISO 8601.validTostring or nullalwaysnotAfter, ISO 8601: the logo disappears when it passes.domainsarray of stringsalwaysThe domains the certificate names (SAN).coversDomainboolean or nullalwaysThe certificate names the checked domain.chainTrustedboolean or nullalwaysThe chain reaches a mark certificate root.rootstring or nullalwaysThe root it reaches.logoMatchesboolean or nullalwaysThe logo at l= is byte for byte the one embedded in the certificate.revocationstring or nullalwaysThe CA's revocation answer (OCSP or CRL);nullwhen not asked.one of
"good","revoked","unknown","error",nullctLoggedboolean or nullalwaysEvery embedded Certificate Transparency proof verifies (mark certificates must be logged).
providersarray of objectsalwaysWhat each mailbox provider would show, by its own published rules: Gmail, Apple Mail, Yahoo, AOL, Fastmail, La Poste, Zone, Zoner, Onet, Outlook.Show 4 fields
idstringalwaysOur id: gmail, apple, yahoo, aol, fastmail, laposte, zone, zoner, onet, outlook.namestringalwaysIts name.showsstringalwayscheckmark(the logo with a verified badge),logo,no,maybe(it also weighs things no outside check can see: volume, reputation, its approval).one of
"checkmark","logo","no","maybe"reasonsarray of objectsalwaysWhy it shows, doesn't, or might.Show 2 fields
codestringalwaysStable code, e.g.no_cert,dmarc,reputation.messagestringalwaysThe reason in plain English.
nextStepsarray of objectsalwaysWhat to do next, most important first.codestringalwaysStable code, e.g.enforce_dmarc,get_certificate,fix_logo.messagestringalwaysThe step in plain English.
issuesarray of objectsalwaysEvery step's findings (record, DMARC, logo, certificate), worst first, in the BIMI Checker's words with the fix where there is one.errorbreaks something (receivers fail it),warningrisks it,infois worth knowing.severitystringalwayserrorbreaks something,warningrisks it,infois worth knowing.one of
"error","warning","info"codestringalwaysStable machine-readable code.messagestringalwaysThe problem and what to do about it, in plain English.
Errors
429rate_limitedThe domain (its registrable domain) was checked 20 times in the last minute by all callers together, or every caller together reached 600 a minute: retry afterRetry-Afterseconds.
Plus the errors every endpoint can return: 400 for a missing or unreadable parameter, 429 past the limit, 503 when every source failed.
Code samples
curl "https://dnsrobot.net/api/v2/bimi?domain=cnn.com"const params = new URLSearchParams({ domain: "cnn.com" });
const res = await fetch(`https://dnsrobot.net/api/v2/bimi?${params}`);
const data = await res.json();
if (!res.ok) throw new Error(data.error.message);
console.log(data.verdict);import requests
r = requests.get(
"https://dnsrobot.net/api/v2/bimi",
params={"domain": "cnn.com"},
timeout=30,
)
data = r.json()
if not r.ok:
raise RuntimeError(data["error"]["message"])
print(data["verdict"])package main
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
)
func main() {
q := url.Values{"domain": {"cnn.com"}}
res, err := http.Get("https://dnsrobot.net/api/v2/bimi?" + q.Encode())
if err != nil {
panic(err)
}
defer res.Body.Close()
var data map[string]any
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
panic(err)
}
if res.StatusCode != http.StatusOK {
panic(data["error"])
}
fmt.Println(data["verdict"])
}<?php
$query = http_build_query(["domain" => "cnn.com"]);
$context = stream_context_create(["http" => ["ignore_errors" => true, "timeout" => 30]]);
$data = json_decode(file_get_contents("https://dnsrobot.net/api/v2/bimi?" . $query, false, $context), true);
if (isset($data["error"])) {
throw new RuntimeException($data["error"]["message"]);
}
print_r($data["verdict"]);require "net/http"
require "json"
uri = URI("https://dnsrobot.net/api/v2/bimi")
uri.query = URI.encode_www_form(domain: "cnn.com")
data = JSON.parse(Net::HTTP.get(uri))
raise data["error"]["message"] if data["error"]
puts data["verdict"]// Cargo.toml: reqwest = { version = "0.13", features = ["blocking", "json"] }, serde_json = "1"
fn main() -> Result<(), Box<dyn std::error::Error>> {
let res = reqwest::blocking::get("https://dnsrobot.net/api/v2/bimi?domain=cnn.com")?;
let ok = res.status().is_success();
let data: serde_json::Value = res.json()?;
if !ok {
return Err(data["error"]["message"].to_string().into());
}
println!("{}", data["verdict"]);
Ok(())
}using System.Text.Json;
using var http = new HttpClient();
var res = await http.GetAsync("https://dnsrobot.net/api/v2/bimi?domain=cnn.com");
var data = JsonDocument.Parse(await res.Content.ReadAsStringAsync()).RootElement;
if (!res.IsSuccessStatusCode)
throw new Exception(data.GetProperty("error").GetProperty("message").GetString());
Console.WriteLine(data.GetProperty("verdict"));cnn.com: a VMC with Gmail's checkmark
GET /api/v2/bimi?domain=cnn.com{
"query": {
"domain": "cnn.com",
"selector": "default"
},
"verdict": "ready",
"record": {
"status": "pass",
"state": "found",
"name": "default._bimi.cnn.com",
"level": "author",
"text": "v=BIMI1; l=https://amplify.valimail.com/bimi/time-warner/R4Ezf5xK5ac-CNN.svg; a=https://amplify.…",
"ttl": 1800,
"logoUrl": "https://amplify.valimail.com/bimi/time-warner/R4Ezf5xK5ac-CNN.svg",
"evidenceUrl": "https://amplify.valimail.com/bimi/time-warner/R4Ezf5xK5ac-CNN.pem",
"declined": false,
"valid": true,
"avp": "brand",
"lps": null
},
"dmarc": {
"status": "pass",
"meetsRequirement": true,
"domain": {
"domain": "cnn.com",
"foundAt": "cnn.com",
"record": "v=DMARC1; p=reject; rua=mailto:dmarc_agg@vali.email; ruf=mailto:Njk3@ruf.vali.email",
"p": "reject",
"sp": null,
"pct": null,
"testing": false,
"state": "ok"
},
"organizationalDomain": null
},
"logo": {
"status": "pass_with_warnings",
"notChecked": null,
"url": "https://amplify.valimail.com/bimi/time-warner/R4Ezf5xK5ac-CNN.svg",
"finalUrl": null,
"httpStatus": 200,
"contentType": "image/svg+xml",
"bytes": 1725,
"sha256": "215d5054ca31f6d7aea6a316edc8824e45828fc18678fce20c8c886cb4252d22",
"title": "CNN",
"svgTinyPs": true,
"square": true,
"viewBox": [
0,
0,
967.5,
// … 1 more item
]
},
"certificate": {
"status": "pass",
"notChecked": null,
"url": "https://amplify.valimail.com/bimi/time-warner/R4Ezf5xK5ac-CNN.pem",
"httpStatus": 200,
"type": "vmc",
"markType": "Registered Mark",
"wordMark": null,
"trademark": {
"office": null,
"country": "US",
"registration": "5817930"
},
"organization": "Cable News Network, Inc.",
"country": "US",
"issuer": "DigiCert Verified Mark RSA4096 SHA256 2021 CA1",
"ca": "digicert",
"serial": "0cb658999c795a3a19c7ee4434a810bd",
"validFrom": "2026-08-28T00:00:00.000Z",
"validTo": "2027-09-01T23:59:59.000Z",
"domains": [
"cnn.com"
],
"coversDomain": true,
"chainTrusted": true,
"root": "DigiCert Verified Mark Root CA",
"logoMatches": true,
"revocation": "good",
"ctLogged": true
},
"providers": [
{
"id": "gmail",
"name": "Gmail",
"shows": "checkmark",
"reasons": []
},
{
"id": "apple",
"name": "Apple Mail",
"shows": "logo",
"reasons": [
{
"code": "undocumented",
"message": "Apple publishes no rules: based on its BIMI partners' documents."
}
]
},
{
"id": "yahoo",
"name": "Yahoo",
"shows": "maybe",
"reasons": [
{
"code": "reputation",
"message": "Also needs bulk volume and a good sender reputation."
}
]
},
// … 7 more items
],
"nextSteps": [],
"issues": [
{
"severity": "warning",
"code": "svg_colors",
"message": "Only 1 colour is drawn: the profile requires at least two when rendered (a transparent backgroun…"
},
{
"severity": "warning",
"code": "svg_background",
"message": "The logo seems to have no solid background: inboxes show it on white or dark grey and crop it to…"
},
{
"severity": "info",
"code": "svg_desc_missing",
"message": "No <desc>: optional; Gmail recommends one for screen readers."
},
// … 2 more items
],
"tookMs": 2205
}Shortened for the page: long lists show their first items.
github.com
GET /api/v2/bimi?domain=github.com{
"query": {
"domain": "github.com",
"selector": "default"
},
"verdict": "none",
"record": {
"status": "fail",
"state": "none",
"name": null,
"level": null,
"text": null,
"ttl": null,
"logoUrl": null,
"evidenceUrl": null,
"declined": false,
"valid": null,
"avp": null,
"lps": null
},
"dmarc": {
"status": "pass",
"meetsRequirement": true,
"domain": {
"domain": "github.com",
"foundAt": "github.com",
"record": "v=DMARC1; p=quarantine; sp=reject; pct=100; rua=mailto:dmarc@github.com; ruf=mailto:dmarc@github…",
"p": "quarantine",
"sp": "reject",
"pct": 100,
"testing": false,
"state": "ok"
},
"organizationalDomain": null
},
"logo": {
"status": "not_checked",
"notChecked": "no_record",
"url": null,
"finalUrl": null,
"httpStatus": null,
"contentType": null,
"bytes": null,
"sha256": null,
"title": null,
"svgTinyPs": null,
"square": null,
"viewBox": null
},
"certificate": {
"status": "not_checked",
"notChecked": "no_record",
"url": null,
"httpStatus": null,
"type": null,
"markType": null,
"wordMark": null,
"trademark": null,
"organization": null,
"country": null,
"issuer": null,
"ca": null,
"serial": null,
"validFrom": null,
"validTo": null,
"domains": [],
"coversDomain": null,
"chainTrusted": null,
"root": null,
"logoMatches": null,
"revocation": null,
"ctLogged": null
},
"providers": [
{
"id": "gmail",
"name": "Gmail",
"shows": "no",
"reasons": [
{
"code": "no_record",
"message": "No BIMI record."
}
]
},
{
"id": "apple",
"name": "Apple Mail",
"shows": "no",
"reasons": [
{
"code": "no_record",
"message": "No BIMI record."
}
]
},
{
"id": "yahoo",
"name": "Yahoo",
"shows": "no",
"reasons": [
{
"code": "no_record",
"message": "No BIMI record."
}
]
},
// … 7 more items
],
"nextSteps": [
{
"code": "publish_record",
"message": "Publish a BIMI record at default._bimi.github.com (the BIMI Checker's Build a record mode writes…"
}
],
"issues": [
{
"severity": "error",
"code": "record_none",
"message": "No BIMI record at default._bimi.github.com or at the organizational domain. Publish a TXT record…"
}
],
"tookMs": 303
}Shortened for the page: long lists show their first items.