{
  "openapi": "3.0.2",
  "info": {
    "title": "BounceLens Email Check",
    "version": "1.0.0",
    "description": "Free email address checks: format, domain and mail server (MX), typo suggestions, disposable (throwaway) domains, role addresses, free providers and duplicates. No API key and no signup. It does not log in to mail servers, so it cannot tell whether a mailbox exists: addresses that pass are marked \"unconfirmed\".",
    "termsOfService": "https://bouncelens.com/terms",
    "contact": { "name": "BounceLens Support", "url": "https://bouncelens.com/support", "email": "support@bouncelens.com" }
  },
  "externalDocs": { "description": "API documentation", "url": "https://bouncelens.com/email-check-api/" },
  "servers": [{ "url": "https://bouncelens.com" }],
  "paths": {
    "/api/check": {
      "get": {
        "operationId": "checkEmail",
        "summary": "Check one email address",
        "parameters": [
          {
            "name": "email",
            "in": "query",
            "required": true,
            "description": "The address to check. URL-encode it (a + sign must be sent as %2B).",
            "schema": { "type": "string" },
            "example": "john@gmial.com"
          }
        ],
        "responses": {
          "200": { "$ref": "#/components/responses/CheckResponse" },
          "400": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      },
      "post": {
        "operationId": "checkEmails",
        "summary": "Check a list of email addresses",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["emails"],
                "properties": {
                  "emails": {
                    "type": "array",
                    "description": "1 to 500 addresses, with at most 20 different domains in one call.",
                    "minItems": 1,
                    "maxItems": 500,
                    "items": { "type": "string" }
                  }
                }
              },
              "example": { "emails": ["info@spotify.com", "a@mailinator.com"] }
            }
          }
        },
        "responses": {
          "200": { "$ref": "#/components/responses/CheckResponse" },
          "400": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    }
  },
  "components": {
    "responses": {
      "CheckResponse": {
        "description": "One result per address, in the order sent, plus totals.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CheckResponse" } } }
      },
      "Error": {
        "description": "The request was refused; \"error\" says why.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "RateLimited": {
        "description": "More than 20 requests in 10 seconds from one IP address. Wait 10 seconds and retry. The body is not JSON."
      }
    },
    "schemas": {
      "CheckResponse": {
        "type": "object",
        "required": ["summary", "results"],
        "properties": {
          "summary": { "$ref": "#/components/schemas/Summary" },
          "results": { "type": "array", "items": { "$ref": "#/components/schemas/Result" } }
        }
      },
      "Result": {
        "type": "object",
        "required": ["input", "email", "status", "reasons", "flags", "did_you_mean", "provider", "mx"],
        "properties": {
          "input": { "type": "string", "description": "The address as it was sent." },
          "email": { "type": "string", "nullable": true, "description": "The cleaned address, or null when the format is invalid." },
          "status": {
            "type": "string",
            "enum": ["invalid", "risky", "unconfirmed"],
            "description": "invalid: will bounce. risky: disposable, likely typo, or no MX record. unconfirmed: format and domain are OK, the mailbox itself is not confirmed."
          },
          "reasons": { "type": "array", "items": { "type": "string" }, "description": "Plain-English reasons for the status." },
          "flags": {
            "type": "object",
            "required": ["disposable", "role", "free", "duplicate", "gateway"],
            "properties": {
              "disposable": { "type": "boolean", "description": "Throwaway email domain." },
              "role": { "type": "boolean", "description": "Shared inbox such as info@ or sales@." },
              "free": { "type": "boolean", "description": "Free provider such as Gmail or Yahoo." },
              "duplicate": { "type": "boolean", "description": "Same inbox as an earlier address in this call (Gmail dots and +tags ignored)." },
              "gateway": { "type": "boolean", "description": "The domain's mail goes through a security gateway." }
            }
          },
          "did_you_mean": { "type": "string", "nullable": true, "description": "Corrected address when the domain looks like a typo." },
          "provider": { "type": "string", "nullable": true, "description": "Mail host when recognised, for example Google or Microsoft." },
          "mx": { "type": "string", "nullable": true, "description": "The domain's first mail server." }
        }
      },
      "Summary": {
        "type": "object",
        "required": ["total", "invalid", "risky", "unconfirmed", "duplicates", "disposable", "role", "free", "typos"],
        "properties": {
          "total": { "type": "integer" },
          "invalid": { "type": "integer" },
          "risky": { "type": "integer" },
          "unconfirmed": { "type": "integer", "description": "Clean, unique addresses. invalid + risky + duplicates + unconfirmed = total." },
          "duplicates": { "type": "integer" },
          "disposable": { "type": "integer" },
          "role": { "type": "integer" },
          "free": { "type": "integer" },
          "typos": { "type": "integer" }
        }
      },
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": { "error": { "type": "string" } }
      }
    }
  }
}
