Redirections
API Reference

Error Reference

Complete reference for all API error codes with resolution steps.

Error Reference

All API errors return JSON with two fields:

  • error - Human-readable error message
  • code - Machine-readable error code (SCREAMING_SNAKE_CASE)
{
  "error": "Human-readable error message",
  "code": "MACHINE_READABLE_CODE"
}

Use the code field in your application logic to handle specific error types programmatically.


Authentication Errors

MISSING_API_KEY

HTTP Status: 401 Unauthorized

Description: The X-API-Key header was not provided in the request.

Response:

{
  "error": "Unauthorized",
  "code": "MISSING_API_KEY"
}

Resolution:

  1. Include the X-API-Key header in your request
  2. Verify the header name is exactly X-API-Key (case-sensitive)
  3. Ensure your HTTP client is configured to send custom headers

Example Fix:

# Missing header (causes error)
curl "https://api.3xx.app/v1/lookup?project=<project_id>&path=/test"

# Correct request
curl "https://api.3xx.app/v1/lookup?project=<project_id>&path=/test" \
  -H "X-API-Key: redir_live_your_key_here"

INVALID_API_KEY

HTTP Status: 401 Unauthorized

Description: The provided API key is malformed, invalid, revoked, or does not belong to the specified project.

Response:

{
  "error": "Unauthorized",
  "code": "INVALID_API_KEY"
}

Resolution:

  1. Verify you copied the complete API key (format: redir_live_ + 32 characters)
  2. Check that the key hasn't been revoked in your project settings
  3. Confirm the key belongs to the project you're querying
  4. Generate a new API key if the original was lost or compromised

Common Causes:

  • Truncated API key (missing characters)
  • Using test key (redir_test_) in production
  • Key was revoked by team member
  • Wrong project ID in query parameter

Validation Errors

MISSING_PROJECT

HTTP Status: 400 Bad Request

Description: The required project query parameter was not provided.

Response:

{
  "error": "Missing project parameter",
  "code": "MISSING_PROJECT"
}

Resolution:

  1. Add ?project=YOUR_PROJECT_ID to your request URL
  2. Find your project ID in the Dashboard under project settings

Example Fix:

# Missing project parameter (causes error)
curl -H "X-API-Key: redir_live_your_key_here" \
  "https://api.3xx.app/v1/lookup?path=/test"

# Correct request
curl -H "X-API-Key: redir_live_your_key_here" \
  "https://api.3xx.app/v1/lookup?project=<project_id>&path=/test"

MISSING_PATH

HTTP Status: 400 Bad Request

Description: The required path query parameter was not provided (lookup endpoint only).

Response:

{
  "error": "Missing path parameter",
  "code": "MISSING_PATH"
}

Resolution:

  1. Add &path=/your/path to your request URL
  2. Ensure the path starts with / (forward slash)

Example Fix:

# Missing path parameter (causes error)
curl -H "X-API-Key: redir_live_your_key_here" \
  "https://api.3xx.app/v1/lookup?project=<project_id>"

# Correct request
curl -H "X-API-Key: redir_live_your_key_here" \
  "https://api.3xx.app/v1/lookup?project=<project_id>&path=/blog/old-post"

MISSING_FORMAT

HTTP Status: 400 Bad Request

Description: The required format query parameter was not provided (export endpoint only).

Response:

{
  "error": "Missing format parameter",
  "code": "MISSING_FORMAT"
}

Resolution:

  1. Add &format=FORMAT to your export request
  2. Choose from valid formats: csv, nginx, nginx-map-exact, nginx-map-prefix, haproxy, haproxy-map-exact, haproxy-map-prefix

Example Fix:

# Missing format parameter (causes error)
curl -H "X-API-Key: redir_live_your_key_here" \
  "https://api.3xx.app/v1/export?project=<project_id>"

# Correct request
curl -H "X-API-Key: redir_live_your_key_here" \
  "https://api.3xx.app/v1/export?project=<project_id>&format=csv"

INVALID_FORMAT

HTTP Status: 400 Bad Request

Description: The format parameter value is not one of the supported export formats.

Response:

{
  "error": "Invalid format. Use one of: csv, nginx, nginx-map-exact, nginx-map-prefix, haproxy, haproxy-map-exact, haproxy-map-prefix",
  "code": "INVALID_FORMAT"
}

Resolution:

  1. Check the format parameter for typos
  2. Use one of the 7 valid formats listed in the error message

Valid Formats:

  • csv - Comma-separated values
  • nginx - Nginx configuration
  • nginx-map-exact - Nginx map (exact matches)
  • nginx-map-prefix - Nginx map (prefix matches)
  • haproxy - HAProxy configuration
  • haproxy-map-exact - HAProxy map (exact matches)
  • haproxy-map-prefix - HAProxy map (prefix matches)

Example Fix:

# Invalid format (causes error)
curl -H "X-API-Key: redir_live_your_key_here" \
  "https://api.3xx.app/v1/export?project=<project_id>&format=apache"

# Correct request
curl -H "X-API-Key: redir_live_your_key_here" \
  "https://api.3xx.app/v1/export?project=<project_id>&format=nginx"

Rate Limiting Errors

RATE_LIMIT_EXCEEDED

HTTP Status: 429 Too Many Requests

Description: You have exceeded your per-minute rate limit for the current tier.

Response:

{
  "error": "Too Many Requests",
  "code": "RATE_LIMIT_EXCEEDED"
}

Response Headers:

Retry-After: 45
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1706745600

Rate Limits by Tier:

  • Free: 100 requests per minute
  • Pro: 1,000 requests per minute
  • Enterprise: Unlimited

Resolution:

  1. Wait for the time specified in Retry-After header (in seconds)
  2. Reduce request frequency to stay within limit
  3. Implement exponential backoff for retries
  4. Upgrade to a higher tier for increased limits

Example Retry Logic:

async function lookupWithRetry(path) {
  try {
    return await lookup(path);
  } catch (error) {
    if (error.code === 'RATE_LIMIT_EXCEEDED') {
      const retryAfter = error.retryAfter || 60;
      console.log(`Rate limited. Waiting ${retryAfter}s...`);
      await sleep(retryAfter * 1000);
      return await lookup(path);
    }
    throw error;
  }
}

Rate limits are enforced per API key. Using multiple keys from the same project shares the organization's limit.


QUOTA_EXCEEDED

HTTP Status: 429 Too Many Requests

Description: You have exhausted your monthly API quota for the current billing period.

Response:

{
  "error": "Monthly API Quota Exceeded",
  "code": "QUOTA_EXCEEDED"
}

Response Headers:

Retry-After: 2592000
X-Quota-Limit: 10000
X-Quota-Remaining: 0
X-Quota-Reset: 1709251200

Monthly Quotas by Tier:

  • Free: 10,000 requests per month
  • Pro: 1,000,000 requests per month
  • Enterprise: Unlimited

Resolution:

  1. Immediate: Upgrade your plan to increase quota
  2. Wait: Quota resets at the start of next billing period (see X-Quota-Reset header)
  3. Optimize: Cache responses client-side to reduce API calls

Tier Upgrade Options:

  • Free → Pro: 100x quota increase (10k → 1M requests/month)
  • Pro → Enterprise: Unlimited requests

Quota limits are cumulative across all API keys in your organization, not per-key.


General Errors

METHOD_NOT_ALLOWED

HTTP Status: 405 Method Not Allowed

Description: The API only supports GET requests. You sent a different HTTP method (POST, PUT, DELETE, etc.).

Response:

{
  "error": "Method Not Allowed",
  "code": "METHOD_NOT_ALLOWED"
}

Resolution:

  1. Change your request to use the GET method
  2. Verify your HTTP client is configured correctly

Example Fix:

# Wrong method (causes error)
curl -X POST "https://api.3xx.app/v1/lookup?project=<project_id>&path=/test" \
  -H "X-API-Key: redir_live_your_key_here"

# Correct method
curl -X GET "https://api.3xx.app/v1/lookup?project=<project_id>&path=/test" \
  -H "X-API-Key: redir_live_your_key_here"

NOT_FOUND

HTTP Status: 404 Not Found

Description: The requested endpoint does not exist, or (for exports) no deployment data is available.

Response:

{
  "error": "Not Found",
  "code": "NOT_FOUND"
}

Common Causes:

  1. Wrong endpoint path: Verify you're using /v1/lookup or /v1/export
  2. No deployment: Export endpoint requires at least one deployment
  3. Version not found: Requested version number doesn't exist

Resolution for Endpoint Not Found:

  • Check the URL path for typos
  • Verify you're using the correct base URL
  • Valid endpoints: /v1/lookup, /v1/export

Resolution for Export Not Found:

# First deploy your configuration in the Dashboard
# Then retry the export request

If requesting a specific version, verify the version number exists:

# List available versions in your Dashboard
# Or omit the version parameter to get latest:
curl -H "X-API-Key: redir_live_your_key_here" \
  "https://api.3xx.app/v1/export?project=<project_id>&format=csv"

Response Headers

When a 429 error occurs, the response includes special headers to help you handle rate limits and quotas:

Rate Limit Headers

Available on RATE_LIMIT_EXCEEDED errors:

HeaderDescription
Retry-AfterSeconds to wait before retrying
X-RateLimit-LimitMaximum requests per minute for your tier
X-RateLimit-RemainingRequests remaining (always 0 when limit exceeded)
X-RateLimit-ResetUnix timestamp when rate limit resets

Quota Headers

Available on QUOTA_EXCEEDED errors:

HeaderDescription
Retry-AfterSeconds until quota resets (typically ~30 days)
X-Quota-LimitMonthly quota for your tier
X-Quota-RemainingRequests remaining (always 0 when quota exceeded)
X-Quota-ResetUnix timestamp when quota resets (billing period end)

Error Handling Best Practices

Check Status Code First

Always check the HTTP status code before parsing the response body:

const response = await fetch(url, { headers });

if (response.status === 204) {
  // No redirect found - not an error
  return null;
}

if (!response.ok) {
  const error = await response.json();
  throw new Error(`API error: ${error.code}`);
}

return await response.json();

Handle Specific Error Codes

Switch on error codes for precise error handling:

try {
  const result = await lookupRedirect(path);
} catch (error) {
  switch (error.code) {
    case 'RATE_LIMIT_EXCEEDED':
      // Wait and retry
      await sleep(error.retryAfter * 1000);
      return await lookupRedirect(path);

    case 'QUOTA_EXCEEDED':
      // Notify user to upgrade
      showUpgradePrompt();
      break;

    case 'INVALID_API_KEY':
      // Credentials issue - log out user
      handleAuthFailure();
      break;

    default:
      // Unknown error
      console.error('API error:', error);
  }
}

Respect Retry-After Header

Always honor the Retry-After header on 429 responses:

import time
import requests

def lookup_with_retry(path, max_retries=3):
    for attempt in range(max_retries):
        response = requests.get(url, headers=headers)

        if response.status_code == 429:
            retry_after = int(response.headers.get('Retry-After', 60))
            print(f"Rate limited. Waiting {retry_after}s...")
            time.sleep(retry_after)
            continue

        return response

    raise Exception("Max retries exceeded")

See Also