The API is OpenAI-compatible, not OpenAI-identical. This page is the complete list of differences — everything that could surprise a client written against OpenAI.
Endpoints that do not exist#
404, not a different shape
embeddings, moderations, batches, legacy completions, fine_tuning, assistants and vector_stores. An SDK will report these as unknown routes.Model ids#
Only upfyn-bala, upfyn-yuva and upfyn-rishi. OpenAI names are not aliased and return 400 unknown_model. If you are porting code, this is the one change you cannot skip.
Extra fields in responses#
We add fields rather than change existing ones, so a permissive client is unaffected. A strictly-typed one may hide them.
{"id": "chatcmpl-...","choices": [ ... ],"usage": {"prompt_tokens": 18,"completion_tokens": 42,"total_tokens": 60,"prompt_tokens_details": {"cached_tokens": 0,"cache_write_tokens": 0 // Upfyn addition}},"credits_used": 0.31, // Upfyn"credits_remaining": 4821.44, // Upfyn"request_id": "req_...", // Upfyn"session_id": null, // Upfyn"upfyn_tag": null // Upfyn}
- Python: read them from
response.model_extra. - TypeScript: cast, or read the raw response.
Behavioural differences#
| Difference | What happens |
|---|---|
max_tokens and max_completion_tokens are mutually exclusive | Sending both is 400 conflicting_parameters. Some SDK versions send both — send one. |
| Unknown body parameters are forwarded to the provider | A parameter OpenAI accepts may be rejected by the upstream, so the 400 you see can come from further down the chain. |
service_tier: "priority" on Rishi | 400 unsupported_parameter — that model has no priority tier. It works on Bala and Yuva. |
| Intermediate usage chunks are suppressed while streaming | You get exactly one usage chunk, at the end. Clients that sum every usage frame would otherwise double-count. |
| Speech rejects voice-cloning parameters | Refused with a 400 rather than silently ignored. |
| Speech-to-text can 404 | Those routes are feature-flagged. An SDK reads the 404 as "endpoint does not exist". |
| SSE comment keepalives | Frames beginning : appear while a model is thinking. Conformant parsers ignore them; a hand-rolled parser must skip any line that is not data:. |
Error types#
Some type values are ours: billing_error, auth_error, not_found, api_error. Branch on the HTTP status first — that is always meaningful — and on code second.
402 is the one to handle specially
OpenAI has no direct equivalent. It means out of credits, and no amount of retrying or re-authenticating will change that. An SDK's default retry policy will retry it; turn that off.Porting a checklist#
- Change
base_urltohttps://ai.upfyn.com/v1. - Change every model id.
- Set
max_retriesto 0 and handle retries yourself. - Remove any
embeddingsormoderationscalls — there is no substitute here. - Send only one of
max_tokens/max_completion_tokens. - Add
stream_options.include_usageif you track cost on streaming calls. - Add a
402branch.
Once those are done the rest of an OpenAI codebase generally runs unchanged. If something does not, it is worth telling us through Support — a compatibility gap we do not know about is a bug.
