DKIM Archive API
Specification for DKIM Archive API, which follows OpenAPI 3.0.3 specs.
API Documentation header image

API Reference

This is the API specification for the DKIM Archive API, which processes Gmail messages for DKIM signatures and provides -

  1. Endpoints to query archived keys
  2. Add domain-selector pairs, and
BASE URL
https://archive.zk.email/api

List-keys

GET
/api/key

Returns a list of DKIM keys for a given domain and its subdomains.

Parameters

domainstring(query)
REQUIRED

Output the domain name and/or any matching subdomains

TRY IT OUT

Responses

200
Successful operation
Media type: application/json
EXAMPLE VALUE
[
  {
    "id": 4711,
    "value": "string",
    "domain": "string",
    "selector": "string",
    "lastSeenAt": "2025-06-25T13:03:56.862Z",
    "firstSeenAt": "2025-06-25T13:03:56.862Z",
    "observations": [
      {
        "source": "live_dns",
        "firstSeenAt": "2025-06-25T13:03:56.862Z",
        "lastSeenAt": "2025-06-25T13:03:56.862Z"
      }
    ]
  }
]
SCHEMA
DkimKeys
Items object
value* string
domain* string
selector* string
lastSeenAt* stringdate-time
firstSeenAt* stringdate-time
400
Missing or invalid parameter
429
Rate limit exceeded
500
Unexpected error

Signed observation statements

GET
/api/key/statement

Returns signed, offline-verifiable statements for one domain and selector, as a JSON array of compact JWS strings. Embed them in your own evidence and re-verify them later without calling this service.

One statement is issued per stored key value per signable observation channel, each carrying the source it came from. Bound key validity on the channel you trust rather than on a blended window.

Only live_dns is signed. gcd_recovered observations are not: their ingest path does not establish that the submitted email is genuine. So a key known only from a submitted email yields an empty array here while still appearing, unsigned, in /api/key.

Parameters

domainstring(query)
REQUIRED
selectorstring(query)
REQUIRED

Responses

200
Successful operation
EXAMPLE VALUE
[
  "eyJhbGciOiJFZERTQSIsImtpZCI6ImFyY2hpdmUtc3RhdGVtZW50LTIwMjYtMDgtMTMtNTdZYjRrWDEifQ..."
]
Decoded JWS payload:
STATEMENT PAYLOAD
{
  "v": 1,
  "iss": "archive.zk.email",
  "iat": 1789000000,
  "record": {
    "id": "4711",
    "domain": "example.com",
    "selector": "mail2026",
    "value": "v=DKIM1; k=rsa; p=MIIB...",
    "source": "live_dns",
    "first_seen_at": "2026-06-01T00:00:00.000Z",
    "last_seen_at": "2026-07-15T08:00:00.000Z"
  }
}

A response is capped at 200 records, the most recent by first-seen, because each one costs a signature. Every response carries X-Total-Records, and X-Records-Truncated: true when the cap was hit. Note that X-Total-Records counts stored records, not statements, so it is legitimately larger than the array whenever a pair has observations we do not sign.

400
Missing or invalid parameter
429
Rate limit exceeded
503
Statement signing is not configured on this deployment

Statement verification keys

GET
/.well-known/dkim-archive-jwks.json

The public keys statements are verified against, as a JWKS. Any stock JOSE library can use it. Resolve the key by the kid in the JWS header, and accept only EdDSA or ES256.

The set is append-only: rotation adds a key and never removes one, so statements signed by a retired key stay verifiable. It is also mirrored in the repository, so you can pin it instead of fetching it at verification time.

Full format specification, provenance semantics, and freshness guarantees.

Limit reached? Reach out to our team to enhance your rate limit.