Resources
Troubleshooting
API Error Reference

API Error Reference

This page covers the most common HTTP errors returned by the NAT API and how to resolve them.


401 Unauthorized — invalid or missing API key

Error body:

{
  "error": "INVALID_API_KEY",
  "message": "The API key provided is invalid or has been revoked.",
  "status": 401
}

Causes and fixes:

CauseFix
Wrong header name (Authorization: Bearer instead of X-API-Key)Use X-API-Key: nat_pk_...
Trailing whitespace or truncated keyCopy the full key from the dashboard
Key revoked after rotationUse the new key from Settings → API Keys
Key not yet provisioned (new signup)Wait a few seconds, then retry

Verify your key with a quick health check:

curl https://api.nat-testing.io/api/v1/usage \
  -H "X-API-Key: $NAT_API_KEY"

A 200 OK response confirms the key is valid. See Onboarding Issues for more detail.


402 Payment Required — quota exceeded

Error body:

{
  "error": "QUOTA_EXCEEDED",
  "message": "Monthly scan quota exhausted. Quota resets on 2025-02-01.",
  "quota_reset_date": "2025-02-01T00:00:00Z"
}

This error is returned when you attempt to start a new scan after exhausting your monthly quota. In-flight scans are not interrupted.

Resolution:

  1. Wait for automatic reset — quota resets on quota_reset_date (first day of the next billing period)
  2. Check remaining quotaGET /api/v1/usage returns scans_remaining and quota_reset_date
  3. Upgrade your plan — immediately increase your quota:
    curl -X POST https://api.nat-testing.io/api/v1/billing/checkout \
      -H "X-API-Key: $NAT_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "price_id": "price_pro_monthly",
        "success_url": "https://app.nat-testing.io/dashboard?checkout=success",
        "cancel_url": "https://app.nat-testing.io/dashboard"
      }'

See Billing & Plans for plan quotas and a comparison table.


429 Too Many Requests — rate limited

Error body:

{
  "error": "RATE_LIMITED",
  "message": "Too many requests. Please slow down and retry after 10 seconds.",
  "retry_after": 10
}

NAT enforces per-tenant rate limits to ensure fair use. The retry_after field (in seconds) indicates when you can safely retry.

Recommended retry pattern:

import time
import requests
 
def call_with_retry(url, headers, **kwargs):
    while True:
        response = requests.get(url, headers=headers, **kwargs)
        if response.status_code == 429:
            retry_after = int(response.json().get("retry_after", 10))
            print(f"Rate limited. Retrying in {retry_after}s...")
            time.sleep(retry_after)
            continue
        response.raise_for_status()
        return response
async function callWithRetry(url, options) {
  while (true) {
    const res = await fetch(url, options);
    if (res.status === 429) {
      const { retry_after = 10 } = await res.json();
      console.log(`Rate limited. Retrying in ${retry_after}s...`);
      await new Promise((r) => setTimeout(r, retry_after * 1000));
      continue;
    }
    return res;
  }
}

If you are running scans in a tight loop from CI/CD, add a short delay between scan submissions (time.sleep(1) or equivalent) to avoid triggering rate limits.


503 Service Unavailable — SYNC_NOT_CONFIGURED

Error body:

{
  "error": "SYNC_NOT_CONFIGURED",
  "message": "NAT_APP_SYNC_KEY is not configured. Set this environment variable to enable tenant synchronization.",
  "status": 503
}

Cause: The NAT_APP_SYNC_KEY environment variable is not set on the NAT engine server. This variable is required for tenant synchronization between the NAT app and the engine.

Fix (self-hosted deployments):

  1. Obtain the sync key from your NAT app admin dashboard or from your deployment configuration
  2. Set the environment variable:
    export NAT_APP_SYNC_KEY="your_sync_key_here"
    Or in your Docker Compose file:
    environment:
      - NAT_APP_SYNC_KEY=your_sync_key_here
  3. Restart the NAT engine server
⚠️

NAT_APP_SYNC_KEY is a server-side secret — never expose it in client-side code or version control.

For a full list of required environment variables, see the Self-Hosted Setup guide.


Webhook signature verification failures — 400 Bad Request

Symptom: Stripe webhooks return 400 Bad Request and events are not processed.

Cause: The Stripe-Signature header does not match the expected signature computed from STRIPE_WEBHOOK_SECRET.

Diagnosis steps:

  1. In the Stripe dashboard, go to Developers → Webhooks and click your endpoint
  2. Check the delivery log for 400 responses and review the error detail
  3. Verify the STRIPE_WEBHOOK_SECRET value matches exactly:
    # The secret starts with whsec_
    echo $STRIPE_WEBHOOK_SECRET
  4. For local development, use the Stripe CLI to forward webhooks (it handles signing automatically):
    stripe listen --forward-to localhost:8080/api/v1/billing/webhook

Common mistakes:

MistakeFix
Using the API key instead of the webhook signing secretCopy whsec_... from Stripe webhook endpoint page, not from API keys
Middleware consuming the raw request body before signature checkEnsure the webhook route reads the raw body before any body-parsing middleware
Clock skew greater than 5 minutesSync server time with NTP — Stripe rejects requests with timestamps >5 min old

See Billing Issues for more webhook debugging steps.


Related pages

Was this helpful?