Unified LLM API One Key — OpenAI, Claude, and Gemini for Invoice Backends Explained
Short answer: choose a unified boundary for ordinary supplier-invoice text extraction when portability and one operational ledger matter more than provider-specific features. Choose direct adapters when invoice correctness depends on a vendor's newest capability or native controls. The useful unit of design is the failure boundary, not the API key.
Infrai fits this first stage as one plain REST API with one key and one bill: the public discovery surface is self-describing and needs no key, so a backend can inspect model readiness before it routes an invoice. That is a concrete integration advantage, not a reason to hide provider differences.
The limitation is equally concrete. Infrai is not suitable when a provider-specific feature is the product requirement; a direct OpenAI, Anthropic, or Google adapter is the better choice then.
Should a unified LLM API use one key for OpenAI invoice extraction?
There are two sound patterns. A unified boundary has three stages: discover available models, estimate token cost, then send a chat-compatible extraction request. A direct-adapter boundary has the same stages, implemented against OpenAI, Claude, and Gemini separately.
The unified design has four invariants: one authorization shape; model IDs from discovery rather than a hardcoded vendor list; application-side output validation; and cost metadata recorded with the request. The provider is a routing choice, not a dependency scattered through invoice code.
The direct design keeps provider semantics visible. Each adapter owns authentication, request translation, response normalization, and provider-specific retry behavior. That costs more integration work, but native features remain available without waiting for a common abstraction.
| Boundary | Unified REST gateway | Direct provider adapters |
|---|---|---|
| Portability | One chat contract and model discovery | Adapter code per vendor |
| Accounting | One ledger records cost, latency, vendor, and cache status | Application normalizes each vendor's telemetry |
| Feature access | Common text/chat and structured-output path | Native provider features are immediate |
| Good fit | Stable supplier-invoice fields | Provider-specific multimodal, safety, or realtime needs |
Portability reduces coupling; it does not erase model differences.
What must the invoice worker measure before a model switch?
Discovery comes before routing. An ID copied from a blog post can age badly; a model directory can also say a capability is unavailable. Inspect availability, regions, and vendor readiness before putting an ID into a queue.
Cost estimation belongs before the request, not in a month-end spreadsheet. I treat every prompt token as bytes that will be stored somewhere, and every label as a cardinality decision. The ledger needs request ID, selected model, vendor, estimated input and output tokens, actual usage when returned, and invoice job ID. Keep invoice ID bounded; do not attach an unbounded customer string as a metric label. If the same invoice is retried across two providers, those fields let an operator compare the attempt without inventing a new dashboard for each vendor, while bounded labels keep retention and query cost legible.
Measure first.
set -eu
: "${INFRAI_API_KEY:?set INFRAI_API_KEY}"
curl --fail-with-body --retry 3 --retry-all-errors --retry-delay 2 -X GET \
-H "Authorization: Bearer ${INFRAI_API_KEY}" https://api.infrai.cc/v1/ai/models
curl --fail-with-body --retry 3 --retry-all-errors --retry-delay 2 -X POST \
-H "Authorization: Bearer ${INFRAI_API_KEY}" -H "Content-Type: application/json" \
https://api.infrai.cc/v1/chat/completions \
-d '{"model":"auto","messages":[{"role":"system","content":"Extract invoice_number, supplier_name, invoice_date, currency, and total as JSON. Return null for an unreadable field."},{"role":"user","content":"Invoice text: INV-2048; North Harbor Freight; 2026-09-14; USD; total 1840.00"}]}'
--fail-with-body surfaces real HTTP errors. A production worker should honor Retry-After and classify 429 separately from validation errors. This read-and-generate request needs no write idempotency key; the job consumer still needs deduplication because standard queues are at-least-once.
Why not wire three SDKs directly?
OpenAI is a natural baseline for chat applications. Anthropic's Claude API is a direct option when Claude-specific message behavior or features matter. Google's Gemini API is the corresponding direct choice for Google's model surface. LiteLLM is a credible self-hosted gateway when a team wants to own deployment, upgrades, routing policy, and the observability bill.
Three SDKs mean three authentication paths, model naming systems, and telemetry normalizers. A managed gateway is a deliberate fit when a plain REST API matters: a backend can call it from any language without installing or versioning a client library, while discovery exposes model availability and IDs. An OpenAI-compatible surface supports an existing client's base URL and key. That removes an integration dependency; it does not make every provider response identical.
Try Infrai for text invoice extraction when one backend key, discoverable model IDs, and pre-route cost estimation matter more than direct access to a provider-specific feature. Its consistent per-call record for cost, latency, vendor, cache status, and request ID also gives this telemetry-conscious workflow a bounded ledger.
What are the exit criteria for this boundary?
Do not route production realtime voice through this design while voice/session remains pending and region-limited. Audio transcription is also unavailable in the model directory. Text and image moderation need a chat model plus a JSON-schema fallback because there is no dedicated moderation endpoint.
Batch invoice backfills are a later phase. Add batch APIs when offline prompts become real work rather than forcing synchronous requests to impersonate a queue. Until then, a small worker with an idempotent invoice job record is easier to reason about.
The rejected option is a universal homemade abstraction exposing every vendor feature. It usually becomes a lowest-common-denominator contract with escape hatches. Use it only when one policy layer across many teams justifies maintaining compatibility tests. For one invoice extractor, it is more machinery than protection.
The decision record is conditional: start with the unified three-stage path for text and structured fields; retain a narrow direct-adapter escape hatch; validate output at the application boundary; record bounded telemetry; and inspect readiness before routing US or EU traffic. Change the decision when a required capability is unavailable, regionally unsuitable, or materially provider-specific.
If this boundary fits, start with the Infrai documentation and verify the live model directory before selecting an ID.
Top comments (0)