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 have
request_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.
| item | Simple API | Operational API |
|---|---|---|
| success response | return data | Includes schema and version |
| failure response | String or arbitrary JSON | standard error envelope |
| traceability | Log Search | request_id, trace_id |
| retry | Caller judgment | specify retryable |
| Restriction Policy | doesn't exist | rate limit header |
| Check status | /healthone | liveness/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.
| field | explanation |
|---|---|
error.code | Unique code retrievable from within the system |
error.message | Description that users or developers can understand |
error.retryable | Whether the client can retry |
error.details | Additional information for debugging. Excluding sensitive information |
request_id | single request identifier |
trace_id | distributed 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.
| API | Is Dempotency Required? | reason |
|---|---|---|
GET /answers/{id} | lowness | read request |
POST /questions | middle | Prevents duplicate creation of the same question |
POST /documents/{id}/index | height | Avoid duplicate indexes |
POST /payments | very high | Prevent 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.
| endpoint | purpose | Check for |
|---|---|---|
/livez | process survival | application process |
/readyz | Traffic can be received | DB, Redis, Queue, config |
/metrics | Indicator collection | Prometheus, 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
- OpenAPI Specification:https://swagger.io/specification/
- FastAPI Error Handling:https://fastapi.tiangolo.com/tutorial/handling-errors/
- W3C Trace Context:https://www.w3.org/TR/trace-context/
- Google SRE: Monitoring Distributed Systems:https://sre.google/sre-book/monitoring-distributed-systems/
- AWS Builders Library: Idempotent APIs:https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/
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 code | client behavior |
|---|---|
RATE_LIMITED | Retry after backoff |
UNAUTHORIZED_DOCUMENT | Access permission information |
CONTEXT_TOO_LARGE | Request to reduce input |
PROVIDER_TIMEOUT | Retry or fallback |

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