Direct answer: The API contract for online feature retrieval needs to specify entity IDs and requested feature names in, values and per-feature staleness metadata out, with authentication scoped per caller, a short client-side timeout with a defined retry policy, and response-embedded versioning so a caller can detect when it received a stale or mismatched feature definition.
Structured elaboration:
- Request/response contract. Request: entity ID(s) (support batch lookups since most inference paths need many features at once), a list of feature names or a named feature group/vector, and an optional as-of timestamp for offline/backtesting parity. Response: a map of feature name to value, plus per-feature metadata (last-computed timestamp, feature definition version) so the caller can decide whether a feature is too stale to use.
- Authentication and authorization. Service-to-service auth via short-lived tokens (mTLS or a token issued by a service-identity system) rather than static API keys; authorization scoped per feature group so a caller only sees features it is entitled to (relevant for PII (personally identifiable information)-sensitive features).
- Caching strategy. The API itself should be cache-friendly: deterministic request shape, and response headers or fields that state a TTL (time-to-live) so a client-side cache (or CDN-style edge cache for less dynamic features) knows how long it may reuse a value.
- Timeout and retry semantics. A tight client-side timeout (a few milliseconds under the overall inference budget) with at most one retry to a different replica, not a retry storm; on timeout, the client contract should specify a documented fallback (return a default/null feature with a flag, rather than blocking the whole inference request).
- Feature versioning and metadata in the response. Include the feature definition's version or hash in the response so a caller doing an online/offline consistency check can confirm the online value was produced by the same transformation logic as the training data.
Worked example: A request for {"entity_id": "u_123", "features": ["f_recency_7d", "f_avg_spend_30d"]} returns {"f_recency_7d": {"value": 3.2, "computed_at": "2026-07-23T10:15:02Z", "version": "v3"}, "f_avg_spend_30d": {"value": null, "computed_at": null, "version": "v3", "status": "missing"}}; the caller's contract with the platform says a missing feature is returned explicitly as null with a status field rather than the whole request failing, so a single missing feature does not take down inference for the entity.
Trade-offs & pitfalls: Returning rich per-feature metadata on every response adds payload size and marshaling cost at high QPS (queries per second); a common mitigation is to make metadata optional via a request flag, so hot-path inference can skip it and only debugging/monitoring calls request it. A retry policy that is too aggressive (multiple retries with no backoff) can turn a single slow shard into a cascading overload; the contract should specify a hard retry budget and prefer failing fast with a fallback over retrying into an already-struggling backend. Embedding a feature-definition version in every response is easy to forget to check on the consumer side, so the value it provides (catching a schema mismatch) only materializes if the platform also builds tooling that actively surfaces version mismatches rather than leaving it to each consumer to remember to compare.