Get started/ Quickstart
Get started

Quickstart

Get a key, send one submission, act on the verdict. Five minutes in any language.

1. Get a key

Sign in with your email. The dashboard creates a key on the Free plan with 1,000 checks a month. Copy it once. Only a hash is stored on our side, so a lost key is rotated, not recovered.

2. Send a submission

Forward the fields your form collected plus the visitor’s IP address.

curl
curl https://api.spamex.dev/v1/classify \
  -H "Authorization: Bearer $SPAMEX_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Rachel Green",
    "email": "[email protected]",
    "subject": "Leaking kitchen faucet",
    "message": "My kitchen faucet has been dripping for a week. Could someone come out this week? What are your rates?",
    "ip": "198.51.100.7"
  }'
Node
const res = await fetch("https://api.spamex.dev/v1/classify", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SPAMEX_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: fields.name,
    email: fields.email,
    subject: fields.subject,
    message: fields.message,
    ip: req.ip,
  }),
  signal: AbortSignal.timeout(5000),
});
const verdict = res.ok ? await res.json() : null; // null: deliver (fail open)
Python
import httpx

r = httpx.post(
    "https://api.spamex.dev/v1/classify",
    headers={"Authorization": f"Bearer {SPAMEX_KEY}"},
    json={
        "name": fields["name"],
        "email": fields["email"],
        "subject": fields["subject"],
        "message": fields["message"],
        "ip": request.client.host,
    },
    timeout=5.0,
)
verdict = r.json() if r.status_code == 200 else None  # None: deliver (fail open)
PHP
$ch = curl_init('https://api.spamex.dev/v1/classify');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 5,
    CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('SPAMEX_KEY'), 'Content-Type: application/json'],
    CURLOPT_POSTFIELDS => json_encode([
        'name'    => $_POST['name'] ?? '',
        'email'   => $_POST['email'] ?? '',
        'subject' => $_POST['subject'] ?? '',
        'message' => $_POST['message'] ?? '',
        'ip'      => $_SERVER['REMOTE_ADDR'] ?? null,
    ]),
]);
$res = curl_exec($ch);
$verdict = curl_getinfo($ch, CURLINFO_HTTP_CODE) === 200 ? json_decode($res, true) : null; // null: deliver

The response comes back in a few milliseconds for most submissions:

{
  "id": "1dab208f9c8043d8b016acd9343deb71",
  "label": "ham",
  "score": 0.0018,
  "stage": "ml",
  "reasons": ["ML_HAM"],
  "latency_ms": 2.97
}

3. Act on the verdict

The simplest policy that works well:

verdictdo
spam with HONEYPOT_FILLED, FAST_SUBMIT, DUP_KNOWN_SPAM or a *_REPUTATION_BAD codeDrop silently. Return the usual “thanks” page without sending mail. These are bots.
spam from stage ml or llmRoute to a review folder instead of the inbox. Show the visitor a fallback email address.
hamDeliver normally.

Store the id next to the message. If a verdict turns out wrong, one call fixes it and teaches your site’s memory:

curl -X POST https://api.spamex.dev/v1/feedback \
  -H "Authorization: Bearer $SPAMEX_KEY" -H "Content-Type: application/json" \
  -d '{"id": "1dab208f9c8043d8b016acd9343deb71", "label": "ham"}'

Handling verdicts goes deeper: what to show users, when to fail open, how to wire a “not spam” button.

4. Add the free signals

Two signals from your own form catch a large share of bots before any model runs.

  • A hidden trap field. Add an input bots will fill in and humans never see. Name it something a bot wants, such as website, hide it with CSS rather than type="hidden", and tell spamex the field name in your key settings. Send its value as honeypot.
  • Timing. Put the page-load time in a hidden field and note the submit time on your server. Send them as form_load_ts and submit_ts. A form completed in under two seconds is a hard spam verdict.
{
  "message": "...",
  "honeypot": "",
  "form_load_ts": 1757000000,
  "submit_ts": 1757000041,
  "ip": "198.51.100.7"
}

That is the whole integration. For a complete handler in your language, see Node.js, PHP and WordPress or Python. Running a form service? Read For form platforms.

↑↓ navigate↵ openDocs and blog