OpenAI-compatibele API: overstappen in drie regels
Wat OpenAI-compatibel echt betekent, de drie regels die je app naar een Europese API verhuizen, en een eerlijke lijst van wat niet meegaat.
Drie regels. Je richt de base_url van de client op de nieuwe aanbieder, verwisselt de API-key en vervangt de modelnaam door een tiernaam. Al het andere in een chat completions-call, de messages-array, streaming, tools, JSON-schema’s, blijft precies zoals het is, want OpenAI-compatibel betekent hetzelfde HTTP-contract implementeren, niet er alleen op lijken.
Het interessante van een migratie is niet de code. Het is wat niet meegaat: de endpoints die nog niet bestaan, het modelgedrag dat onder dezelfde prompt verschuift, en die ene foutcode die je waarschijnlijk nog nooit hebt afgehandeld.

Wat betekent “OpenAI-compatibel” eigenlijk?
Het gaat om het contract van één endpoint: POST /v1/chat/completions, met dezelfde request body en dezelfde vorm van de response, inclusief het server-sent events-formaat voor streaming. Dat contract werd de standaardinterface van de sector zoals de API van S3 de standaard werd voor objectopslag, en om dezelfde reden: alles was er al tegenaan geschreven.
De officiële SDK’s zijn dunne clients bovenop dat contract. In de Python-library van OpenAI is de base URL een argument van de constructor, dus hem ergens anders op richten is een ondersteunde handeling en geen hack. Je retrylogica, je streaminghandler en je typedefinities blijven allemaal werken.
Wat het niet betekent, is dat het hele platform dezelfde functies heeft. Compatibele diensten implementeren het chat-endpoint goed en lopen daarna uiteen. Daarom is de eerlijke versie van dit artikel vooral een lijst van verschillen.
from openai import OpenAIclient = OpenAI(api_key=os.environ["UNION_API_KEY"],base_url="https://api.europeanailab.eu/v1",)response = client.chat.completions.create(model="eu-standard",messages=[{"role": "user", "content": prompt}],stream=True,)
3 regels verschillen: de key, de base URL en een tiernaam in plaats van een modelnaam.
Al het andere in de call is hetzelfde contract: messages, streaming, tools, JSON-schema’s. Wat verandert, is niet de vorm van het request maar welk model het antwoord schrijft. Daarom gaat de checklist verderop over evaluatie en niet over code.
De drie regels in de praktijk
Dit is de hele wijziging in Python. De SDK’s voor Node, Go en .NET nemen dezelfde twee argumenten onder een andere naam.
from openai import OpenAI
client = OpenAI(
api_key=os.environ["UNION_API_KEY"],
base_url="https://api.europeanailab.eu/v1",
)
response = client.chat.completions.create(
model="eu-standard",
messages=[{"role": "user", "content": prompt}],
stream=True,
)
Leest je code de base URL al uit een omgevingsvariabele, en dat is het regelen waard vóór elke migratie, dan is dit een configwijziging en geen deploy van nieuwe code. Dat geeft je ook de goedkoopst denkbare rollback: zet hem terug.
Wat gaat mee, wat verandert, wat ontbreekt?
De tabel die je echt wilt hebben voordat je het werk inplant.
| Functie | Status | Wat je doet |
|---|---|---|
| Chat completions | Werkt ongewijzigd | Niets |
| Streaming (SSE) | Werkt ongewijzigd | Vraag het gebruik op in de stream, zodat de meting exact blijft |
| Function calling en tools | Zelfde vorm | Test opnieuw welke tool het model kiest, niet of het dat kan |
| Structured output (JSON-schema) | Zelfde vorm | Blijf aan jouw kant valideren, altijd |
| Systeemprompts | Werken | Reken op het bijstellen van formuleringen, niet van structuur |
| Modelnamen | Veranderen | Gebruik een tier: eu-lite, eu-standard, eu-long, eu-pro |
| Gebruik en kosten | Veranderen | Lees de response-headers in plaats van een dashboard |
| Embeddings, beeld, spraak | Ontbreken | Houd hiervoor je huidige aanbieder |
| Batch-API | Ontbreekt | Op de roadmap, nog niet uitgebracht |
| Stateful API in de stijl van Assistants | Ontbreekt | Niet gepland; houd state aan jouw kant |
Drie van die rijen zijn gaten. We publiceren ze als lijst op de homepage in plaats van je ze halverwege een migratie te laten ontdekken, want “compatibel” is een woord dat flink is opgerekt door diensten die het halve contract implementeren en het daarbij laten.
Waarom worden modelnamen tiernamen?
Je koopt een tier, geen model. eu-standard is het werkpaard, eu-long neemt een miljoen tokens context, eu-pro is de zware redeneerder, en elk ervan hoort bij een genoemd open-weight model dat met zijn licentie op de pagina over AI-transparantie staat.
De reden is verloop. Open-weight modellen worden om de paar maanden vervangen, en wie de modelnaam van vandaag hardcodeert in zijn applicatie, moet opnieuw deployen telkens als het veld beweegt. Een tier is een belofte over capaciteit en prijs die blijft staan als het model eronder wordt verwisseld. En als wij het verwisselen, zeggen we dat, in plaats van stilletjes te veranderen wat je code doet.
De licenties doen er ook toe, en ze zijn kort genoeg om te lezen. Het meeste wat we draaien valt onder Apache 2.0 of MIT. Daarom mogen dezelfde gewichten überhaupt legaal vanaf Europese hardware worden aangeboden.

Wat verandert in de antwoorden, niet in de API?
Dit is het deel dat teams verrast, dus trek er tijd voor uit. Een overstap naar een andere aanbieder is een modelwissel, en modellen verschillen op manieren die geen compatibiliteitslaag kan verbergen.
De opmaak verschuift. Een prompt die zo is afgesteld dat het ene model nette markdown oplevert, kan bij een ander een inleidend praatje opleveren. Meestal een fix van één regel, maar het blijft een fix.
Het oordeel over tool calls verschilt. De vorm van een function call is identiek; de bereidheid van het model om hem aan te roepen niet. Er zijn modellen die te gretig aanroepen en modellen die uit hun geheugen antwoorden.
Weigeringen verschillen. Sommige open-weight modellen ontwijken feitelijke vragen over politiek of geschiedenis. Wij behandelen dat als een defect en sturen de vraag één keer door naar een model zonder die blinde vlek, en in de header X-EU-Routed laten we weten dat we dat deden. Dat staat volledig beschreven op de pagina over AI-transparantie.
Het praktische antwoord is een testset. Houd twintig echte requests aan met de verwachte output, draai ze tegen beide aanbieders en lees de diffs zelf. Twintig is genoeg om regressies in opmaak en toolkeuze te vangen, en klein genoeg dat je het ook echt doet.
Kosten en metering na de overstap
Prijzen per token is overal hetzelfde idee, maar waar je de getallen afleest, verandert. Elke response vertelt wat het request kostte:
X-EU-Tokens-InenX-EU-Tokens-Out, de gemeten tokens.X-EU-Cost-EUR, wat er voor dit request van je saldo afging.X-EU-Balance-EUR, wat er over is.X-EU-Model-Tier, welke tier het request echt draaide.
Zo kunnen je eigen logs de kostenvraag beantwoorden per feature, per klant en per endpoint, zonder te wachten tot een maandfactuur het met je oneens is. De tarieven achter die getallen staan op de prijspagina, en hoe je in het algemeen over tokenkosten nadenkt, ook waarom output duurder is dan input, wordt uitgewerkt in wat een AI-API per token kost.
Eén gewoonte die je mee moet nemen: zet max_tokens op elke call. Het is de effectiefste kostenbeheersing in elke API met prijzen per token, en het is één regel.
Fouten, limieten en de 402 die je nog niet afhandelt
Je foutafhandeling dekt waarschijnlijk 429 en 500. Vooraf betaald tegoed voegt er één aan toe.
- 402, onvoldoende saldo. Het request wordt geweigerd met een link om op te laden, er wordt achteraf niets gefactureerd en je saldo zakt nooit onder nul. Behandel het als een authenticatiefout: stuur een alert en probeer het niet opnieuw in een loop.
- 429, rate limit of plafond. Respecteer
Retry-After. De plafonds stel je zelf in, per key, in euro’s per dag en per maand. - 502 en 503, upstream niet beschikbaar. Probeer opnieuw met backoff. Wat er tijdens een storing gebeurt, en de beperking die daarachter zit, namelijk dat we op één provider draaien, staat op de statuspagina.
- Mislukte requests zijn gratis. Kwam er geen bruikbaar antwoord terug, dan gaat er niets van je saldo af, en een request dat naar een zwakker model is omgeleid, wordt afgerekend tegen het laagste van de twee tarieven.
Het plafond voor nieuwe accounts overvalt mensen één keer: € 25 per dag in de eerste 48 uur, wat je saldo ook is. Het bestaat omdat een uitgelekte key op dag één de duurste fout is die een nieuwe integratie kan maken.
Maakt compatibiliteit je echt overdraagbaar?
Gedeeltelijk, en het loont om precies te zijn over welk deel. Het wire-contract is overdraagbaar: elke dienst die chat completions spreekt, kan achter dezelfde client worden ingewisseld. Dat alleen haalt de grofste vorm van lock-in weg, die waarbij vertrekken betekent dat je je integratielaag moet herschrijven.
Wat niet overdraagbaar is, is alles wat je bovenop een specifiek model hebt gebouwd. Prompts die je maandenlang hebt bijgeschaafd, few-shot-voorbeelden die zijn gekozen op de eigenaardigheden van één model, evaluatiesets die zijn gescoord tegen de outputstijl van één model, en elke drempel die je hebt ingesteld door dat model aan het werk te zien. Niets daarvan gaat netjes mee, en niets daarvan zie je in een architectuurdiagram.
Behandel het model dus als een dependency met een versie, niet als een constante. Bewaar prompts in bestanden in plaats van ze door de code te strooien, zet de testset in de repo en schrijf op waarom een drempel is wat hij is. Teams die dat doen, wisselen in een middag van aanbieder. Teams die dat niet doen, zijn een kwartaal bezig met uitzoeken waarvoor hun prompts compenseerden.
Checklist voor de migratie
Een halve dag voor een kleine service, een sprint voor iets met echt verkeer.
- Zet de base URL in de config als dat nog niet zo is. Alles hieronder wordt makkelijker zodra dat klopt.
- Kies een tier op basis van de workload, niet op naam. Classificatie en extractie horen op de goedkope tier; alleen zwaar redeneerwerk heeft de dure nodig.
- Bouw de testset. Twintig echte requests, verwachte output, gecommit in de repo.
- Draai beide aanbieders naast elkaar op een klein deel van het verkeer en vergelijk de resultaten.
- Koppel de kostenheaders aan wat je al logt.
- Voeg het 402-pad toe, en meteen ook een plafond per key.
- Houd de oude aanbieder twee weken geconfigureerd. Een migratie die je met één configwijziging kunt terugdraaien, is een migratie die je op een dinsdag kunt doen.
Tegen welke aanbieder je die evaluatie draait, is een vraag op zich, en de vier realistische opties worden vergeleken in Europese AI-aanbieders vergeleken. De eerste tien requests zijn gratis en je laadt minimaal € 10 op, dus de evaluatie kost minder dan de vergadering over de vraag of je hem moet doen. Het volledige contract, met curl en Python om te kopiëren, staat in de API-documentatie, en waar die requests stap voor stap worden verwerkt, lees je in waar je prompts naartoe gaan.
Veelgestelde vragen
Wat betekent OpenAI-compatibel?
Dat de dienst hetzelfde HTTP-contract implementeert als het chat completions-endpoint van OpenAI: dezelfde request body, dezelfde vorm van de response, hetzelfde streamingformaat. Omdat de officiële SDK’s dunne wrappers rond dat contract zijn, is er een andere base URL op zetten meestal alles wat nodig is.
Hoe stap ik over van de OpenAI-API naar een andere aanbieder?
Vervang de API-key, zet de base_url van de client op de nieuwe aanbieder en vervang de modelnaam. In de Python- en Node-SDK’s van OpenAI is de base URL een argument van de constructor, dus de wijziging is drie regels: de key, de base URL en de modelnaam.
Werken streaming en function calling nog na de overstap?
Bij Union AI wel. Streaming gebruikt hetzelfde server-sent events-formaat en kan aan het eind van de stream het tokengebruik teruggeven, en function calling en structured output met een JSON-schema houden dezelfde vorm van request en response. Wat verschilt, is het oordeel van het model over wanneer het een tool aanroept, en dat is het testen waard.
Welke functies van OpenAI ontbreken bij compatibele API’s?
Dat verschilt per aanbieder, en de eerlijke publiceren de lijst. Bij Union AI is versie 1 alleen het chat completions-contract: er zijn geen endpoints voor embeddings, beeld of spraak, geen batch-API en geen stateful API in de stijl van Assistants. Die staan benoemd op de roadmap in plaats van dat we doen alsof ze bestaan, dus controleer bij elke compatibele aanbieder dezelfde vier gaten.
Geven mijn prompts dezelfde antwoorden op een open-weight model?
Nee, en geen twee modellen doen dat ooit. Opmaakinstructies, het gedrag bij tool calls en de manier van weigeren verschuiven allemaal, dus behandel een overstap naar een andere aanbieder als een modelwissel: houd twintig echte requests aan als testset en vergelijk de output voordat je overschakelt.
Hoe vergelijk ik de kosten voordat ik overstap?
Neem een week echt verkeer, tel de input- en outputtokens apart en prijs beide kanten per miljoen tokens tegen hun lijsttarieven. Output domineert de meeste rekeningen, dus een aanbieder die op input goedkoop lijkt, kan bij een spraakzame workload toch duurder uitvallen.
Kan ik de SDK van OpenAI blijven gebruiken?
Ja. De officiële SDK’s van OpenAI werken ongewijzigd tegen elke dienst die hetzelfde contract implementeert, en daar draait de compatibiliteit om. Je houdt je retrylogica, je afhandeling van streaming en je types, en je verandert alleen waar de requests naartoe gaan.