API guidelines

Enterprise API Guidelines

API Style Guideline

Inspired by Zalando’s API Guidelines.

The requirement level keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” used in this document (case insensitive) are to be interpreted as described in RFC 2119.


A. API Specification and Documentation

MUST provide and publish API specifications using OpenAPI.
MUST create or update API documentation in the developer portal.
SHOULD provide comprehensive documentation, from straight-forward getting started examples to in-depth details on all request and response parameters.


B. REST Design

MUST use REST maturity level 2 (as per the Richardson Maturity Model).


C. HTTP Request Standards

MUST use HTTP methods correctly — comply with standardized HTTP semantics (see RFC-9110 Methods).
MUST fulfill common method properties (safety, idempotency, and cacheability).
MUST use official HTTP status codes correctly — follow RFC-9110 Status Codes.
SHOULD only use most common HTTP status codes.
MUST use most specific HTTP status codes.
MUST use EV- prefix for custom headers.
Example: EV-Quota-Limit
MAY consider supporting Idempotency-Key header for POST requests.


D. HTTP Response Standards

MUST return HTTP 201 Created for successful resource creation.
Include a Location header with the URI of the newly created resource.

HTTP 201 Created
Location: /v3/resources/1234567890abcdef
Content-Type: application/json
{
  "id": "1234567890abcdef"
}

MUST return HTTP 204 No Content for successful deletions.

HTTP 204 No Content

MUST return HTTP 400 Bad Request when the client sends invalid data.

HTTP 400 Bad Request
Content-Type: application/json
{
  "type": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.1",
  "title": "Bad Request",
  "status": 400,
  "detail": "Invalid request payload.",
  "traceId": "",
  "errors": [
    {
      "path": "field.name",
      "messages": ["Must not be empty"]
    }
  ]
}

MUST return HTTP 401 Unauthorized when authentication is missing or invalid.

HTTP 401 Unauthorized
Content-Type: application/json
{
  "type": "https://datatracker.ietf.org/doc/html/rfc7235#section-3.1",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Authentication required to access this resource."
}

MUST return HTTP 403 Forbidden when access is denied despite authentication.

HTTP 403 Forbidden
Content-Type: application/json
{
  "type": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.3",
  "title": "Forbidden",
  "status": 403,
  "detail": "User does not have access to this resource."
}

MUST return HTTP 404 Not Found when the requested resource does not exist.

HTTP 404 Not Found
Content-Type: application/json
{
  "type": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.4",
  "title": "Not Found",
  "status": 404,
  "detail": "Requested resource does not exist."
}

MUST return HTTP 429 Too Many Requests when rate limits are exceeded.
MAY include the Retry-After header.

HTTP 429 Too Many Requests
Retry-After: 60
Content-Type: application/json
{
  "type": "https://datatracker.ietf.org/doc/html/rfc6585#section-4",
  "title": "Too Many Requests",
  "status": 429,
  "detail": "Rate limit exceeded. Please retry later.",
  "traceId": ""
}

MUST return HTTP 500 Internal Server Error for unexpected server errors.

HTTP 500 Internal Server Error
Content-Type: application/json
{
  "type": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.6.1",
  "title": "Internal Server Error",
  "status": 500,
  "detail": "An unexpected error occurred on our side. Please try again later."
}

E. Resource Naming and URL Structure

MUST NOT use /api as base path.
MUST keep URLs verb-free.
MUST avoid actions — think about resources.
MUST use domain-specific resource names.
MUST pluralize resource names.
MUST use URL-friendly resource identifiers.
MUST use kebab-case for path segments.
MUST identify resources and sub-resources via path segments.
Example: /resources/{resource-id}/sub-resources/{sub-resource-id}
MAY consider using non-nested URLs for sub-resources.
SHOULD limit number of sub-resource levels (≤ 3).
MUST use snake_case for query parameters.
Example: /resources?filter_field_value=value
SHOULD pass lists in query parameters using comma-separated values.
Example: /scorecards?siren_numbers=1,2,3,4,5


F. Data and Format Standards

MUST use JSON as the payload data format.
SHOULD define and observe a general response format for all outcomes (success, error, batch).
MUST use standard media types (IANA).
MUST use OpenAPI-defined standard data formats.
MUST use standard date/time formats (RFC 3339).
Example: "2025-03-21T14:23:45Z"
SHOULD use UTC for time representation.
SHOULD use Accept-Language header for localization (when supported).
MUST use standard formats for country, language, and currency codes:
Country: ISO 3166-1-alpha-2
Language: ISO 639-1
Currency: ISO 4217
MUST use camelCase for property names.
SHOULD declare enum values using UPPER_SNAKE_CASE.
Exception: case-sensitive external standards like ISO 639-1.
MUST follow a common sorting behavior.
Use sort_field and sort_type (values: asc, desc)

GET /resources?sort_field=name&sort_type=asc

MUST support pagination.

GET /resources?page_size=100&page_number=1
{
  "pagination": {
    "currentPage": 1,
    "pageSize": 100,
    "totalItems": 1000,
    "hasNextPage": true
  },
  "data": [
    {...},
    {...}
  ]
}

G. Errors

MUST specify success and error responses.
Include actionable messages. Refer to section D for standard error formats and examples.


H. Security

MUST secure all endpoints.
MUST NOT expose stack traces in production.


I. Versioning and Compatibility

MUST not break backward compatibility.
SHOULD prefer backward-compatible extensions:
add only optional fields, avoid changing semantics, keep validation consistent.
SHOULD avoid versioning.
Prefer resource variants over breaking changes.
MUST use semantic versioning (SemVer 2.0.0).
MUST use URL versioning.
MUST reflect deprecation in OpenAPI specs.
SHOULD add Deprecation and Sunset headers to responses.