Designing Production-Ready APIs
메뉴

AI Backend

Designing Production-Ready APIs

Operational APIs start with failure contracts, traceability, and repeatability.

Designing Production-Ready APIs hero image

Failure responses and tracking IDs come before endpoints

This article summarizes how to design the LLM service's API in an operable form.

When you think of API design, URLs and HTTP methods usually come to mind. First decide on endpoints such asPOST /chatandGET /documents/{id}. However, from an operational perspective, there are things you need to decide before endpoints.

This is a failure response. Tracking ID. Whether retry is possible. Rate limit policy. It is based on health check and readiness.

Analysis date: 2026-05-12 Practice standard environment: FastAPI, Pydantic, PostgreSQL, OpenTelemetry Key reference materials: OpenAPI Specification, FastAPI Error Handling, W3C Trace Context, Google SRE Fact checking of official documents and operational interpretation of this article are separated in the main text.

Key takeaways

  • API contracts include not only success responses but also failure responses.
  • All requests must haverequest_idortrace_id.
  • The LLM API must document retry, timeout, and idempotency.
  • Health checks should separate process survival verification and dependency readiness.
  • The API document is not just a front-end specification, but also a document for operators and failure responders.

1. What is an operational API?

An operational API is not an API that only works well under normal circumstances. This is an API that allows the cause of a failure to be tracked, the caller to determine whether to retry, and the operator to check the status with indicators.

itemSimple APIOperational API
success responsereturn dataIncludes schema and version
failure responseString or arbitrary JSONstandard error envelope
traceabilityLog Searchrequest_id, trace_id
retryCaller judgmentspecify retryable
Restriction Policydoesn't existrate limit header
Check status/healthoneliveness/readiness separation

In LLM services, this difference is more important. This is because model call failures, schema validation failures, rate limits, and long delays often occur.

2. Failure responses come before success responses.

Most APIs, which make it difficult to respond to failures in practice, have different failure responses.

// This is an example JSON structure.{  "message": "failed"}

The cause cannot be determined from this response. I can't tell what the request is, if it's retryable, if it was sent incorrectly by the user, or if it's an internal server issue.

An operational failure response must include, at a minimum, the information below:

// This is an example JSON structure.{  "error": {    "code": "LLM_SCHEMA_VALIDATION_FAILED",    "message": "Model response did not match the expected schema.",    "retryable": true,    "details": {      "schema_version": "answer.v1"    }  },  "request_id": "req_01HX...",  "trace_id": "0af7651916cd43dd8448eb211c80319c"}

3. Error Response Envelope Design

The recommended error envelope structure is as follows.

fieldexplanation
error.codeUnique code retrievable from within the system
error.messageDescription that users or developers can understand
error.retryableWhether the client can retry
error.detailsAdditional information for debugging. Excluding sensitive information
request_idsingle request identifier
trace_iddistributed tracking identifier

One thing to be careful of is not to include the original text of the prompt, personal information, API key, or full text of internal documents indetails.

Security Caution LLM requests/responses may contain sensitive documents. You must first set a masking policy to avoid including the original text of prompts, document chunks, and user inputs in failure responses and logs.

4. trace ID and Request ID design

request_idis a single request identifier inside the service.trace_idis a distributed tracking identifier that cuts across multiple services and external calls.

# This is an example.Client→ API Server span→ Retrieval span→ LLM Provider span→ Validation span→ DB Save span

In the LLM service, a request is divided into several subtasks. So you need to connect a trace to see “where it’s slow.”

An example response header is as follows:

# This is an example.X-Request-Id: req_01HXABCtraceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01

5. API that requires idempotency key

Not all APIs require an idempotency key. However, it is required for APIs that change state.

APIIs Dempotency Required?reason
GET /answers/{id}lownessread request
POST /questionsmiddlePrevents duplicate creation of the same question
POST /documents/{id}/indexheightAvoid duplicate indexes
POST /paymentsvery highPrevent duplicate payments

The Document Index API is a prime example. If the same document is indexed multiple times, vector rows may be duplicated and search quality may decrease.

# This is an example.POST /documents/doc_123/indexIdempotency-Key: doc_123:index:v4

The server stores this key in the task table, and returns the existing task status when the same key comes again.

6. Rate limit response design

The LLM API has both cost and provider limitations. Therefore, the rate limit is not just a security device but also a cost control device.

# This is an example.HTTP/1.1 429 Too Many RequestsX-RateLimit-Limit: 100X-RateLimit-Remaining: 0X-RateLimit-Reset: <unix_timestamp>Retry-After: 30

The 429 response must also include request_id and error code.

// This is an example JSON structure.{  "error": {    "code": "RATE_LIMIT_EXCEEDED",    "message": "Too many requests. Retry after 30 seconds.",    "retryable": true  },  "request_id": "req_01HX..."}

7. Health Check and Readiness

Having just one/healthis not enough.

endpointpurposeCheck for
/livezprocess survivalapplication process
/readyzTraffic can be receivedDB, Redis, Queue, config
/metricsIndicator collectionPrometheus, etc.

You should be careful about whether to add the LLM provider status to readiness. If the entire service is excluded from traffic just because the external model API is temporarily slow, the failure may actually increase. Usually, the provider status is viewed as a separate metric and a fallback policy is set.

8. OpenAPI documentation standards

Your API documentation should include failure responses as well as success responses.

# This is an example.responses:'200':description: Answer created'400':description: Invalid request'429':description: Rate limit exceeded'500':description: Internal server error

Items to document include:

# This is an example.[ ] request schema[ ] response schema[ ] error schema[ ] authentication[ ] rate limit[ ] retry policy[ ] idempotency key[ ] timeout expectation[ ] trace/request ID header

9. Practical checklist

# This is an example.[ ] Do all API responses have a request_id?[ ] Is error.code a unique, searchable string?[ ] Is there a retryable field?[ ] 429 Is there a Retry-After in the response?[ ] Is idempotency key supported in the state change API?[ ] Have you separated liveness and readiness?[ ] Does the OpenAPI document include a failure response?[ ] Is there no sensitive information left in the log?

10. Q&A

Q1. Are both request_id and trace_id needed?

For small services, you can start with just request_id. However, if the service is divided into multiple components, trace_id is required. request_id is advantageous for tracing a single request, trace_id is advantageous for concatenating multiple spans.

Q2. Do I need to include an idempotency key in all APIs?

no. You can change the state and apply it first to the API with the possibility of retrying. APIs such as document indexing, payments, and external event handling are priorities.

Q3. Should LLM provider failure be considered a failure in readiness?

In most cases, it is better to view it as a separate provider health metric. Fallback, provider routing, and degraded responses may be safer than taking down the entire API server due to provider failure.

11. References and uncertainty

References

uncertainty

  • The actual error code scheme should align with your organization's API governance standards.
  • The detailed implementation of the trace ID header may vary depending on the observability stack used.

Practical application example: API that sets the failure response first

If you are creating an LLM answer generation API, it is better to decide on failure responses before success responses. This is because whether a retry is possible depends on whether the user uploaded a document that is too large, a document without permission, a provider timeout, or schema validation failure.

{  "error": {    "code": "CONTEXT_TOO_LARGE",    "message": "The input document has exceeded the current processing limit.",    "retryable": false,    "request_id": "req_123",    "trace_id": "trace_456"  }}

The comparison standard isretryable. Rate limits can be retried after a certain period of time, but no permissions cannot be resolved by retrying. If the context is exceeded, the document must be shortened or the work divided. Without this distinction, the client would display all failures with the same alert, and the user wouldn't know what to do.

failure codeclient behavior
RATE_LIMITEDRetry after backoff
UNAUTHORIZED_DOCUMENTAccess permission information
CONTEXT_TOO_LARGERequest to reduce input
PROVIDER_TIMEOUTRetry or fallback

댓글

GitHub 계정으로 로그인하면 댓글을 남길 수 있습니다. 댓글은 GitHub Discussions를 통해 운영됩니다.

TOP