DEV Community

FlareCanary
FlareCanary

Posted on

How to Monitor Third-Party APIs for Breaking Changes (5-Minute Setup)

Your app works. Tests pass. Deploys are green. Then a third-party API quietly renames a field, and your payments page breaks at 2 AM.

According to KushoAI's 2026 report, 41% of APIs experience undocumented schema changes within 30 days — and that jumps to 63% within 90 days. If you depend on third-party APIs, the question isn't if they'll change, but when.

Here's a real example: a team using a shipping API saw the provider move tracking_number into a nested shipment.tracking object. No changelog. No deprecation notice. Just a Monday morning incident.

In this tutorial, I'll show you how to set up continuous schema monitoring for any API you depend on — so you find out about changes before your users do.

What We're Building

We're setting up automated monitoring that:

  1. Polls your API endpoints on a schedule
  2. Learns the response structure automatically
  3. Detects when the structure changes
  4. Classifies changes by severity (info / warning / breaking)
  5. Alerts you via email or webhook

No OpenAPI spec required. No contract testing setup. Just point it at an endpoint.

Option 1: FlareCanary (Hosted, 2 Minutes)

The fastest path. Free for up to 5 endpoints.

Step 1: Sign up

Go to flarecanary.com and create an account. No credit card needed.

Step 2: Add an endpoint

From the dashboard, click Add Endpoint and enter:

  • URL: The API endpoint you want to monitor (e.g., https://api.example.com/v2/products)
  • Method: GET, POST, etc.
  • Headers: Add auth headers if needed (Authorization: Bearer sk-...)
  • Body: For POST/PUT/PATCH endpoints, add the request body

Step 3: Baseline learning

FlareCanary immediately polls the endpoint and records the response schema. This becomes your baseline — the "known good" structure.

You'll see the inferred schema in the dashboard:

response (object)
├── data (array)
   └── [0] (object)
       ├── id (number)
       ├── name (string)
       ├── price (number)
       └── tags (array)
           └── [0] (string)
├── meta (object)
   ├── page (number)
   └── total (number)
└── status (string)
Enter fullscreen mode Exit fullscreen mode

Step 4: Configure alerts

Set up where you want notifications:

  • Email: Get a formatted alert with the exact changes
  • Webhook: POST to Slack, PagerDuty, or your incident management tool

Step 5: Wait (or simulate)

FlareCanary polls on your plan's schedule. When a change is detected, you'll get an alert like:

BREAKING: response.data[].tracking_number removed
  Was: string
  Severity: Breaking — field removal will cause undefined access

WARNING: response.data[].shipment added (object)
  New field with properties: tracking (string), carrier (string)
  Severity: Info — new field, backward compatible

ACTION: The field tracking_number may have moved to
        shipment.tracking. Review the full diff in your dashboard.
Enter fullscreen mode Exit fullscreen mode

That's it. Two minutes, and you have continuous schema monitoring.

Option 2: DIY with a Cron Job (5 Minutes)

If you prefer self-hosted, here's a minimal Node.js script you can run on a schedule.

The Script

// schema-monitor.js
const fs = require('fs');
const path = require('path');

const BASELINE_DIR = './baselines';
const ENDPOINTS = [
  {
    name: 'products-api',
    url: 'https://api.example.com/v2/products',
    headers: { 'Authorization': 'Bearer ' + process.env.API_KEY }
  },
  // Add more endpoints here
];

// Infer a structural schema from a JSON value
function inferSchema(value) {
  if (value === null || value === undefined) return { type: 'null' };
  if (Array.isArray(value)) {
    return {
      type: 'array',
      items: value.length > 0 ? inferSchema(value[0]) : { type: 'unknown' }
    };
  }
  if (typeof value === 'object') {
    const properties = {};
    for (const [key, val] of Object.entries(value)) {
      properties[key] = inferSchema(val);
    }
    return { type: 'object', properties };
  }
  return { type: typeof value };
}

// Compare two schemas and return differences
function compareSchemas(baseline, current, path = '') {
  const diffs = [];

  if (baseline.type !== current.type) {
    diffs.push({
      path: path || 'root',
      change: 'type_changed',
      from: baseline.type,
      to: current.type,
      severity: 'breaking'
    });
    return diffs;
  }

  if (baseline.type === 'object' && current.type === 'object') {
    const baseKeys = Object.keys(baseline.properties || {});
    const currKeys = Object.keys(current.properties || {});

    // Removed fields
    for (const key of baseKeys) {
      if (!currKeys.includes(key)) {
        diffs.push({
          path: `${path}.${key}`,
          change: 'removed',
          was: baseline.properties[key].type,
          severity: 'breaking'
        });
      }
    }

    // Added fields
    for (const key of currKeys) {
      if (!baseKeys.includes(key)) {
        diffs.push({
          path: `${path}.${key}`,
          change: 'added',
          type: current.properties[key].type,
          severity: 'info'
        });
      }
    }

    // Recurse into shared fields
    for (const key of baseKeys.filter(k => currKeys.includes(k))) {
      diffs.push(
        ...compareSchemas(
          baseline.properties[key],
          current.properties[key],
          `${path}.${key}`
        )
      );
    }
  }

  if (baseline.type === 'array' && current.type === 'array') {
    if (baseline.items && current.items) {
      diffs.push(
        ...compareSchemas(baseline.items, current.items, `${path}[]`)
      );
    }
  }

  return diffs;
}

async function monitor() {
  if (!fs.existsSync(BASELINE_DIR)) fs.mkdirSync(BASELINE_DIR);

  for (const endpoint of ENDPOINTS) {
    try {
      const res = await fetch(endpoint.url, { headers: endpoint.headers });
      if (!res.ok) {
        console.error(`${endpoint.name}: HTTP ${res.status}`);
        continue;
      }

      const data = await res.json();
      const schema = inferSchema(data);
      const baselinePath = path.join(BASELINE_DIR, `${endpoint.name}.json`);

      if (!fs.existsSync(baselinePath)) {
        // First run — save baseline
        fs.writeFileSync(baselinePath, JSON.stringify(schema, null, 2));
        console.log(`${endpoint.name}: Baseline saved`);
        continue;
      }

      const baseline = JSON.parse(fs.readFileSync(baselinePath, 'utf-8'));
      const diffs = compareSchemas(baseline, schema);

      if (diffs.length === 0) {
        console.log(`${endpoint.name}: No drift detected`);
      } else {
        console.log(`${endpoint.name}: DRIFT DETECTED`);
        for (const diff of diffs) {
          const icon = diff.severity === 'breaking' ? '!!!'
                     : diff.severity === 'warning' ? '!!'
                     : 'i';
          console.log(`  [${icon}] ${diff.path}: ${diff.change}`);
        }
        // TODO: Send alert (Slack webhook, email, PagerDuty, etc.)
      }
    } catch (err) {
      console.error(`${endpoint.name}: ${err.message}`);
    }
  }
}

monitor();
Enter fullscreen mode Exit fullscreen mode

Run It

# Run once
node schema-monitor.js

# Or add to crontab (every 6 hours)
# crontab -e
0 */6 * * * cd /path/to/monitor && node schema-monitor.js >> monitor.log 2>&1
Enter fullscreen mode Exit fullscreen mode

Limitations of DIY

This gets you 80% of the way, but you'll eventually want:

  • Optional field tracking — fields that appear intermittently cause false positives
  • Auth token refresh — OAuth tokens expire; you need auto-renewal
  • Rate limit handling — respect API provider limits
  • Historical diffing — compare today's schema against last week, not just the baseline
  • Team visibility — a dashboard where anyone can see status

That's where a dedicated tool saves you maintenance time.

What Should You Monitor?

Prioritize by business impact:

Priority API Type Why
Critical Payment (Stripe, PayPal) Revenue impact
Critical Auth (Auth0, Firebase) User lockout
High Data you render (weather, maps) UI breaks
Medium Analytics, tracking Data integrity
Low Internal microservices You control both sides

Start with your payment and auth integrations. Those are the ones where a surprise change costs real money.

One More Thing: The AI Agent Problem

If you're using AI agents that make API calls (and in 2026, who isn't?), schema drift is even more dangerous. Your agent was trained or configured with a specific understanding of the API response structure. When that structure changes, the agent doesn't throw an error — it confidently processes incorrect data.

The Nordic APIs Reliability Report 2026 found that AI APIs (OpenAI, Anthropic) have the highest incident frequency across 215+ services. And those are the well-documented ones — smaller APIs that your agents depend on are even more likely to change without notice.

Monitoring the APIs your agents depend on is becoming table stakes. Whether you build it yourself or use a service, the cost of not monitoring is always higher than the cost of monitoring.


FlareCanary monitors your API endpoints for schema drift — free for up to 5 endpoints. No OpenAPI spec required.

Got questions? Drop them in the comments.

Top comments (0)