Free Email Spam Tester Tests API
The Tests API from Free Email Spam Tester — 4 operation(s) for tests.
The Tests API from Free Email Spam Tester — 4 operation(s) for tests.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/email-spam-tester-com:email-spam-tester-com-tests-api"
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Email Spam Tester Tests API
description: 'Test an email before you send it to anybody.
The loop is three calls. Ask for an address, send the message you were about to
send your list to that address, then read the report. No key, no account, no
limit per person on the first 100,000 reports.
curl -X POST https://email-spam-tester.com/api/v1/inbox
# send your message to the address it returns, then:
curl https://email-spam-tester.com/api/v1/tests/
The address accepts exactly one message and expires after an hour. The report
link is permanent and carries `report_url` inside the JSON, so a forwarded
report still says where it came from.
**Poll `/status`, not `/`.** The full report is several thousand lines of JSON
and the analysis takes a few seconds; the status endpoint is the cheap way to
find out whether it is worth fetching yet. `analysis_status` reaches
`checks_ready` first and the AI plan lands a minute or so later under
`ai_status`, so a caller that only wants the checks does not have to wait for
the plan.
**Agents:** there is an MCP server at `/mcp` with the same capability and a
blocking wait, which is a better fit than polling from a model.
Every finding carries `citations`: the section of the RFC it comes from, quoted,
and the page where Google states its own requirement. Those are curated by hand
rather than generated, so they can be relied on.'
contact:
name: Email Spam Tester
url: https://email-spam-tester.com/
version: 1.0.0
tags:
- name: Tests
paths:
/api/v1/tests/{slug}/locale:
put:
summary: Set Locale
description: 'Change which language the report and the AI plan will be written in.
The language is fixed when the address is handed out, because the SMTP
connection that arrives ten minutes later carries no hint of who is
waiting for it. That leaves one gap: somebody reserves an address, then
switches the page to their own language before sending. Without this they
would read a German page and get an English plan.
Only while the reservation is unused. Once the mail has arrived the
analysis is either running or done, and rewriting the language then would
mean re-running it — a different operation, and one nobody asked for.
The slug is the capability, as everywhere else here: whoever holds the
address holds the test. There is nothing to leak — a wrong guess sets the
language of a report the guesser cannot read.'
operationId: set_locale_api_v1_tests__slug__locale_put
parameters:
- name: slug
in: path
required: true
schema:
type: string
title: Slug
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/LocaleIn'
responses:
'204':
description: Successful Response
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
tags:
- Tests
/api/v1/tests/{slug}/status:
get:
summary: Test Status
description: 'Where the analysis is. With `wait`, hold the request until it gets there.
A panel plugin would otherwise poll every couple of seconds for a minute or
five, from thousands of servers. `until=checks` returns once the checks are
in; `until=plan` waits for the AI plan as well. Past `wait` seconds it
answers with whatever there is, the same as without it.'
operationId: test_status_api_v1_tests__slug__status_get
parameters:
- name: slug
in: path
required: true
schema:
type: string
title: Slug
- name: wait
in: query
required: false
schema:
type: integer
maximum: 50
minimum: 0
default: 0
title: Wait
- name: until
in: query
required: false
schema:
enum:
- checks
- plan
type: string
default: checks
title: Until
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/StatusOut'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
tags:
- Tests
/api/v1/tests/{slug}/message:
get:
summary: Message
description: 'The message itself: headers, both bodies, and the source verbatim.
Separate from the report rather than a field on it, because the report is
polled every two seconds while the analysis runs and a megabyte of source
on every poll would be paid for by every user to serve the few who open the
panel. This is fetched once, when they do.'
operationId: message_api_v1_tests__slug__message_get
parameters:
- name: slug
in: path
required: true
schema:
type: string
title: Slug
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/MessageViewOut'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
tags:
- Tests
/api/v1/tests/{slug}:
get:
summary: Report
operationId: report_api_v1_tests__slug__get
parameters:
- name: slug
in: path
required: true
schema:
type: string
title: Slug
- name: lang
in: query
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Lang
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/ReportOut'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
tags:
- Tests
components:
schemas:
AttachmentOut:
properties:
filename:
anyOf:
- type: string
- type: 'null'
title: Filename
content_type:
type: string
title: Content Type
size_bytes:
type: integer
title: Size Bytes
inline:
type: boolean
title: Inline
type: object
required:
- filename
- content_type
- size_bytes
- inline
title: AttachmentOut
LocaleIn:
properties:
lang:
type: string
title: Lang
type: object
required:
- lang
title: LocaleIn
description: 'Which language the reader is on now.
Sent when somebody switches language on a reservation they have not used
yet. Not validated to a fixed set here: locales.normalise falls back to the
base language and then to English, so an unknown tag is a page in English
rather than a 422 the visitor cannot act on.'
HTTPValidationError:
properties:
detail:
items:
$ref: '#/components/schemas/ValidationError'
type: array
title: Detail
type: object
title: HTTPValidationError
MessageOut:
properties:
subject:
type: string
title: Subject
from_addr:
anyOf:
- type: string
- type: 'null'
title: From Addr
bounce_address:
anyOf:
- type: string
- type: 'null'
title: Bounce Address
size_bytes:
type: integer
title: Size Bytes
has_html:
type: boolean
title: Has Html
has_text:
type: boolean
title: Has Text
forged_headers:
additionalProperties: true
type: object
title: Forged Headers
kind:
anyOf:
- type: string
- type: 'null'
title: Kind
type: object
required:
- subject
- from_addr
- bounce_address
- size_bytes
- has_html
- has_text
- forged_headers
title: MessageOut
ReportOut:
properties:
report_url:
type: string
title: Report Url
default: ''
generated_by:
type: string
title: Generated By
default: Email Spam Tester (https://email-spam-tester.com)
slug:
type: string
title: Slug
analysis_status:
type: string
title: Analysis Status
ai_status:
type: string
title: Ai Status
score_compat:
anyOf:
- type: number
- type: 'null'
title: Score Compat
score_ours:
anyOf:
- type: number
- type: 'null'
title: Score Ours
subscores:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
title: Subscores
scoring_version:
anyOf:
- type: string
- type: 'null'
title: Scoring Version
complete:
type: boolean
title: Complete
errored_checks:
items:
type: string
type: array
title: Errored Checks
ai_verdict:
anyOf:
- type: string
- type: 'null'
title: Ai Verdict
ai_confidence:
anyOf:
- type: string
- type: 'null'
title: Ai Confidence
received_at:
type: string
format: date-time
title: Received At
checks_ready_at:
anyOf:
- type: string
format: date-time
- type: 'null'
title: Checks Ready At
message:
anyOf:
- $ref: '#/components/schemas/MessageOut'
- type: 'null'
panel:
items:
$ref: '#/components/schemas/EngineOut'
type: array
title: Panel
checks:
items:
$ref: '#/components/schemas/CheckOut'
type: array
title: Checks
fixes:
items:
$ref: '#/components/schemas/FixOut'
type: array
title: Fixes
translating:
type: boolean
title: Translating
default: false
type: object
required:
- slug
- analysis_status
- ai_status
- score_compat
- score_ours
- subscores
- scoring_version
- complete
- errored_checks
- ai_verdict
- ai_confidence
- received_at
- checks_ready_at
- message
- panel
- checks
- fixes
title: ReportOut
mailtester__api__schemas__HeaderOut:
properties:
name:
type: string
title: Name
value:
type: string
title: Value
forged:
type: boolean
title: Forged
type: object
required:
- name
- value
- forged
title: HeaderOut
StatusOut:
properties:
slug:
type: string
title: Slug
analysis_status:
type: string
title: Analysis Status
ai_status:
type: string
title: Ai Status
checks_done:
type: integer
title: Checks Done
checks_total:
type: integer
title: Checks Total
type: object
required:
- slug
- analysis_status
- ai_status
- checks_done
- checks_total
title: StatusOut
CheckOut:
properties:
id:
type: string
title: Id
category:
type: string
title: Category
status:
type: string
title: Status
title:
type: string
title: Title
summary:
type: string
title: Summary
summary_localised:
anyOf:
- type: string
- type: 'null'
title: Summary Localised
weight_compat:
type: number
title: Weight Compat
weight_ours:
type: number
title: Weight Ours
evidence:
additionalProperties: true
type: object
title: Evidence
citations:
additionalProperties: true
type: object
title: Citations
default: {}
type: object
required:
- id
- category
- status
- title
- summary
- weight_compat
- weight_ours
- evidence
title: CheckOut
FixOut:
properties:
id:
type: string
title: Id
fix:
additionalProperties: true
type: object
title: Fix
gain_compat:
type: number
title: Gain Compat
gain_ours:
type: number
title: Gain Ours
source:
type: string
title: Source
type: object
required:
- id
- fix
- gain_compat
- gain_ours
- source
title: FixOut
ValidationError:
properties:
loc:
items:
anyOf:
- type: string
- type: integer
type: array
title: Location
msg:
type: string
title: Message
type:
type: string
title: Error Type
input:
title: Input
ctx:
type: object
title: Context
type: object
required:
- loc
- msg
- type
title: ValidationError
MessageViewOut:
properties:
available:
type: boolean
title: Available
unavailable_reason:
anyOf:
- type: string
- type: 'null'
title: Unavailable Reason
size_bytes:
type: integer
title: Size Bytes
headers:
items:
$ref: '#/components/schemas/mailtester__api__schemas__HeaderOut'
type: array
title: Headers
text:
anyOf:
- type: string
- type: 'null'
title: Text
text_truncated:
type: boolean
title: Text Truncated
html:
anyOf:
- type: string
- type: 'null'
title: Html
html_truncated:
type: boolean
title: Html Truncated
attachments:
items:
$ref: '#/components/schemas/AttachmentOut'
type: array
title: Attachments
source:
anyOf:
- type: string
- type: 'null'
title: Source
source_truncated:
type: boolean
title: Source Truncated
type: object
required:
- available
- unavailable_reason
- size_bytes
- headers
- text
- text_truncated
- html
- html_truncated
- attachments
- source
- source_truncated
title: MessageViewOut
description: The raw message, for reading. Absent once its retention window closes.
EngineOut:
properties:
id:
type: string
title: Id
title:
type: string
title: Title
status:
type: string
title: Status
summary:
type: string
title: Summary
evidence:
additionalProperties: true
type: object
title: Evidence
scores:
type: boolean
title: Scores
type: object
required:
- id
- title
- status
- summary
- evidence
- scores
title: EngineOut