API Documentation

Track form submissions programmatically using FormTrackr's API. Perfect for React, Vue, Angular, and other custom implementations.

Overview

FormTrackr's API allows you to track form submissions directly from your application without using the default JavaScript snippet. This is ideal for:

  • React, Vue, Angular, and other JavaScript frameworks
  • Single-page applications (SPAs) with programmatic form handling
  • Multi-step forms that don't trigger native submit events
  • Server-side form processing
  • Custom form validation and submission logic

Note: If you're using standard HTML forms, you can use our default tracking snippet which automatically captures form submissions. This API documentation is for custom implementations.

Getting Started

Step 1: Get Your Project Key

Log in to your FormTrackr dashboard and navigate to your project. Your Project Key is displayed in the project settings. This unique identifier is required for all API requests.

Step 2: Make API Requests

Send POST requests to the FormTrackr API endpoint with your form submission data. The API endpoint is:

https://formtrackr.app/api/collect

API Endpoint

POST/api/collect

Request Headers

Content-Type: application/json

Request Body

{
  "projectKey": "string (required)",
  "sessionId": "string (required)",
  "pageUrl": "string (required, valid URL)",
  "formId": "string (optional)",
  "formName": "string (optional)",
  "fields": [
    {
      "name": "string",
      "value": "string"
    }
  ],
  "contact": {
    "name": "string (optional)",
    "email": "string (optional, valid email)",
    "phone": "string (optional)"
  },
  "attribution": {
    "landingPage": "string (optional)",
    "referrer": "string (optional)",
    "utms": {
      "source": "string (optional)",
      "medium": "string (optional)",
      "campaign": "string (optional)",
      "term": "string (optional)",
      "content": "string (optional)"
    },
    "gclid": "string (optional)"
  },
  "timestamp": "string (optional, ISO 8601)",
  "userAgent": "string (optional)"
}

Response

Success response (200):

{
  "success": true,
  "leadId": "string",
  "requestId": "string"
}

Error responses:

  • 400 Bad Request: Invalid request data or missing required fields
  • 403 Forbidden: Domain not allowed (if domain allowlist is configured)
  • 429 Too Many Requests: Rate limit exceeded
  • 500 Internal Server Error: Server error

React Example

Here's a complete example of tracking a form submission in a React component:

import React, { useState } from 'react';

const ContactForm = () => {
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    message: '',
  });

  // Generate or retrieve session ID (store in localStorage)
  const getSessionId = () => {
    let sessionId = localStorage.getItem('formtrackr_session_id');
    if (!sessionId) {
      sessionId = Math.random().toString(36).substring(2) + Date.now().toString(36);
      localStorage.setItem('formtrackr_session_id', sessionId);
    }
    return sessionId;
  };

  // Get URL parameters for attribution
  const getUrlParam = (name: string) => {
    const params = new URLSearchParams(window.location.search);
    return params.get(name);
  };

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();

    // Submit to your backend first
    const response = await fetch('/api/contact', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(formData),
    });

    if (response.ok) {
      // Track in FormTrackr after successful submission
      try {
        const attribution = {
          landingPage: window.location.href,
          referrer: document.referrer || null,
          utms: {
            source: getUrlParam('utm_source'),
            medium: getUrlParam('utm_medium'),
            campaign: getUrlParam('utm_campaign'),
            term: getUrlParam('utm_term'),
            content: getUrlParam('utm_content'),
          },
          gclid: getUrlParam('gclid'),
        };

        await fetch('https://formtrackr.app/api/collect', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            projectKey: 'YOUR_PROJECT_KEY',
            sessionId: getSessionId(),
            pageUrl: window.location.href,
            formId: 'contact-form',
            formName: 'Contact Form',
            fields: [
              { name: 'name', value: formData.name },
              { name: 'email', value: formData.email },
              { name: 'message', value: formData.message },
            ],
            contact: {
              name: formData.name,
              email: formData.email,
            },
            attribution: attribution,
            timestamp: new Date().toISOString(),
            userAgent: navigator.userAgent,
          }),
        });

        alert('Thank you for your message!');
        setFormData({ name: '', email: '', message: '' });
      } catch (error) {
        console.error('FormTrackr tracking error:', error);
        // Don't fail the form submission if tracking fails
      }
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={formData.name}
        onChange={(e) => setFormData({ ...formData, name: e.target.value })}
        placeholder="Name"
        required
      />
      <input
        type="email"
        value={formData.email}
        onChange={(e) => setFormData({ ...formData, email: e.target.value })}
        placeholder="Email"
        required
      />
      <textarea
        value={formData.message}
        onChange={(e) => setFormData({ ...formData, message: e.target.value })}
        placeholder="Message"
        required
      />
      <button type="submit">Submit</button>
    </form>
  );
};

export default ContactForm;

Plain JavaScript Example

Example using vanilla JavaScript:

// Session ID management
function getSessionId() {
  let sessionId = localStorage.getItem('formtrackr_session_id');
  if (!sessionId) {
    sessionId = Math.random().toString(36).substring(2) + Date.now().toString(36);
    localStorage.setItem('formtrackr_session_id', sessionId);
  }
  return sessionId;
}

// Get URL parameter
function getUrlParam(name) {
  const params = new URLSearchParams(window.location.search);
  return params.get(name);
}

// Track form submission
async function trackFormSubmission(formData) {
  const attribution = {
    landingPage: window.location.href,
    referrer: document.referrer || null,
    utms: {
      source: getUrlParam('utm_source'),
      medium: getUrlParam('utm_medium'),
      campaign: getUrlParam('utm_campaign'),
      term: getUrlParam('utm_term'),
      content: getUrlParam('utm_content'),
    },
    gclid: getUrlParam('gclid'),
  };

  try {
    const response = await fetch('https://formtrackr.app/api/collect', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        projectKey: 'YOUR_PROJECT_KEY',
        sessionId: getSessionId(),
        pageUrl: window.location.href,
        formId: 'contact-form',
        formName: 'Contact Form',
        fields: [
          { name: 'name', value: formData.name },
          { name: 'email', value: formData.email },
          { name: 'message', value: formData.message },
        ],
        contact: {
          name: formData.name,
          email: formData.email,
        },
        attribution: attribution,
        timestamp: new Date().toISOString(),
        userAgent: navigator.userAgent,
      }),
    });

    if (response.ok) {
      const data = await response.json();
      console.log('Lead tracked:', data.leadId);
    }
  } catch (error) {
    console.error('Tracking error:', error);
  }
}

// Use in your form handler
document.getElementById('contact-form').addEventListener('submit', async (e) => {
  e.preventDefault();
  
  const formData = {
    name: document.getElementById('name').value,
    email: document.getElementById('email').value,
    message: document.getElementById('message').value,
  };

  // Submit to your backend first
  const response = await fetch('/api/contact', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(formData),
  });

  if (response.ok) {
    // Track in FormTrackr after successful submission
    trackFormSubmission(formData);
  }
});

Server-Side Example (Node.js)

Example of tracking from a Node.js server:

// Express.js example
const express = require('express');
const fetch = require('node-fetch');
const app = express();

app.use(express.json());

app.post('/api/contact', async (req, res) => {
  const { name, email, message } = req.body;

  // Process your form submission here
  // (e.g., save to database, send email, etc.)

  // Track in FormTrackr
  try {
    const trackingResponse = await fetch('https://formtrackr.app/api/collect', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        projectKey: process.env.FORMTRACKR_PROJECT_KEY,
        sessionId: req.body.sessionId || 'server-generated-session-id',
        pageUrl: req.body.pageUrl || req.headers.referer || 'unknown',
        formId: 'contact-form',
        formName: 'Contact Form',
        fields: [
          { name: 'name', value: name },
          { name: 'email', value: email },
          { name: 'message', value: message },
        ],
        contact: {
          name: name,
          email: email,
        },
        attribution: req.body.attribution || {},
        timestamp: new Date().toISOString(),
        userAgent: req.headers['user-agent'],
      }),
    });

    if (trackingResponse.ok) {
      const trackingData = await trackingResponse.json();
      console.log('Lead tracked:', trackingData.leadId);
    }
  } catch (error) {
    console.error('FormTrackr tracking error:', error);
    // Don't fail the request if tracking fails
  }

  res.json({ success: true });
});

app.listen(3000);

Best Practices

1. Track After Successful Submission

Always track form submissions after your form has been successfully processed. This prevents spam submissions from being tracked:

❌ Don't: Track before validation or submission

✅ Do: Track only after successful backend submission

1.1 Recommended: UseBasin + FormTrackr (Static Sites)

For static sites deployed on platforms like Netlify or Vercel, we recommend using UseBasin as your primary form backend and spam filter, and then tracking only successful submissions in FormTrackr. This pattern gives you robust spam protection, email notifications, and a reliable delivery layer (UseBasin), while FormTrackr focuses on lead attribution and analytics.

The recommended flow looks like this:

  • 1. Your frontend form submits to https://usebasin.com/f/YOUR_FORM_ID (or a Netlify / Vercel serverless function that then posts to UseBasin).
  • 2. If UseBasin accepts the request (response.ok or a success flag in your API), you treat the submission as valid.
  • 3. Only then do you call FormTrackr.track(...) (or /api/collect) with the same fields + attribution data.

This is the pattern used in our production template sites: UseBasin handles spam protection, validation, and delivery. FormTrackr receives only real, accepted submissions so your reporting stays clean.

2. Session ID Management

Generate a unique session ID when a user first visits your site and store it in localStorage. Reuse the same session ID for all form submissions during that session. This helps FormTrackr properly attribute multiple form submissions to the same user session.

3. Capture Attribution Data

Capture UTM parameters and GCLID from the URL when the user first lands on your site. Store this data and include it in all form submission tracking requests. This ensures proper attribution for Google Ads conversions.

4. Handle Errors Gracefully

FormTrackr tracking should never block your form submission. Wrap tracking calls in try-catch blocks and log errors without failing the user's form submission.

5. Include All Form Fields

Include all form fields in the fields array, not just contact information. This ensures complete data capture in your FormTrackr dashboard.

Rate Limiting

The API has rate limiting to prevent abuse:

  • 100 requests per minute per project key and IP address combination
  • If you exceed the rate limit, you'll receive a 429 Too Many Requests response
  • Rate limits are reset after the time window expires

Domain Allowlist

If you've configured a domain allowlist in your project settings, API requests must originate from an allowed domain. The API checks the Origin or Referer header to validate the request source.

For server-side requests, make sure to include the appropriate headers or configure your project to allow server-side API calls.

CORS (Cross-Origin Resource Sharing)

The API supports CORS and accepts requests from any origin. The API endpoint responds to OPTIONS preflight requests with appropriate CORS headers.

Field Reference

Required Fields

  • projectKey - Your project's unique identifier
  • sessionId - Unique session identifier (generate and store in localStorage)
  • pageUrl - Valid URL of the page where the form was submitted

Optional Fields

  • formId - HTML form element ID
  • formName - HTML form element name attribute
  • fields - Array of form field name/value pairs
  • contact - Object with name, email, and/or phone
  • attribution - UTM parameters, GCLID, referrer, etc.
  • timestamp - ISO 8601 timestamp (defaults to current time)
  • userAgent - User agent string (defaults to request header)

Troubleshooting

Invalid Project Key (400)

Make sure you're using the correct project key from your FormTrackr dashboard. Project keys are case-sensitive.

Domain Not Allowed (403)

If you've configured a domain allowlist, ensure your request originates from an allowed domain. Check the Origin or Referer header.

Rate Limit Exceeded (429)

You're sending too many requests. Wait a minute before retrying, or implement request throttling in your application.

Leads Not Appearing in Dashboard

Check the API response for the leadId. If you receive a success response, the lead was created. If it's not appearing in your dashboard, check:

  • You're viewing the correct project
  • Your browser cache is cleared
  • There are no filters applied in the leads view

Need Help?

If you need assistance with the API or have questions: