Skip to main content
Understand error responses, status codes, and rate limits in Noorle APIs.

Error Response Format

REST API

{
  "error": {
    "code": "invalid_request",
    "message": "Missing required parameter: api_key",
    "details": {
      "parameter": "api_key",
      "reason": "required"
    }
  }
}
HTTP Status: 400 (varies by error type)

MCP/A2A (JSON-RPC 2.0)

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params",
    "data": {
      "expected": "string",
      "actual": "number"
    }
  }
}

HTTP Status Codes

StatusMeaningAction
200OKSuccess
201CreatedResource created successfully
204No ContentSuccess, no body returned
400Bad RequestFix your request (validation error)
401UnauthorizedInvalid or missing authentication
403ForbiddenAuthenticated but not authorized
404Not FoundResource doesn’t exist
409ConflictRequest conflicts with existing state
429Too Many RequestsRate limit exceeded, retry later
500Internal Server ErrorServer error, not your fault
502Bad GatewayUpstream service error
503Service UnavailableService temporarily down
504Gateway TimeoutRequest timed out

Common Error Codes

Authentication Errors (401)

{
  "error": {
    "code": "unauthorized",
    "message": "Invalid API key"
  }
}
Causes:
  • Missing X-API-Key or Authorization header
  • Invalid/expired token
  • Revoked API key
Fix:
  • Check API key format
  • Refresh OAuth token
  • Regenerate API key

Authorization Errors (403)

{
  "error": {
    "code": "forbidden",
    "message": "This API key does not have permission to upload plugins"
  }
}
Causes:
  • API key lacks required scope
  • User doesn’t have permission
  • Resource access denied
Fix:
  • Create API key with correct scopes
  • Contact account administrator
  • Check resource ownership

Validation Errors (400)

{
  "error": {
    "code": "validation_error",
    "message": "Invalid request body",
    "details": {
      "field": "memory_limit",
      "issue": "must be valid Kubernetes format (e.g., 512Mi)"
    }
  }
}
Causes:
  • Missing required fields
  • Invalid field types
  • Constraint violations
Fix:
  • Check required fields
  • Validate field format
  • Review API documentation

Resource Not Found (404)

{
  "error": {
    "code": "not_found",
    "message": "Plugin 'my-plugin' not found"
  }
}
Causes:
  • Plugin/agent doesn’t exist
  • Version not found
  • Resource deleted
Fix:
  • Verify resource ID/name
  • Check in correct account
  • List available resources

Rate Limit (429)

{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Too many requests. Retry after 60 seconds"
  }
}
Response headers:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1711123260
Fix:
  • Wait until X-RateLimit-Reset timestamp
  • Implement exponential backoff
  • Optimize batch requests

Server Error (500)

{
  "error": {
    "code": "internal_error",
    "message": "An internal error occurred",
    "request_id": "req_abc123xyz"
  }
}
Causes:
  • Server-side issue
  • Database error
  • External service failure
Fix:
  • Retry with exponential backoff
  • Report with request_id
  • Check status page

JSON-RPC Error Codes

Used by MCP and A2A protocols:
CodeMessageMeaning
-32700Parse errorInvalid JSON
-32600Invalid RequestBad method/params format
-32601Method not foundMethod doesn’t exist
-32602Invalid paramsWrong parameter types
-32603Internal errorServer-side error
Example:
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "Method not found: invalid_method"
  }
}

Rate Limiting

Limits

Default rate limits (per account):
EndpointLimitWindow
General API1,0001 hour
Plugin upload1001 hour
Tool calls10,0001 hour
Agent creation501 day

Rate Limit Headers

Every response includes:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1711123200
  • Limit: Total requests allowed per window
  • Remaining: Requests left in current window
  • Reset: Unix timestamp when limit resets

Handling Rate Limits

1. Check Headers
if (response.headers['x-ratelimit-remaining'] === '0') {
  const resetTime = response.headers['x-ratelimit-reset'];
  console.log(`Rate limit reset at: ${new Date(resetTime * 1000)}`);
}
2. Exponential Backoff
import time
import random

def call_with_retry(func, max_retries=3):
    for attempt in range(max_retries):
        try:
            return func()
        except RateLimitError as e:
            if attempt == max_retries - 1:
                raise
            wait_time = 2 ** attempt + random.random()
            print(f"Rate limited. Waiting {wait_time}s...")
            time.sleep(wait_time)
3. Batch Operations
# ❌ Slow: 100 individual requests
for plugin in plugins:
  curl api/plugin/$plugin

# ✅ Better: Batch request
curl -X POST api/plugins/batch \
  -d '{"ids": ["p1", "p2", ..., "p100"]}'

Error Handling Examples

Python

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

# Retry strategy
session = requests.Session()
retry = Retry(
    total=5,
    backoff_factor=1,
    status_forcelist=[429, 500, 502, 503, 504]
)
adapter = HTTPAdapter(max_retries=retry)
session.mount('https://', adapter)

try:
    resp = session.get(
        'https://api.noorle.com/v1/capabilities',
        headers={'X-API-Key': 'nk_...'},
        timeout=10
    )
    resp.raise_for_status()
except requests.exceptions.HTTPError as e:
    if e.response.status_code == 429:
        reset_time = e.response.headers['X-RateLimit-Reset']
        print(f"Rate limited until {reset_time}")
    else:
        print(f"Error: {e.response.json()}")
except requests.exceptions.RequestException as e:
    print(f"Connection error: {e}")

JavaScript

async function apiCall(url: string, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      const response = await fetch(url, {
        headers: { "X-API-Key": "nk_..." },
      });

      if (!response.ok) {
        if (response.status === 429) {
          const resetTime = response.headers.get("X-RateLimit-Reset");
          const waitTime = Math.max(
            1000,
            Number(resetTime) * 1000 - Date.now()
          );
          console.log(`Rate limited. Waiting ${waitTime}ms`);
          await new Promise((resolve) => setTimeout(resolve, waitTime));
          continue;
        }

        throw new Error(`HTTP ${response.status}`);
      }

      return response.json();
    } catch (error) {
      if (i === retries - 1) throw error;

      const backoff = Math.pow(2, i) * 1000;
      await new Promise((resolve) => setTimeout(resolve, backoff));
    }
  }
}

cURL with Retry

#!/bin/bash

function api_call() {
  local url=$1
  local max_retries=3
  local retry=0

  while [ $retry -lt $max_retries ]; do
    response=$(curl -s -w "\n%{http_code}" \
      -H "X-API-Key: nk_..." \
      "$url")

    http_code=$(echo "$response" | tail -n1)
    body=$(echo "$response" | head -n-1)

    if [ "$http_code" == "200" ]; then
      echo "$body"
      return 0
    elif [ "$http_code" == "429" ]; then
      reset_time=$(curl -s -I -H "X-API-Key: nk_..." "$url" | \
        grep "X-RateLimit-Reset" | awk '{print $2}')
      sleep $((reset_time - $(date +%s)))
      ((retry++))
    else
      echo "Error: $body"
      return 1
    fi
  done

  return 1
}

Debugging

Request ID

Every response includes a unique request ID for debugging:
curl -v https://api.noorle.com/v1/capabilities \
  -H "X-API-Key: nk_..."

# Response headers:
# X-Request-ID: req_abc123xyz
Report this ID when contacting support.

Verbose Logging

Enable debug logging:
# cURL
curl -v https://api.noorle.com/v1/capabilities

# Python
import logging
logging.basicConfig(level=logging.DEBUG)

# Node.js
process.env.DEBUG = 'noorle:*'

Health Check

Check API status:
curl https://api.noorle.com/health

# Response:
# {
#   "status": "healthy",
#   "version": "v1",
#   "timestamp": "2024-03-22T10:30:45Z"
# }

Status Page

Real-time status: https://status.noorle.com
  • Current incidents
  • Maintenance windows
  • Historical uptime
  • API performance metrics

Next Steps