Problem Details for HTTP APIs

HTTP status codes alone carry limited information about API errors, and custom error formats differ across every service. Problem details solves this with a standard format for machine-readable error information in HTTP API responses, sending a structured JSON or XML object describing the error type, HTTP status, a human-readable summary, and details specific to the occurrence.

The format defines two media types: application/problem+json for JSON serialization and application/problem+xml for XML. JSON is the dominant format in practice.

Usage

HTTP status codes carry limited information. A 403 tells the client access is denied but does not explain why. Missing Authentication, an expired token, an IP block, and an account suspension all produce the same status code. Error bodies historically varied between APIs, forcing clients to write custom parsing logic for each service.

Problem details solve this by defining a consistent envelope for error responses. A single parser handles errors from any API using the format. Five standard members cover the fields every error response needs, and extension members allow APIs to include domain-specific data like retry timers, request IDs, or validation errors.

Adoption is broad. Spring Framework and ASP.NET Core include native support. The Zalando RESTful API Guidelines mandate the format for all error responses. Libraries exist for Python, Node.js, Go, PHP, Ruby, Rust, and Kotlin.

Members

Five standard members form the core of every problem details object. All five are optional. APIs include whichever members apply to the error at hand.

type

A URI reference identifying the problem category. The type member acts as a stable identifier for the class of error, not the specific occurrence. When the URI is web-locatable, the target document provides human-readable documentation about the problem.

A consumer uses the type URI as the problem's primary identifier and does not automatically dereference it, while a member whose JSON value has the wrong type is ignored. When absent or set to "about:blank", the problem has no additional semantics beyond the HTTP status code. The title member mirrors the status code reason phrase in this case.

Absolute URIs are recommended. Relative URIs resolve per standard URI resolution rules. Non-resolvable URI schemes (like tag:) are permitted for cases where dereferenceable documentation is impractical.

"type": "https://api.example.re/errors/rate-limit"

status

The HTTP status code for the error, expressed as an integer in the 100-599 range. This member is advisory. The actual HTTP status code in the response header is the authoritative value. Including status in the body makes the problem details object self-contained when logged, queued, or forwarded through systems where the HTTP status line is lost.

Generators must use the same status code in both the HTTP response header and the status member. A mismatch between the two suggests an intermediary (proxy, load balancer, or firewall) modified the response in transit.

"status": 429

title

A short, human-readable summary of the problem type. The title value stays consistent across occurrences of the same problem type. Clients display the title as a label, not as a detailed explanation. The title changes only for localization purposes, driven by Accept-Language content negotiation.

"title": "Rate limit exceeded"

detail

A human-readable explanation specific to this occurrence. Unlike title, the detail value changes per occurrence and describes what went wrong in this particular request. The value targets human readers. Clients are not expected to parse detail for machine-readable information. Machine-readable data belongs in extension members.

"detail": "Request quota of 1000/hour exhausted. Resets at 2026-03-11T15:00:00Z."

instance

A URI reference identifying the specific occurrence. The instance member enables correlation between the error response and server-side logs or tracing systems. The URI is either dereferenceable (returning additional details about the occurrence) or opaque (serving as a unique identifier only). When dereferenceable, the endpoint implements the same access controls as any other API endpoint.

"instance": "/logs/errors/abc123-def456"

Extension members

APIs add custom members beyond the five standard fields to carry domain-specific data. Clients ignore unrecognized extensions, allowing problem types to add fields over time without breaking existing consumers.

Extension member names conventionally start with a letter and consist of alphanumeric characters plus underscores, with a minimum length of three characters. These are recommendations, not strict requirements. For XML serialization compatibility, names also conform to the XML Name production rule.

Common patterns

Extension Purpose
retryable Whether the request is safe to retry
retry_after Seconds to wait before retrying
errors Array of field-level validation failures
trace_id Distributed tracing identifier
error_code Vendor-specific numeric error code
balance Remaining quota or account balance

Validation errors commonly use an errors array where each entry identifies the invalid field and a human-readable message:

{
  "type": "https://api.example.re/errors/validation",
  "title": "Validation failed",
  "status": 422,
  "detail": "Two fields failed validation.",
  "errors": [
    {
      "field": "email",
      "message": "Invalid email format"
    },
    {
      "field": "age",
      "message": "Must be a positive integer"
    }
  ]
}

An account balance problem using extensions to communicate both the current balance and the cost of the attempted operation:

{
  "type": "https://api.example.re/errors/out-of-credit",
  "title": "Insufficient credit",
  "status": 403,
  "detail": "Account balance is 30, cost is 50.",
  "instance": "/account/12345/msgs/abc",
  "balance": 30,
  "accounts": [
    "/account/12345",
    "/account/67890"
  ]
}

The about:blank default

When the type member is absent or set to "about:blank", the problem type carries no additional semantics beyond the HTTP status code. The title member mirrors the standard HTTP reason phrase for the given status code (e.g., "Not Found" for 404, "Too Many Requests" for 429).

The about:blank default is useful for APIs returning generic HTTP errors without defining custom problem types. A minimal problem details response for an unauthorized request:

{
  "type": "about:blank",
  "title": "Unauthorized",
  "status": 401
}

This is functionally equivalent to omitting the type member entirely. The IANA Problem Types Registry lists about:blank with the title "See HTTP Status Code" and no recommended HTTP status code. Spring Boot defaults to about:blank when generating problem details from built-in exceptions, while ASP.NET Core fills type with an RFC 9110 section URL instead.

Content negotiation

Clients signal preference for problem details using the Accept header. An API client requesting JSON error responses sends:

Accept: application/problem+json

The server returns problem details for error responses and the regular media type for success responses. This pattern integrates naturally with content negotiation, where the Accept header already drives format selection for successful responses. Problem details extend the same mechanism to errors.

Both application/problem+json and application/json are valid Accept values for requesting JSON problem details. The application/problem+json type is more specific and signals explicit awareness of the format. The +json structured syntax suffix means any JSON parser handles the response body even without understanding the problem details semantics.

XML serialization

The XML serialization uses application/problem+xml with the XML namespace urn:ietf:rfc:7807 (preserved from the earlier specification for backward compatibility). Each standard member maps to an XML element of the same name. Extension members map to additional elements within the same namespace. Extension arrays use <i> child elements.

<?xml version="1.0" encoding="UTF-8"?>
<problem xmlns="urn:ietf:rfc:7807">
  <type>https://api.example.re/errors/out-of-credit</type>
  <title>Insufficient credit</title>
  <status>403</status>
  <detail>Account balance is 30, cost is 50.</detail>
  <instance>/account/12345/msgs/abc</instance>
  <balance>30</balance>
  <accounts>
    <i>https://api.example.re/account/12345</i>
    <i>https://api.example.re/account/67890</i>
  </accounts>
</problem>

JSON is the dominant serialization on the web. XML serialization is more common in enterprise and SOAP-adjacent environments.

Framework support

The format has native support in the two largest server-side web frameworks and library support across every major language.

Spring Framework

Spring Framework 6.0 and Spring Boot 3.0 introduced the ProblemDetail class. All built-in Spring MVC exceptions implement the ErrorResponse interface, producing problem details responses automatically. Enabling the feature in Spring Boot:

spring.mvc.problemdetails.enabled=true

The ResponseEntityExceptionHandler base class maps all Spring MVC exceptions to problem details with application/problem+json as the response content type. Custom exceptions extend ErrorResponseException to carry domain-specific problem types and extensions. The framework supports internationalization of title and detail through MessageSource integration.

ASP.NET Core

ASP.NET Core provides the ProblemDetails and ValidationProblemDetails classes. The AddProblemDetails() service registration enables automatic problem details generation across exception handling, status code pages, and developer exception page middleware.

builder.Services.AddProblemDetails();
app.UseExceptionHandler();
app.UseStatusCodePages();

The IProblemDetailsService and ProblemDetailsFactory allow customization of the generated responses, including adding extension members and custom type URIs. The [ApiController] attribute enables automatic problem details for error status codes.

Other frameworks

Framework Language Support type
Quarkus Java quarkus-resteasy-problem extension
Micronaut Java micronaut-problem-json module
Huma Go Built-in ErrorModel struct
NestJS TypeScript nest-problem-details-filter package
Rails Ruby problem_details-rails gem
Symfony PHP problem-details-symfony-bundle
FastAPI Python fastapi-problem-details plugin
Axum Rust problem_details crate
Ktor Kotlin kotlin-rfc9457-problem-details

Real-world adoption

Zalando RESTful API Guidelines

Zalando's RESTful API Guidelines mandate problem details for all error responses across the organization. Every API endpoint returns application/problem+json for 4xx and 5xx responses. Zalando propagates a flow ID for distributed tracing through the X-Flow-ID header rather than a problem-details extension. Zalando tends to use non-resolvable URIs for type, relying on OpenAPI documentation to define problem types instead.

IANA Problem Types Registry

IANA maintains a registry of common problem type URIs available for reuse across APIs. The registry uses a Specification Required registration policy, meaning each entry references a stable, freely available specification. Vendor-specific and deployment-specific values are not eligible.

The registry currently contains six entries:

Type URI Title Status
about:blank See HTTP Status Code N/A
...#date Date Not Acceptable 400
...#ohttp-key Oblivious HTTP key configuration not acceptable 400
...#digest-unsupported-algorithms Unsupported Hashing Algorithms 400
...#digest-invalid-values Invalid Digest Values 400
...#digest-mismatched-values Mismatched Digest Values 400

The three digest entries cover integrity failures around the Content-Digest and Repr-Digest header fields. The registry stays small by design. Most problem types are specific to individual APIs or organizations. The registry targets problems common across multiple independent specifications.

History

The original specification defining problem details was published in 2016, establishing the application/problem+json and application/problem+xml media types along with the five standard members. The 2023 revision obsoleted the original with three additions:

  • An IANA Problem Types Registry for common, reusable problem type URIs
  • Guidance for handling multiple problems of different types in a single response (recommend the most relevant or urgent)
  • Guidance for using non-dereferenceable type URIs (e.g., tag: scheme URIs)

The revision introduced no breaking changes. The XML namespace (urn:ietf:rfc:7807) was deliberately preserved for backward compatibility. Existing implementations continue to work without modification.

Example

API validation error

A POST request to an API endpoint fails input validation. The server returns 422 Unprocessable Content with problem details describing each invalid field.

Request

POST /api/users HTTP/1.1
Host: api.example.re
Content-Type: application/json
Accept: application/problem+json

{"email": "not-an-email", "age": -5}

Response

HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json
{
  "type": "https://api.example.re/errors/validation",
  "title": "Validation failed",
  "status": 422,
  "detail": "Two fields failed validation.",
  "instance": "/logs/errors/7f8a9b0c",
  "errors": [
    {
      "field": "email",
      "message": "Invalid email format"
    },
    {
      "field": "age",
      "message": "Must be a positive integer"
    }
  ]
}

The type URI identifies this as a validation error. The errors extension array lists each invalid field with a specific message, enabling the client to highlight individual form fields.

Rate limiting

A client exceeds the API rate limit. The server returns 429 Too Many Requests with extension members indicating retry timing.

Request

GET /api/data HTTP/1.1
Host: api.example.re
Accept: application/problem+json

Response

HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
Retry-After: 30
{
  "type": "https://api.example.re/errors/rate-limit",
  "title": "Rate limit exceeded",
  "status": 429,
  "detail": "Request quota of 1000/hour exhausted.",
  "retryable": true,
  "retry_after": 30
}

The retryable extension tells the client the request is safe to retry. The retry_after value mirrors the Retry-After response header, making the problem details object self-contained for logging and asynchronous processing.

Minimal about:blank response

A server returns a minimal problem details response using the about:blank default when no custom problem type applies to the error.

Response

HTTP/1.1 404 Not Found
Content-Type: application/problem+json
{
  "type": "about:blank",
  "title": "Not Found",
  "status": 404
}

The about:blank type signals no additional semantics beyond the 404 status code. The title mirrors the standard HTTP reason phrase.

See also

Last updated: August 18, 2026