Back to Blog
Tutorial April 28, 2026 12 min read

Building a Spam-Free Contact Form in 15 Minutes

Stop bots from flooding your inbox. We walk through integrating the Spam Shield API into a Node.js contact form and show real-world results after 30 days.

Every website has a contact form. Every contact form gets spam. After three weeks of manually deleting “FREE iPhone Winner!!” messages, I decided to actually fix the problem.

The Setup

I had a simple Express.js server with a contact form endpoint. It looked something like this:

app.post('/contact', async (req, res) => {
  const { name, email, message } = req.body;
  // Send email...
  await sendEmail({ name, email, message });
  res.json({ success: true });
});

Simple. But bots didn’t care about simplicity. They found my form and flooded it.

The Solution: Spam Shield API

The Contact Form Spam Shield API checks submissions for:

  • Spam patterns (ALL CAPS, excessive punctuation, suspicious links)
  • Bot signatures (fake emails, known spam domains)
  • Phishing attempts (suspicious URLs, credential harvesting)
  • Velocity attacks (too many submissions from one source)

Here’s the integration:

import express from 'express';

const app = express();

// Real spam check
app.post('/contact', async (req, res) => {
  const { name, email, message } = req.body;
  
  // Check for spam
  const spamResponse = await fetch('https://contact-form-spam-shield-ai-spam-bot-detector-api.p.rapidapi.com/', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-RapidAPI-Key': process.env.RAPIDAPI_KEY,
      'X-RapidAPI-Host': 'contact-form-spam-shield-ai-spam-bot-detector-api.p.rapidapi.com'
    },
    body: JSON.stringify({ name, email, message })
  });
  
  const { is_spam, confidence, verdict } = await spamResponse.json();
  
  if (verdict === 'BLOCK') {
    return res.status(400).json({ error: 'Submission rejected' });
  }
  
  // Only real submissions reach here
  await sendEmail({ name, email, message });
  res.json({ success: true });
});
Pro Tip

You can use confidence to implement soft blocks—flag high-confidence spam for review but don’t automatically reject it.

Real-World Results

After 30 days with the integration in place:

MetricBeforeAfter
Spam submissions~150/day~2/day
False positivesN/A<1%
Avg response time320ms340ms
Bot submissions~130/day0

The two remaining “spam” detections were actually legitimate submissions that looked spammy (all caps subject lines from frustrated customers).

What I Learned

  1. Speed matters: The API responds in <200ms, so users don’t notice the check
  2. Confidence > binary: Using the confidence score is more useful than just the verdict
  3. Log everything: I still log blocked submissions for occasional review
  4. Rate limiting helps too: Combined with API spam checking, rate limiting catches burst attacks

The Full Integration

Here’s the complete production-ready version with error handling and logging:

app.post('/contact', async (req, res) => {
  const { name, email, message } = req.body;
  
  // Validate input
  if (!name || !email || !message) {
    return res.status(400).json({ error: 'Missing required fields' });
  }
  
  try {
    const spamResponse = await fetch('https://contact-form-spam-shield-ai-spam-bot-detector-api.p.rapidapi.com/', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-RapidAPI-Key': process.env.RAPIDAPI_KEY
      },
      body: JSON.stringify({ name, email, message })
    });
    
    if (!spamResponse.ok) {
      console.error('Spam API error:', spamResponse.status);
      // Fail open for spam API errors—don't block legitimate users
    } else {
      const { is_spam, confidence } = await spamResponse.json();
      
      if (is_spam && confidence > 0.85) {
        console.log('Spam blocked:', { email, confidence });
        return res.status(400).json({ error: 'Invalid submission' });
      }
    }
    
    await sendEmail({ name, email, message });
    res.json({ success: true });
    
  } catch (error) {
    console.error('Contact form error:', error);
    res.status(500).json({ error: 'Internal error' });
  }
});

Total integration time: 15 minutes. Spam reduction: 99%.

Not bad for an afternoon’s work.