To build custom applications with Large Language Models, we connect to them programmatically using developer APIs. Let's learn how to use the Google Gemini and OpenAI developer libraries.
Google provides the @google/generative-ai SDK to interact with the Gemini family of models. In Python, you initialize the client using GoogleGenerativeAI and request content from the fast, highly capable **gemini-2.5-flash** model:
from google import generativeai as genai
genai.configure(api_key="YOUR_API_KEY")
model = genai.GenerativeModel('gemini-2.5-flash')
response = model.generate_content("Explain APIs in one sentence.")
print(response.text)
The **openai** library is another developer standard. While APIs differ slightly, they share common parameters: passing a model name, setting system instructions, and defining a **temperature** (which controls the randomness of generation).
Whichever provider you use, a call is an HTTPS request carrying your API key, a model name, a list of messages and some settings. The response contains the generated text plus metadata such as token counts and a finish reason. The SDK is a thin convenience layer over that. Two details matter a lot in practice. First, the API is stateless: the model remembers nothing between calls, so to continue a conversation you resend the earlier messages every time. Second, you are billed and limited by tokens(roughly word fragments) for both input and output, so long histories cost more and eventually exceed the model's context window.
Chat-style APIs take a list of messages, each with a role. The common roles are a system or developer message for standing instructions, user for the person's input, and assistantfor earlier model replies. A multi-turn conversation is just that list growing over time. Here is the OpenAI Python SDK's Chat Completions style:
from openai import OpenAI
client = OpenAI() # reads OPENAI_API_KEY from the environment
messages = [
{"role": "system", "content": "You answer in one short sentence."},
{"role": "user", "content": "What is an API?"},
]
reply = client.chat.completions.create(
model="gpt-4o-mini", # use a model name that is current for your account
messages=messages,
temperature=0.2,
max_tokens=100,
)
text = reply.choices[0].message.content
print(text)
# To continue the conversation, append both sides and call again
messages.append({"role": "assistant", "content": text})
messages.append({"role": "user", "content": "Give a real-world example."})Model names change often, so always check the provider's current model list rather than copying a name from a tutorial. The Gemini SDK shown earlier is a different package, and Google has been moving to a newer unified google-genai package (from google import genai with genai.Client(...)), so check the current quickstart before starting a new project.
An API key is a password that spends money. Never hard-code it in source files, commit it to Git, or ship it in front-end JavaScript where any visitor can read it. Load it from an environment variable on the server and call the provider from your backend:
import os
api_key = os.environ.get("GEMINI_API_KEY")
if not api_key:
raise RuntimeError("Set GEMINI_API_KEY before running this script")Real API calls fail: networks drop, providers return rate-limit errors (HTTP 429) or temporary server errors (5xx), and requests that are too large are rejected. Retrying with exponential backoff is the standard answer: wait a little, retry, and double the wait each time, with a small random jitter so many clients do not retry in lockstep.
import random
import time
def call_with_retry(fn, max_attempts=5):
for attempt in range(max_attempts):
try:
return fn()
except Exception as err: # narrow this to your SDK's rate-limit/server errors
if attempt == max_attempts - 1:
raise
delay = (2 ** attempt) + random.random()
print(f"Attempt {attempt + 1} failed ({err}); retrying in {delay:.1f}s")
time.sleep(delay)
# result = call_with_retry(lambda: client.chat.completions.create(...))Retry only errors that can succeed later (rate limits, timeouts, server errors). Retrying an invalid request or a bad key just wastes time. Many SDKs also have built-in retry settings worth reading about.
A long answer can take many seconds to finish. Streaming returns the text in small chunks as it is generated, so a chat interface can show words immediately instead of a blank screen. Most SDKs expose it as a flag or a separate method that you iterate over. The trade-off is slightly more code for handling partial output and errors that occur mid-stream.
max_tokens is too small the reply stops mid-sentence; check the finish reason in the response.An LLM API call is a stateless request: model, messages, and settings in; text and usage metadata out. You manage conversation history yourself, keep keys on the server, retry transient failures with backoff, and use parameters like temperature and max tokens deliberately. The mock client in the challenge below mirrors the shape of a real SDK so you can practice the structure without a key.
Let's simulate a standard Gemini API model setup wrapper function in Python!
GoogleGenerativeAI.__init__(self, api_key) that saves the API key.generateContent(self, prompt) that returns a string containing the prompt and the model name "gemini-2.5-flash".Write this simulation in the code execution sandbox on the right!