Skip to main content

How to Handle API Errors: Status Codes and Error Objects

·APIScout Team
api errorshttp status codeserror handlingapi designbest practices

How to Handle API Errors: Status Codes and Error Objects

Bad error handling is the number one developer experience complaint about APIs. Generic "Something went wrong" messages waste debugging time. Missing status codes break client error handling. Inconsistent error formats require per-endpoint error parsing. Here's how to handle errors properly on both sides.

HTTP Status Codes: The Complete Guide

2xx — Success

CodeNameWhen to Use
200OKSuccessful GET, PUT, PATCH, DELETE with response body
201CreatedSuccessful POST that creates a resource (include Location header)
202AcceptedRequest accepted for async processing (not yet completed)
204No ContentSuccessful DELETE or PUT with no response body

4xx — Client Errors

CodeNameWhen to Use
400Bad RequestMalformed request (invalid JSON, missing required fields)
401UnauthorizedMissing or invalid authentication credentials
403ForbiddenAuthenticated but not authorized for this resource/action
404Not FoundResource doesn't exist at this URL
405Method Not AllowedHTTP method not supported for this endpoint
409ConflictResource state conflict (duplicate, version mismatch)
410GoneResource existed but has been permanently deleted
422Unprocessable EntityRequest is well-formed but semantically invalid
429Too Many RequestsRate limit exceeded (include Retry-After header)

5xx — Server Errors

CodeNameWhen to Use
500Internal Server ErrorUnexpected server failure (bug, unhandled exception)
502Bad GatewayUpstream service returned an invalid response
503Service UnavailableServer is temporarily overloaded or in maintenance
504Gateway TimeoutUpstream service didn't respond in time

The Minimum Set

If you only use a few, use these: 200, 201, 204, 400, 401, 403, 404, 409, 422, 429, 500, 503.

Error Response Format

The Standard Error Object

{
  "error": {
    "type": "validation_error",
    "code": "invalid_parameter",
    "message": "The 'email' field must be a valid email address.",
    "param": "email",
    "request_id": "req_abc123def456",
    "doc_url": "https://api.example.com/docs/errors#invalid_parameter"
  }
}

With Multiple Validation Errors

{
  "error": {
    "type": "validation_error",
    "message": "Request validation failed.",
    "errors": [
      {
        "field": "email",
        "code": "invalid_format",
        "message": "Must be a valid email address."
      },
      {
        "field": "password",
        "code": "too_short",
        "message": "Must be at least 8 characters.",
        "metadata": { "min_length": 8, "actual_length": 5 }
      }
    ],
    "request_id": "req_abc123"
  }
}

Essential Fields

FieldPurposeRequired
typeError category (validation_error, authentication_error, rate_limit_error)Yes
codeMachine-readable error code (invalid_parameter, not_found, rate_limited)Yes
messageHuman-readable explanationYes
request_idUnique ID for debugging and supportYes
param / fieldWhich parameter/field caused the errorFor validation errors
doc_urlLink to documentation about this errorRecommended
metadataAdditional context (limits, allowed values, etc.)Optional

How Top APIs Handle Errors

Stripe

{
  "error": {
    "type": "card_error",
    "code": "card_declined",
    "decline_code": "insufficient_funds",
    "message": "Your card has insufficient funds.",
    "param": "source",
    "charge": "ch_abc123"
  }
}

Stripe's errors include the relevant object ID (charge), decline codes, and the specific parameter that caused the error.

GitHub

{
  "message": "Validation Failed",
  "errors": [
    {
      "resource": "Issue",
      "field": "title",
      "code": "missing_field"
    }
  ],
  "documentation_url": "https://docs.github.com/rest"
}

GitHub includes the resource type and a documentation URL.

Twilio

{
  "code": 20003,
  "message": "Permission denied",
  "more_info": "https://www.twilio.com/docs/errors/20003",
  "status": 403
}

Twilio uses numeric error codes with a direct link to detailed error documentation.

Client-Side Error Handling

The Error Handling Hierarchy

  1. Network errors — no response received (timeout, DNS failure, connection refused)
  2. HTTP errors — response received with error status code
  3. Application errors — 200 response but with error in the body (some APIs do this)

Pattern: Retry Strategy

Error TypeShould Retry?Strategy
400 (Bad Request)NoFix the request
401 (Unauthorized)NoRe-authenticate
403 (Forbidden)NoCheck permissions
404 (Not Found)NoResource doesn't exist
409 (Conflict)MaybeRe-read and retry
429 (Rate Limited)YesWait Retry-After seconds
500 (Server Error)YesExponential backoff
502 (Bad Gateway)YesExponential backoff
503 (Unavailable)YesWait Retry-After or backoff
Network timeoutYesExponential backoff

Pattern: Error Classification

Classify errors into three categories for your application:

  1. Retryable — 429, 500, 502, 503, 504, network timeouts → retry with backoff
  2. Fixable — 400, 401, 422 → user can fix the input
  3. Terminal — 403, 404, 409 → can't be fixed by retrying or changing input

Common Mistakes

MistakeImpactFix
200 for everythingClients can't distinguish success/failure by status codeUse proper HTTP status codes
Plain text error messagesNo machine parsingJSON error objects
No request IDDebugging requires reproducing the issueInclude request_id in every error
Generic "Server Error"No debugging informationSpecific error codes and messages
Stack traces in productionSecurity vulnerability (leaks internals)Log server-side, return generic 500
Missing Retry-After on 429Clients retry immediately, making it worseAlways include Retry-After
Different error formats per endpointClient needs per-endpoint error handlingConsistent error schema

Designing API error handling? Explore API best practices and tools on APIScout — architecture guides, comparisons, and developer resources.

Comments