Naar de inhoud
trending

Handleidingen

Eerste AI-API-aanroep in Python: stappenplan

Je eerste AI-aanroep in Python via OpenRouter. Van Windows-installatie en venv tot streaming, foutafhandeling en Node.js, inclusief geteste code.

Tekst: 8 min leestijd

👁 114 keer gelezen

Een eerste aanroep naar een AI-model in Python vraagt om amper tien regels code: je installeert de openai-bibliotheek, vult het endpoint en je sleutel in, stuurt een berichtenlijst en leest het antwoord uit. Hieronder loods ik je door het volledige traject: van de installatie van Python op Windows en een virtuele omgeving (venv) tot streaming, foutafhandeling en automatische retries. Elk script heb ik op 4 oktober 2026 uitgevoerd op Windows 11 met Python 3.13.15 via OpenRouter, met de daadwerkelijke uitvoer erbij. Dit is de derde stap in het leertraject AI-API vanaf nul.

Getest op (4 oktober 2026)

  • Windows 11 Pro, Python 3.13.15, bibliotheken: openai 3.24.0, python-dotenv 1.2.4, pydantic 2.13.5.
  • Node.js 26.10 met de bibliotheek openai 7.27.0 (JavaScript-versie onderaan het artikel).
  • Modeltoegang: OpenRouter, standaardmodel openai/gpt-6-luna (ongeveer 0,089 EUR / 0,447 EUR per miljoen tokens, oorspronkelijk 0,10 / 0,50 USD; omgerekend volgens de ECB-referentiekoers van 12 oktober 2026: 1 EUR = 1,1181 USD via ecb.europa.eu).
  • Totale kosten van alle testruns in dit artikel: circa 0,0045 EUR (0,005 USD).

Stap 1. Python installeren op Windows

  1. Ga naar python.org/downloads en download het huidige installatieprogramma voor Windows (Python 3.13 of nieuwer; de openai-bibliotheek vereist minimaal versie 3.10).
  2. Vink in het eerste installatiescherm nadrukkelijk Add python.exe to PATH aan. Zonder deze optie herkent je terminal het commando Python niet - veruit de meest voorkomende beginnersfout.
  3. Als je liever met de terminal werkt: winget install Python.Python.3.13.
  4. Open een nieuwe terminal (PowerShell of Windows Terminal) en voer uit: python --version. Je hoort nu direct het versienummer te zien en niet een venster van de Microsoft Store.

Opent de Microsoft Store alsnog in plaats van de versie? Ga dan in Windows naar Instellingen → Apps → Geavanceerde app-instellingen → Uitvoeringsaliassen voor apps en schakel de aliassen voor python.exe en python3.exe uit.

Stap 2. Een virtuele omgeving (venv) aanmaken

Een venv is een geïsoleerde werkomgeving voor de bibliotheken van een specifiek project. Hierdoor zorgt een pakket in project A niet voor conflicten in project B. Je richt dit eenmalig per project in:

mkdir mijn-api
cd mijn-api
python -m venv .venv
.venv\Scripts\Activate.ps1
pip install openai python-dotenv

Na activering verschijnt er (.venv) aan het begin van je terminalregel. Weigert PowerShell het activeringsscript uit te voeren? Draai dan eenmalig Set-ExecutionPolicy -Scope CurrentUser RemoteSigned. In Git Bash activeer je met source .venv/Scripts/activate, en op Linux of macOS gebruik je source .venv/bin/activate.

Bewaar je sleutel in een bestand genaamd .env in de hoofdmap van je project (als één regel: OPENROUTER_API_KEY=sk-or-v1-...). Hoe je deze aanmaakt en beveiligt, lees je in het artikel API-sleutels: hoe je ze aanvraagt en veilig bewaart.

Stap 3. De eerste API-aanroep

We gebruiken de officiële OpenAI-bibliotheek, maar verwijzen naar het basisadres van OpenRouter. De opbouw van verzoeken is identiek. Hierdoor heb je met één enkele sleutel toegang tot modellen van OpenAI, Anthropic, Google, DeepSeek en vele andere aanbieders. Overstappen naar een ander model is slechts een kwestie van een andere modelnaam invullen.

"""Eerste aanroep naar een AI-model via OpenRouter (OpenAI SDK)."""
import os
from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()  # laadt omgevingsvariabelen uit het .env-bestand in de huidige map

client = OpenAI(
    base_url="https://openrouter.ai/api/v1",
    api_key=os.environ["OPENROUTER_API_KEY"],
)

# Originele testprompt (vertaald uit het Pools):
# Systeemprompt: "Je antwoordt in het Pools, kort en bondig."
# Gebruikersvraag: "Leg in twee zinnen uit wat een API is."
antwoord = client.chat.completions.create(
    model=os.getenv("MODEL", "openai/gpt-6-luna"),
    messages=[
        {"role": "system", "content": "Je antwoordt in het Nederlands, kort en bondig."},
        {"role": "user", "content": "Leg in twee zinnen uit wat een API is."},
    ],
)

print(antwoord.choices[0].message.content)
print("---")
print("model:", antwoord.model)
print("invoertokens:", antwoord.usage.prompt_tokens)
print("uitvoertokens:", antwoord.usage.completion_tokens)
details = antwoord.usage.completion_tokens_details
if details and details.reasoning_tokens:
    print("  waarvan redeneertokens:", details.reasoning_tokens)
kosten = getattr(antwoord.usage, "cost", None)  # OpenRouter voegt kosten in USD toe
if kosten is not None:
    kosten_eur = kosten / 1.1181  # ECB-koers 12-10-2026: 1 EUR = 1.1181 USD
    print(f"kosten: ca. {kosten_eur:.6f} EUR ({kosten:.6f} USD)")

Wat gebeurt hier stap voor stap:

  • load_dotenv() leest de API-sleutel uit het .env-bestand in en plaatst deze in de omgevingsvariabelen;
  • OpenAI(base_url=..., api_key=...) initialiseert de client - zonder base_url zou hij rechtstreeks verbinding maken met de servers van OpenAI;
  • messages is de berichtenlijst: system bepaalt de instructies, user bevat je vraag (meer details vind je in het artikel over systeemprompts, temperatuur en contextvensters);
  • antwoord.choices[0].message.content bevat de gegenereerde tekst;
  • usage toont de tokenteller, waarbij OpenRouter het veld cost toevoegt met de kosten in dollars.

Hieronder staat het resultaat van de testrun op GPT-6 Luna. We testten destijds in het Pools; de gegenereerde tekst is hieronder vertaald naar het Nederlands:

[Vertaling van het oorspronkelijke Poolse testantwoord:]
Een API is een set regels en hulpprogramma's waarmee verschillende software met elkaar kan communiceren. Het bepaalt hoe je verzoeken verstuurt en gegevens of functies van een ander systeem ontvangt.
---
model: openai/gpt-6-luna
invoertokens: 40
uitvoertokens: 58
kosten: ca. 0.000030 EUR (0.000033 USD)

En dezelfde vraag verstuurd naar Gemini 3.8 Flash (waarbij alleen de variabele MODEL werd aangepast; test uitgevoerd in het Pools, hier vertaald):

[Vertaling van het oorspronkelijke Poolse testantwoord:]
Een API (Application Programming Interface) is een reeks regels waarmee verschillende programma's en systemen veilig met elkaar kunnen communiceren en gegevens kunnen uitwisselen. Het fungeert als tussenpersoon, waardoor de ene applicatie functies of bronnen van een andere kan gebruiken zonder de interne werking ervan te hoeven kennen.
---
model: google/gemini-3.8-flash
invoertokens: 29
uitvoertokens: 462
  waarvan redeneertokens: 390
kosten: ca. 0.001569 EUR (0.001754 USD)

Let op de verhoudingen: Gemini genereerde 462 uitvoertokens voor slechts twee zinnen antwoord, doordat 390 tokens opgingen aan “denkwerk” (reasoning tokens) vóór het formuleren van de respons. Voor deze redeneertokens betaal je hetzelfde tarief als voor gewone tekst. Daardoor kostte dit verzoek circa 0,00157 EUR (0,00175 USD), ruim 50 keer meer dan bij Luna. Kijk bij het analyseren van je uitgaven dus altijd naar usage en niet puur naar de zichtbare lengte van het antwoord. Lees hier meer over in de gids over AI-tokens en API-kosten.

Stap 4. Streaming: antwoorden direct op je scherm

Zonder streaming wacht je totdat het complete antwoord is berekend voordat je iets te zien krijgt. Met streaming arriveert de tekst stukje bij beetje in real-time, net zoals in de webinterface van ChatGPT. Dit is onmisbaar bij chatinterfaces en interactieve toepassingen.

"""Streaming: het antwoord verschijnt direct woord voor woord, zoals in ChatGPT."""
import os
from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()
client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=os.environ["OPENROUTER_API_KEY"])

# Vertaalde testprompt (vertaling van het Poolse origineel: "Geef 5 ideeën voor de naam van een kookblog. Alleen een lijst.")
stream = client.chat.completions.create(
    model=os.getenv("MODEL", "openai/gpt-6-luna"),
    messages=[{"role": "user", "content": "Geef 5 ideeën voor de naam van een kookblog. Alleen een lijst."}],
    stream=True,
    stream_options={"include_usage": True},  # het laatste chunk bevat het tokenoverzicht
)

for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
    if chunk.usage:  # arriveert helemaal aan het einde
        print(f"\n--- tokens: {chunk.usage.prompt_tokens} invoer, {chunk.usage.completion_tokens} uitvoer")
[Vertaling van de oorspronkelijke Poolse testuitvoer:]
1. Recht uit de Pan
2. Snufje Smaak
3. Koken met Verstand
4. Met Vork en Mes
5. De Culinaire Haven
--- tokens: 20 invoer, 439 uitvoer

Bij streaming wordt het tokenverbruik niet standaard meegezonden. Je vraagt dit expliciet op via de parameter stream_options={"include_usage": True}; de gegevens zitten vervolgens in het laatste datapakket verwerkt.

Stap 5. Foutafhandeling en retries

In productieomgevingen loopt een API-verzoek soms mis: een ongeldige sleutel, een typefout in de modelnaam, een overschreden snelheidslimiet (429) of een tijdelijke serverstoring bij de aanbieder (5xx). Hanteer hierbij deze vuistregel:

  • Fouten aan jouw kant (400, 401, 403, 404): niet automatisch herhalen. Een nieuw verzoek levert identiek dezelfde fout op; pas eerst je instellingen of code aan.
  • Tijdelijke netwerk- en serverfouten (429, 500, 502, 503, verbroken verbinding): opnieuw proberen met oplopende wachttijd (bijvoorbeeld 2 s, 4 s, 8 s met een willekeurige jitter), zodat meerdere scripts niet op exact hetzelfde moment de server overbelasten.
"""Foutafhandeling bij API-aanroepen: 401, 429, 5xx en exponential backoff retries."""
import os, sys, time, random
from dotenv import load_dotenv
import openai
from openai import OpenAI

load_dotenv()
client = OpenAI(
    # BASE_URL kan worden omgeleid naar de lokale mockserver uit testy/serwer_429.py
    base_url=os.getenv("BASE_URL", "https://openrouter.ai/api/v1"),
    api_key=os.environ.get("OPENROUTER_API_KEY", ""),
    max_retries=0,   # ingebouwde retries uitschakelen om eigen mechanisme te demonstreren
    timeout=60,      # seconden; voorkomt dat een verbroken verbinding oneindig blijft hangen
)


def verstuur_vraag(vraag, model, max_pogingen=4):
    for poging in range(1, max_pogingen + 1):
        try:
            r = client.chat.completions.create(model=model, messages=[{"role": "user", "content": vraag}])
            return r.choices[0].message.content
        except openai.AuthenticationError as e:      # 401: foutieve of ingetrokken sleutel
            sys.exit(f"401: controleer je API-sleutel ({e.message})")
        except openai.PermissionDeniedError as e:    # 403: geen toegang (bijv. regioblokkade of moderatie)
            sys.exit(f"403: toegang geweigerd ({e.message})")
        except openai.NotFoundError as e:            # 404: onbekend model of onjuiste URL
            sys.exit(f"404: model of eindpunt niet gevonden ({e.message})")
        except openai.BadRequestError as e:          # 400: foutieve syntax in verzoek
            sys.exit(f"400: verzoek ongeldig ({e.message})")
        except (openai.RateLimitError, openai.InternalServerError,
                openai.APIConnectionError, openai.APITimeoutError) as e:
            # 429 (te veel verzoeken / saldo ontoereikend), 5xx (serverstoring), netwerkfouten
            if poging == max_pogingen:
                raise
            wachttijd = 2 ** poging + random.random()   # 2, 4, 8 s + willekeurige marge
            status = getattr(e, "status_code", "netwerk")
            print(f"[poging {poging}] fout {status}, wacht {wachttijd:.1f} s...")
            time.sleep(wachttijd)
        except openai.APIStatusError as e:           # overige statuscodes, bijv. 402 (onvoldoende saldo op OpenRouter)
            sys.exit(f"{e.status_code}: {e.message}")


if __name__ == "__main__":
    gekozen_model = sys.argv[1] if len(sys.argv) > 1 else os.getenv("MODEL", "openai/gpt-6-luna")
    print(verstuur_vraag("Hoeveel is 17 * 23? Geef alleen het getal.", gekozen_model))

Vier verschillende scenario’s zijn getest. Een regulier verzoek:

391

Een ongeldige sleutel en een onbestaand model (merk op dat OpenRouter bij een foutief model status 400 teruggeeft in plaats van 404):

401: controleer je API-sleutel (Error code: 401 - {'error': {'message': 'User not found.', 'code': 401}})
400: verzoek ongeldig (Error code: 400 - {'error': {'message': 'openai/gpt-7-bestaat-niet is not a valid model ID', 'code': 400}})

Een 429-foutmelding vereist normaal gesproken een enorme lading gelijktijdige verzoeken om uitgelokt te worden. Daarom bootste een lokaal testscript dit na: de eerste twee verzoeken krijgen status 429 terug, en het derde verzoek slaagt. Het script merkt het verschil met een echte server niet:

"""Mock API-server: geeft twee keer code 429 en daarna een geldig antwoord.
Bedoeld om de retry-functionaliteit te testen zonder onnodig saldo te verbruiken.

Venster 1: python testy/serwer_429.py
Venster 2: set BASE_URL=http://127.0.0.1:8429/v1   (PowerShell: $env:BASE_URL="http://127.0.0.1:8429/v1")
           python 03_bledy_i_retry.py
"""
import json
from http.server import BaseHTTPRequestHandler, HTTPServer

teller = {"n": 0}


class Handler(BaseHTTPRequestHandler):
    def do_POST(self):
        self.rfile.read(int(self.headers.get("Content-Length", 0)))
        teller["n"] += 1
        if teller["n"] <= 2:
            status, inhoud = 429, {"error": {"message": "Rate limit exceeded (test)", "code": 429}}
        else:
            status, inhoud = 200, {
                "id": "test-1", "object": "chat.completion", "created": 0, "model": "testmodel",
                "choices": [{"index": 0, "finish_reason": "stop",
                             "message": {"role": "assistant", "content": "391"}}],
                "usage": {"prompt_tokens": 10, "completion_tokens": 1, "total_tokens": 11},
            }
        body = json.dumps(inhoud).encode()
        self.send_response(status)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)
        print(f"verzoek {teller['n']} -> {status}")


HTTPServer(("127.0.0.1", 8429), Handler).serve_forever()
[poging 1] fout 429, wacht 2.8 s...
[poging 2] fout 429, wacht 4.2 s...
391

Het retry-mechanisme werkt zoals verwacht: twee weigeringen, pauzes van respectievelijk 2,8 s en 4,2 s, en bij poging drie volgt de uitvoer. De openai-bibliotheek heeft standaard ook ingebouwde retries (standaard 2 pogingen via de parameter max_retries), die we hier tijdelijk hadden uitgeschakeld ter demonstratie. In productieprojecten volstaat doorgaans de ingebouwde optie max_retries=5.

Drie praktische tips uit eigen ervaring:

  • Stel altijd een time-out in. Standaard wacht de bibliotheek tot wel 10 minuten. Bij modellen die uitgebreid redeneren kan 60 seconden soms juist krap zijn; stem de time-out dus af op de usecase.
  • Registreer de kosten per aanroep. Door het veld usage.cost consequent weg te schrijven naar een logbestand, krijg je na een week een realistischer overzicht dan met welke rekentool dan ook.
  • Let op het verschil tussen een snelheidslimiet en saldotekort. Bij OpenAI geeft een overschreden budgetlimiet eveneens foutcode 429 (met code project_spend_limit_exceeded), wat niet vanzelf oplost met retries.

Dezelfde logica in JavaScript (Node.js)

De officiële openai-bibliotheek voor Node.js hanteert nagenoeg dezelfde naamgeving. Getest onder Node.js 26.10: installeer eerst met npm install openai dotenv (versies 7.27.0 en 18.0.5) en configureer "type": "module" in package.json. Het script demonstreert achtereenvolgens een standaardaanroep, streaming en foutopvang.

// Eerste aanroep in Node.js via OpenRouter: synchroon antwoord en streaming.
// Uitvoeren: npm install   en vervolgens   node eerste_aanroep.mjs
import "dotenv/config";          // leest .env uit de huidige werkmap
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://openrouter.ai/api/v1",
  apiKey: process.env.OPENROUTER_API_KEY,
});
const model = process.env.MODEL ?? "openai/gpt-6-luna";

// 1. Standaardverzoek (origineel getest in het Pools, hier vertaald)
const r = await client.chat.completions.create({
  model,
  messages: [
    { role: "system", content: "Je antwoordt in het Nederlands, kort." },
    { role: "user", content: "Wat is het verschil tussen JavaScript en Java? Eén zin." },
  ],
});
console.log(r.choices[0].message.content);
console.log(`--- ${r.usage.prompt_tokens} + ${r.usage.completion_tokens} tokens, kosten ${r.usage.cost} USD\n`);

// 2. Streaming (vertaling van het Poolse origineel: "Noem 3 Poolse rivieren. Alleen de namen gescheiden door komma's.")
const stream = await client.chat.completions.create({
  model,
  messages: [{ role: "user", content: "Noem 3 grote rivieren in Nederland of België. Alleen de namen gescheiden door komma's." }],
  stream: true,
});
for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
console.log();

// 3. Foutafhandeling: ongeldig model
try {
  await client.chat.completions.create({ model: "bestaat/niet", messages: [{ role: "user", content: "test" }] });
} catch (e) {
  if (e instanceof OpenAI.APIError) console.log(`Fout ${e.status}: ${e.message}`);
  else throw e;
}

Hieronder zie je de oorspronkelijke testuitvoer van dit script. De prompts en resultaten werden destijds in het Pools uitgevoerd en zijn hieronder naar het Nederlands vertaald (de Poolse rivieren Wisła, Odra en Warta uit het oorspronkelijke antwoord zijn hier vertaald als Wisla, Oder en Warta):

[Vertaling van het oorspronkelijke Poolse testresultaat:]
JavaScript is een dynamische taal die hoofdzakelijk in browsers en webapplicaties wordt gebruikt, terwijl Java een afzonderlijke, statisch getypeerde taal is die gecompileerd wordt om op de JVM te draaien.
--- 39 + 54 tokens, kosten 0.0000309 USD

Wisla, Oder, Warta
Fout 400: 400 bestaat/niet is not a valid model ID

De verschillen ten opzichte van Python zijn minimaal: baseURL in plaats van base_url, await voor asynchrone functies en for await bij streams. Bewaar je sleutel in Node.js eveneens strikt in .env en plaats deze nooit in frontendcode die in de browser zichtbaar is.

Voorbeeldcode downloaden

Alle voorbeeldcode uit deze handleiding (en de overige modules van het traject) is gebundeld in het starterspakket api-starter-archief, inclusief documentatie en een .env.example-sjabloon. Na het uitpakken hoef je enkel je eigen sleutel in te vullen en python 01_pierwsze_zapytanie.py te starten.

Volgende stappen

Veelgestelde vragen

Moet ik voor OpenAI-modellen altijd OpenRouter gebruiken?

Nee, dat hoeft niet. Beschik je over een officiële OpenAI-sleutel, laat dan de parameter base_url weg en roep het model aan zonder tussenvoegsel (bijv. gpt-6-luna). Directe verbindingen met OpenAI, Anthropic en Google worden behandeld in de handleiding over API-sleutels.

Waarom krijg ik de fout ‘ModuleNotFoundError: No module named openai’?

Je hebt het pakket geïnstalleerd in een andere Python-omgeving dan waarin je het script nu uitvoert. Activeer eerst je venv via .venv\Scripts\Activate.ps1 en installeer het opnieuw met pip, of voer het script expliciet uit via .venv\Scripts\python.

Wat betekent de fout ‘KeyError: OPENROUTER_API_KEY’?

Het script kan de omgevingsvariabele niet vinden. Controleer of het bestand exact de naam .env heeft (Windows voegt soms automatisch de extensie .txt toe), of het in dezelfde map staat als het script en of er geen spaties rond het gelijkteken staan.

Kies ik voor Chat Completions of de Responses API?

OpenAI adviseert voor gloednieuwe toepassingen tegenwoordig de Responses API. De beproefde Chat Completions-standaard werkt echter ongewijzigd en vormt nog altijd de universele norm voor platforms als OpenRouter, DeepSeek, Mistral en lokale engines zoals Ollama. Om die reden gebruiken we Chat Completions in deze basiscursus.

Wat kost het om met API’s te oefenen?

Vrijwel niets. Alle tests voor dit artikel kostten bij elkaar nog geen halve eurocent. Met gratis toegankelijke modellen op OpenRouter (herkenbaar aan de toevoeging :free) of de gratis tier van Google Gemini kun je bovendien volledig kosteloos experimenteren.

Google · Jouw bronnenHoger in Google - met één klikToevoegen aan voorkeursbronnen →

Bronnen en verificatiedatum

* Wisselkoersnotitie: USD-bedragen zijn indicatief omgerekend naar EUR tegen de referentiekoers van de Europese Centrale Bank van 12 oktober 2026 (1 EUR = 1,1181 USD), raadpleegbaar via de ECB-wisselkoersdata.

Kinetyra

// read next

More on this from HELDER

Helder

Helder

ADMINISTRATOR
lees ookai-toolsFLUX 3 Image getest: 4K, beeldbewerking en prijzenFLUX 3 Image maakt volgens mijn test echte 4K-beelden en plaatst objecten nauwkeurig met kaders. Bekijk de resultaten, prijzen en prompts.

Meest gelezen

  1. 1Anthropics biolab ontdekt nieuw enzym via bijna 950 Claude-agents
  2. 2Meta onthult sleutelhanger-apparaatje met AI-assistent Muse
  3. 3OpenAI richt wiskundeadviesgroep op na oplossen van meer dan 100 open problemen
// sitemap

Deze pagina in andere talen6

helder in cijfers 0teksten in archief0nieuwsberichten van de afgelopen 7 dagen0videomodellen in observatorium
× ‹ vergroting ›